MySQL 常見錯誤

keme發表於2019-06-04

1. Too many connections

ERROR 1040 (HY000): Too many connections

導致結果:

連線數過多,導致連線不上資料庫,業務無法正常進行

該錯誤發生在有max_connections個客戶連線了mysqld伺服器, 應該重啟mysqld, 用更大的max_connections變數值

#預設連線數
mysql> show variables like '%max_connection%';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 151   |
+-----------------+-------+
1 row in set (0.00 sec)

  

解決問題思路:

1、首先先要考慮在我們 MySQL 資料庫引數檔案裡面,對應的 max_connections 這個引數值是不是設定的太小了,導致客戶端連線數超過了資料庫所承受的最大值。

  • 該值預設大小是 151,可以根據實際情況進行調整。

  • 對應解決辦法:set global max_connections=500

這樣調整會有隱患,因為我們無法確認資料庫是否可以承擔這麼大的連線壓力,就好比原來一個人只能吃一斤牛肉,但現在卻非要讓他吃 10斤牛肉,他肯定接受不了。反應到伺服器上面,就有可能會出現當機的可能。

所以這又反映出了,在新上線一個業務系統的時候,要做好壓力測試。保證後期對資料庫進行優化調整。

2. Packet too large

結果:

如果寫入大資料時,因為預設的配置太小,插入和更新操作會因為 max_allowed_packet 引數限制,而導致失敗。

mysql根據max_allowed_packet引數來限制server接受的資料包大小。

當一個MySQL客戶或mysqld伺服器得到一個max_allowed_packet個位元組長的包, 它發出一個Packet too large錯誤並終止連線。

mysql> show variables like 'max_allowed_packet';
+--------------------+---------+
| Variable_name      | Value   |
+--------------------+---------+
| max_allowed_packet | 4194304 |
+--------------------+---------+

預設是4M大小

可以使用mysqld的命令列選項設定max_allowed_packet為一個更大的尺寸。 例如, 如果將一個全長的BLOB存入一張表中, 需要用max_allowed_packet=24M選項來啟動mysql。

Max_allowed_packet的取值範圍是1024B~1GB

當然不要亂設定,根據具體環境要求,設定太大業務

# 具體設定max_allowed_packet大小
mysql> set @@global.max_allowed_packet=
#在my.cnf 加入這個 
max_allowed_packet= 10M

 

3. 線上要修改mysql引數,怎麼避免mysql 重啟

首先確定改引數是動態引數還是靜態引數

如果是靜態引數還是要重啟服務才會生效,動態引數則不用

這時候要修改全域性變數, 必須要顯示指定"GLOBAL"或者"@@global.", 同時必須要有SUPER許可權.

例如修改最大連線數

#預設連線數
mysql> show variables like '%max_connection%';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 151   |
+-----------------+-------+
1 row in set (0.00 sec)

#修改連線數為500
mysql> set @@global.max_connections=500;
Query OK, 0 rows affected (0.00 sec)

#檢視是否修改成功
mysql> show variables like '%max_connection%';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 500   |
+-----------------+-------+

# 在my.cnf 的[mysqld]下面加上 max_connections=500就可以了,也不用重啟服務

  

4. root密碼忘了怎麼辦

 

忘記了MySQL的root使用者的口令 在my.cnf中新增skip-grant-tables=1選項重啟mysqld

[root@mysql-150 ~]# mysql -u root -h 127.0.0.1
mysql> flush privileges;
mysql> grant all privileges on *.* to root@'localhost' identified by '456789';
mysql> exit
# 在my.cnf 將skip-grant-tables=1選項去掉
# 重啟mysqld之後就可以用最新的密碼登入
[root@mysql-150 ~]# vim /etc/my.cnf
[root@mysql-150 ~]# service mysql restart
Shutting down MySQL............ SUCCESS! 
Starting MySQL. SUCCESS! 
[root@mysql-150 ~]# mysql -u root -p456789 -h 127.0.0.1

  

5. 賬號被鎖定

# 建立一個使用者
mysql> create user keme@'localhost' identified by '123456';
# 給一個只讀許可權
mysql> grant select on *.* to keme@'localhost';

# 可以從本地登入
[root@mysql-150 ~]# mysql -u keme -p123456

# 把keme@'localhost' 給lock住,不讓其使用
mysql> alter user keme@'localhost' account lock;

# 在看看能不能從本地登入
[root@mysql-150 ~]# mysql -u keme -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 3118 (HY000): Access denied for user 'keme'@'localhost'. Account is locked.

# 檢視該使用者是否鎖定
mysql> select host,user,account_locked from mysql.user where user='keme';
+-----------+------+----------------+
| host      | user | account_locked |
+-----------+------+----------------+
| localhost | keme | Y              |
+-----------+------+----------------+
Y已鎖定
# 然後解鎖該keme使用者
mysql> alter user keme@'localhost' account unlock;

# 再去登入keme使用者
[root@mysql-150 ~]# mysql -u keme -p123456

  

6. 環境變數未設定

例如執行: mysqldump提示: -bash: command not found 是 環境變數設定的問題

 

臨時新增:

# 首先要確定mysql 的安裝位置
shell> export PATH=$PATH:/usr/local/mysql/bin

  

永久設定:

# 在/etc/profile 中末尾新增
PATH=$PATH:$HOME/bin:/usr/local/mysql/bin
export PATH
儲存退出後執行: source /etc/bash_profile即可。

  

7. SQL MODE

MySQL伺服器可以以不同的SQL模式來操作, 並且可以為不同客戶端應用不同模式。 這樣每個應用程式可以根據自己的需求來定製伺服器的操作模式

模式定義MySQL應支援哪些SQL語法, 以及應執行哪種資料驗證檢查。 這樣可以更容易地在不同的環境中使用MySQL, 並結合其它資料庫伺服器使用MySQL。

檢視當前的sql_mode

mysql> show variables like 'sql_mode';
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| Variable_name | Value                                                                                                                                     |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| sql_mode      | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select @@sql_mode;
+-------------------------------------------------------------------------------------------------------------------------------------------+
| @@sql_mode                                                                                                                                |
+-------------------------------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------------------------------------------------------------------------------------------------------+

  

7.1 主要的SQL_Mode值包括:

  • ANSI

更改語法和行為, 使其更符合標準SQL。

  • STRICT_TRANS_TABLES
  • TRADITIONAL

使MySQL的行為象“傳統”SQL資料庫系統。 該模式的簡單描述是當在列中插入不正確的值時“給 出 錯誤 而不是警告” 等同STRICT_TRANS_TABLES、 STRICT_ALL_TABLES、NO_ZERO_IN_DATE、 NO_ZERO_DATE、 ERROR_FOR_DIVISION_BY_ZERO、 NO_AUTO_CREATE_USER。

sql mode常用值

  •  ONLY_FULL_GROUP_BY

對於GROUP BY聚合操作,如果在SELECT中的列,沒有在GROUP BY中出現,那麼這個SQL是不合法的,因為列不在GROUP BY從句中

但這有個條件:如果查詢是主鍵列或是唯一索引且非空列,分組列根據主鍵列或者唯一索引且空(null)則sql 分組查詢有效

  • NO_AUTO_VALUE_ON_ZERO

 該值影響自增長列的插入。預設設定下,插入0或NULL代表生成下一個自增長值。如果使用者希望插入的值為0,而該列又是自增長的,那麼這個選項就有用了。

  • STRICT_TRANS_TABLES

在該模式下,如果一個值不能插入到一個事務表中,則中斷當前的操作,對非事務表不做限制

為事務儲存引擎啟用嚴格模式, 也可能為非事務儲存引擎啟用嚴格模式。

嚴格模式控制MySQL如何處理非法或丟失的輸入值。 有幾種原因可以使一個值為非法。 例如, 資料型別錯 誤, 不適合列, 或超出範圍。 當新插入的行不包含某列的沒有顯示定義DEFAULT子句的值,則該值被丟失。 對於事務表, 當啟用STRICT_ALL_TABLES或STRICT_TRANS_TABLES模式時, 如果語句中有非法或丟失值, 則會出現錯誤。 語句被放棄並回滾。

  •  NO_ZERO_IN_DATE

在嚴格模式下,不允許日期和月份為零

  • NO_ZERO_DATE

設定該值,mysql資料庫不允許插入零日期,插入零日期會丟擲錯誤而不是警告。

  • ERROR_FOR_DIVISION_BY_ZERO

在INSERT或UPDATE過程中,如果資料被零除,則產生錯誤而非警告。如果未給出該模式,那麼資料被零除時MySQL返回NULL

  • NO_AUTO_CREATE_USER

禁止GRANT建立密碼為空的使用者

  • NO_ENGINE_SUBSTITUTION

如果需要的儲存引擎被禁用或未編譯,那麼丟擲錯誤。不設定此值時,用預設的儲存引擎替代,並丟擲一個異常

  • PIPES_AS_CONCAT

將”||”視為字串的連線操作符而非或運算子,這和Oracle資料庫是一樣的,也和字串的拼接函式Concat相類似

舉例:

# 建立一個測試表
CREATE TABLE `employee` (
`eid` int(11) NOT NULL,
`ename` varchar(64) DEFAULT NULL,
`sex` int(11) DEFAULT NULL,
PRIMARY KEY (`eid`)
) ENGINE=InnoDB;
# 插入幾條資料
insert into employee (eid,ename,sex) values (1,'keme',18),(2,'xixi',22),(3,'yj',18),(4,'kk',18),(5,'yy',18),(6,'xx',35);

# 設定當前會話的sql_mode為如下
mysql> set @@sql_mode='STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';

mysql> select eid,ename,count(*) from employee group by ename;
+-----+-------+----------+
| eid | ename | count(*) |
+-----+-------+----------+
|   1 | keme  |        1 |
|   4 | kk    |        1 |
|   2 | xixi  |        1 |
|   6 | xx    |        1 |
|   3 | yj    |        1 |
|   5 | yy    |        1 |
+-----+-------+----------+

# 重新設定當前的sql_mode 為如下
mysql> set @@sql_mode='ONLY_FULL_GROUP_BY';
mysql> select eid,ename,count(*) from employee group by ename;
ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'beta.employee.eid' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
mysql> select eid,ename ,count(*) from employee group by eid;
+-----+-------+----------+
| eid | ename | count(*) |
+-----+-------+----------+
|   1 | keme  |        1 |
|   2 | xixi  |        1 |
|   3 | yj    |        1 |
|   4 | kk    |        1 |
|   5 | yy    |        1 |
|   6 | xx    |        1 |
+-----+-------+----------+
6 rows in set (0.00 sec)



mysql> set @@sql_mode='STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';
mysql> insert into employee values(7,'ke','male');
ERROR 1366 (HY000): Incorrect integer value: 'male' for column 'sex' at row 1

mysql> set @@sql_mode='ANSI';
Query OK, 0 rows affected (0.00 sec)

mysql> select @@sql_mode;
+--------------------------------------------------------------------------------+
| @@sql_mode                                                                     |
+--------------------------------------------------------------------------------+
| REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ONLY_FULL_GROUP_BY,ANSI |
+--------------------------------------------------------------------------------+

#改成ANSI模式就可以插入成功了,只不過識別成了0
mysql> insert into employee values(7,'ke','male');
Query OK, 1 row affected, 1 warning (0.01 sec)

mysql> select * from employee where eid=7;
+-----+-------+------+
| eid | ename | sex  |
+-----+-------+------+
|   7 | ke    |    0 |
+-----+-------+------+
1 row in set (0.00 sec)


mysql> set @@sql_mode='STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> select @@sql_mode;
+--------------------------------------------+
| @@sql_mode                                 |
+--------------------------------------------+
| STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION |
+--------------------------------------------+
1 row in set (0.00 sec)

mysql> insert into employee values (8,'ww',17/0);
Query OK, 1 row affected (0.00 sec)

mysql> select * from employee  where eid=8;
+-----+-------+------+
| eid | ename | sex  |
+-----+-------+------+
|   8 | ww    | NULL |
+-----+-------+------+


mysql> set @@sql_mode='STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,ERROR_FOR_DIVISION_BY_ZERO';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> select @@sql_mode;
+-----------------------------------------------------------------------+
| @@sql_mode                                                            |
+-----------------------------------------------------------------------+
| STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION |
+-----------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> insert into employee values (9,'ee',18/0);
ERROR 1365 (22012): Division by 0

mysql> alter table employee modify ename varchar(5);
mysql> set @@sql_mode='STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';
mysql> insert into employee values (9,'qweradsf',11);
ERROR 1406 (22001): Data too long for column 'ename' at row 1

mysql> set @@sql_mode='ANSI';
mysql> insert into employee values (9,'qweradsf',11);
mysql> select * from employee where eid=9;
+-----+-------+------+
| eid | ename | sex  |
+-----+-------+------+
|   9 | qwera |   11 |
+-----+-------+------+


mysql> set @@sql_mode='TRADITIONAL';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> select @@sql_mode;
TRADITIONAL模式有如下值:
|STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |

  

8. 使用者的資源限制

MySQL提供了對每個使用者的資源限制管理

MAX_QUERIES_PER_HOUR : 一個使用者在一個小時內可以執行查詢的次數(基本包含 所 有 語 句 )

MAX_UPDATES_PER_HOUR:一個使用者在一個小時內可以執行修改的次數(僅包含修 改資料庫或表的語句)

MAX_CONNECTIONS_PER_HOUR:允許使用者每小時連線的次數

MAX_USER_CONNECTIONS:一個使用者可以在同一時間連MySQL例項的數量

通過執行create user/alter user設定/修改使用者的資源限制

# 建立一個使用者並設定其資源限制
CREATE USER 'keme1'@'localhost' IDENTIFIED BY
'123456' WITH MAX_QUERIES_PER_HOUR 20
MAX_UPDATES_PER_HOUR 10
MAX_CONNECTIONS_PER_HOUR 5
MAX_USER_CONNECTIONS 2;
#keme1 這個使用者 一個小時可以查詢20次, 修改10次,一個小時可以連線5次,同一時刻只允許兩個使用者

#取消某項資源限制既是把原先的值修改成0
mysql> alter user 'keme1'@'localhost' WITH MAX_CONNECTIONS_PER_HOUR 0;

# 當針對某個使用者的max_user_connections非0時, 則忽略全域性系統引數max_user_connections, 反之則全域性系統引數生效

  

9. 主從同步錯誤

一般主從同步錯誤首先要考慮是不是在從庫中誤操作導致的。結果發現,有人在從庫中進行了一條針對有主鍵表的 sql 語句的插入,導致主庫再插入相同 sql 的時候,主從狀態出現異常。發生主鍵衝突的報錯。

解決方法:

在確保主從資料一致性的前提下,可以在從庫進行錯誤跳過。

像從庫如果不提供什麼服務的話可以在從庫中開啟 read_only 引數,禁止在從庫進行寫入操作,還有使用者必須沒有super 許可權,設定read_only才會生效。

9.1 一般主從複製錯誤的解決辦法

這是正常的狀態

10.0.0.150 是主

10.0.0.151 是從

先模擬故障

# 這是本次的表結構
mysql> show create table students;
| students | CREATE TABLE `students` (
  `sid` int(11) NOT NULL,
  `sname` varchar(20) DEFAULT NULL,
  `sex` int(11) DEFAULT NULL,
  PRIMARY KEY (`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 
#主庫執行,是個空表
mysql> select * from students;
Empty set (0.00 sec)

#在從庫 ,給students 加1條資料: 
mysql> insert into students  values (1,'keme',0);
mysql> show slave status\G;
...
 Slave_IO_Running: Yes
 Slave_SQL_Running: Yes
...
看主從狀態是正常的
# 從庫檢視students 資料
mysql> select * from students;
+-----+-------+------+
| sid | sname | sex  |
+-----+-------+------+
|   1 | keme  |    0 |
+-----+-------+------+


# 在主庫檢視students 表
mysql> select * from students;
Empty set (0.00 sec)

# 插入相同主鍵的值
mysql> insert into students values (1,'keme',1);

# 檢視students表
mysql> select * from students;
+-----+-------+------+
| sid | sname | sex  |
+-----+-------+------+
|   1 | keme  |    1 |
+-----+-------+------+


# 檢視從庫狀態
mysql> show slave status\G;
...
Slave_IO_Running: Yes
Slave_SQL_Running: No
Last_SQL_Error: Could not execute Write_rows event on table beta.students; Duplicate entry '1' for key 'PRIMARY', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mysql-bin.000023, end_log_pos 11789
...
# 主從狀態不一致了,造成的原因是主鍵衝突

  

解決辦法

# 停止從庫
mysql> stop slave;

# 在從庫刪除主鍵衝突的那條語句, 把主庫執行的那條語句在從庫執行
mysql> delete from students where sid=1;
mysql> insert into students values (1,'keme',1);

# 同步跳過臨時錯誤
mysql> set global sql_slave_skip_counter = 1;
mysql> start slave;
mysql> show slave status\G;
...
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
...

# 主庫再次插入資料,看看從庫是不是能夠同步
mysql> insert into students  values (2,'keme',1);

# 從庫檢視
mysql> select * from students;
+-----+-------+------+
| sid | sname | sex  |
+-----+-------+------+
|   1 | keme  |    1 |
|   2 | keme  |    1 |
+-----+-------+------+
2 rows in set (0.00 sec)

# OK,同步成功了, 一般主從錯誤也就解決了

  

那這時候有問題,主從問題不一致了, 主上面插入了很多資料, 這時候該怎麼解決了。

首先主從問題不一致了,你的監控預警機制了,給你發簡訊或者釘釘,這時候你應該儘快去修復從庫,比如就像上面跳過臨時同步錯誤,暫時讓其恢復正常同步。

其次 後期就是用pt工具:比如用pt-table-checksum 找出主從表資料不一致的, pt-table-sync進行修復從庫

 

9.2 GTID 主從複製錯誤解決辦法

現在修改我的主從模式為GTID,這是我的測試環境隨便改,

生產環境不能這樣瞎改

搭建GTID主從時,需要注意的 mysql 引數:

server_id:設定 mysql 例項的 server_id,每個例項的server_id必須不一樣

gtid_mode=on:MYSQL 例項開啟GTID 模式。

enforce_gtid_consitency=on :使用GTID模式複製時,需要開啟此引數,用來保證GTID的一致性。

log-bin=on :Msql 做主從必須開啟binlog

log-slave-updates=1 :覺得slave 從master 接收到的更新且執行完之後,執行的binlog是否記錄到slave的binlog中,建議開啟

binlog_format=row :強烈建議binlog_format使用row格式 在mysql 5.7.6 版本以後預設就是row

skip-slave-start=1 :當slave 資料庫啟動的時候,slave 不會自動開啟複製

 

主庫操作,在[mysqld] 加一下引數,我這個做過主從, 只加一部分引數

# my.cnf 中內容 
[mysqld]
gtid-mode=on
enforce-gtid-consistency=on
log-slave-updates=1

  

從庫操作

# my.cnf 中內容
[mysqld]
gtid-mode=on
enforce-gtid-consistency=on
log-slave-updates=1
skip-slave-start=1

重啟主從資料庫

 

 

在從庫 操作重新設定主從庫的複製關係

mysql> CHANGE MASTER TO
MASTER_HOST = '10.0.0.150',
MASTER_PORT = 3306,
MASTER_USER = 'repl',
MASTER_PASSWORD = '123456',
MASTER_AUTO_POSITION = 1;
mysql> start slave;

#檢視主從狀態
mysql> show slave status\G;
...
 Slave_IO_Running: Yes
 Slave_SQL_Running: Yes
...

  

 

如果是在GTID模式下出現複製報錯, 則使用SQL_SLAVE_SKIP_COUNTER語句會報錯

在GTID 模式的複製情況下,如果slave 發生錯誤,則可以通過跳過該事務的方式恢復主從複製。

 

 現在人為製造slave錯誤

# 在從庫的sutdents 表插入一條資料
mysql> select * from students;
+-----+-------+------+
| sid | sname | sex  |
+-----+-------+------+
|   1 | keme  |    1 |
|   2 | keme  |    1 |
+-----+-------+------+
mysql> insert into students values (3,'keme',0);

# 在檢視從庫的資料
mysql> select * from students;
+-----+-------+------+
| sid | sname | sex  |
+-----+-------+------+
|   1 | keme  |    1 |
|   2 | keme  |    1 |
|   3 | keme  |    0 |
+-----+-------+------+

# 主庫也插入主鍵為3這條資料,引發主從同步錯誤
mysql> insert into students values (3,'keme',1);

  

 

 主從報錯了:

 

 從圖中可以看出,出錯事務的binlog檔案為mysql-bin.000026

開始位置(Exec_Master_Log_Pos)是154 ,結束位置是(end_log_pos ) 395,可以去主庫分析下binlog ,看一下發生衝突的事務是哪個。

可以看到接收並且執行了GTID事件 是

從庫執行了這些
5a13910d-1496-11e9-8375-000c29f859ce:1-3,
f6c31435-38dd-11e9-ac93-000c299bcbee:1-53096

收到卻沒執行的事務號:
Retrieved_Gtid_Set: f6c31435-38dd-11e9-ac93-000c299bcbee:53097

  

可以看出發現衝突的事務號是:f6c31435-38dd-11e9-ac93-000c299bcbee:53097,這時候就要確定哪一個事務發生了衝突,還可以直接從show slave status\G;結果中通過比對的方式找到衝突位置。

嚴謹起見,通過對binlog 內容分析得知衝突事務是插入了一條資料,主鍵為3。在從庫中檢視這條記錄是否真的存在

mysql> select * from students where sid=3;
+-----+-------+------+
| sid | sname | sex  |
+-----+-------+------+
|   3 | keme  |    0 |
+-----+-------+------+

  

發現slave 中存在這條記錄了,這時,可以通過跳過該事務的方式來放棄該事務在slave上的執行,使slave 能夠正常執行。

基於GTID模式的複製,跳過一個事務,需要利用一個空事務來完成。

mysql> stop slave;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> set GTID_NEXT='f6c31435-38dd-11e9-ac93-000c299bcbee:53097';
Query OK, 0 rows affected (0.00 sec)

mysql> begin;commit;
Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

mysql> set GTID_NEXT='AUTOMATIC';
Query OK, 0 rows affected (0.00 sec)

mysql> start slave;
Query OK, 0 rows affected (0.00 sec)

  

檢視slave 狀態

 

 哪主從庫資料是否一致,就看3那條

# 主庫資料
mysql> select * from students where sid=3;
+-----+-------+------+
| sid | sname | sex  |
+-----+-------+------+
|   3 | keme  |    1 |
+-----+-------+------+

#從庫資料
mysql> select * from students where sid=3;
+-----+-------+------+
| sid | sname | sex  |
+-----+-------+------+
|   3 | keme  |    0 |
+-----+-------+------+

  

解決不一致資料:

1 手動修改或者插入

2 用pt用具 來修復或者檢查不一致資料

 

由於我這是我的本地環境,我只手動修改資料,再看主從狀態

mysql> update students set  sex=1 where sid=3;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1  Changed: 1  Warnings: 0

 

注:不止是要主從解決錯誤,還要主從資料的一致性  

10. 資料庫總會出現中文亂碼的情況

為什麼我的資料庫總會出現中文亂碼的情況。一堆中文亂碼不知道怎麼回事?當向資料庫中寫入建立表,並插入中文時,會出現這種問題。此報錯會涉及資料庫字符集的問題。

10.1 解決亂碼的幾個方面

對於中文亂碼的情況,從三個方面

  • 資料終端: 就是我們連線資料庫的工具設定為utf8
  • 作業系統層面:linux 系統通過 在命令爭端檢視當前編碼echo $LANG或者locale

如何修改了系統編碼了:

# centos 6.x 版本是/etc/sysconfig/i18n
修改這個檔案
shell> vim /etc/sysconfig/i18n
# 這一行改為utf8
LANG=en_US.UTF-8
# 修改完,不要重啟,立即生效如下
shell> source /etc/sysconfig/i18n


# centos 7.x 版本是/etc/locale.conf 這個檔案
[root@mysql-150 ~]# vim /etc/locale.conf
LANG="en_US.UTF-8"
#立即生效
[root@mysql-150 ~]# source /etc/locale.conf

  

  • 資料庫層面:

在引數檔案中的[mysqld] 下,加入相應utf8字符集

# 注意資料庫的系統版本
5.6.x 和 5.7.x設定字符集引數不一樣,8.x和5.7.x設定是一樣的
#檢視當前資料庫的字符集引數,檢視當前字符集引數
mysql> show variables like '%character%';

# 檢視資料庫支援的字元編碼,和編碼的排序規則
mysql> show character set;

# 修改sutdents表中sname 欄位的字元編碼
mysql> alter table students modify sname varchar(66) character set gbk;
Query OK, 3 rows affected (0.06 sec)
Records: 3  Duplicates: 0  Warnings: 0

#看看錶結構
mysql> show create table students;
...
| students | CREATE TABLE `students` (
  `sid` int(11) NOT NULL,
  `sname` varchar(66) CHARACTER SET gbk DEFAULT NULL,
  `sex` int(11) DEFAULT NULL,
  PRIMARY KEY (`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
...

# 檢視連線級字符集和排序規則
mysql> show variables like '%collation%';
+----------------------+-----------------+
| Variable_name        | Value           |
+----------------------+-----------------+
| collation_connection | utf8_general_ci |
| collation_database   | utf8_general_ci |
| collation_server     | utf8_general_ci |
+----------------------+-----------------+

  

從上面示例可以得出:

如果修改資料庫字符集,需要從以下考

  • 列級別字符集

  • 表級別字符集

  • 庫級別字符集

  • mysql 例項字符集

10.2 怎麼合理修改mysql字符集了

在/etc/my.cnf 加一下引數

vim /etc/my.cnf 
[mysqld] 
init-connect='SET NAMES utf8' 
character-set-server=utf8 
然後去資料庫操作:
mysql> set @@global.character_set_server=utf8;
Query OK, 0 rows affected (0.00 sec)

mysql> set @@global.init_connect='SET NAMES utf8';
Query OK, 0 rows affected (0.00 sec)
# 注 使用者操作的時候看看有沒有super許可權,對super使用者許可權 set names 不生效

  

有人說,修改完還是亂碼, 這時候就亂碼是哪個庫的字符集,哪個表的字符集,哪個欄位的字符集,還有作業系統字符集,程式連線的字符集,這些都的檢視。

在/etc/my.cnf中init-connect='SET NAMES utf8'是什麼意思:

讓每個客戶端連線都自動設定字符集,但缺點是對擁有super許可權的使用者不生效

init_connect表示伺服器為每個連線的客戶端執行的字串。字串由一個或多個SQL語句組成。要想指定多個語句,用分號間隔開 。

 比如:

# 舉例init_connect
mysql> SET @@GLOBAL.init_connect='SET AUTOCOMMIT=0;set names
utf8';
shell> vim my.cnf
[mysqld]
init_connect='SET AUTOCOMMIT=0;set names utf8'

10.3 連線級字符集和排序規則

  • 每個資料庫客戶端連線都有自己的字符集和排序規則屬性,

    客戶端傳送的語句的字符集是由character_set_client決定,

    而與服務端互動時會根據character_set_connection和collation_connection兩個引數將接收到的語句轉化。當涉及到顯示字串的比較時,由collation_connection引數決定,

    而當比較的是欄位裡的字串時則根據欄位本身的排序規則決定

  • character_set_result引數決定了語句的執行結果以什麼字符集返回給客戶端

  • 客戶端可以很方便的調整字符集和排序規則,比如使用SET NAMES 'charset_name' [COLLATE 'collation_name']表明後續的語句都以該字符集格式傳送給服務端,而執行結果也以此字符集格式返回。

  

set names 字符集

set names charset_name 語句相當於執行了以下三行語句:
SET character_set_client = charset_name;
SET character_set_results = charset_name;
SET character_set_connection = charset_name;

  

或者執行SET CHARACTER SET 'charset_name'命令 :此命令和set names非常類似,唯一不同是將connection的字符集設定為當前資料庫的字符集,所以相當於執行以下三行語句:

SET character_set_client = charset_name;
SET character_set_results = charset_name;
SET character_set_connection = @@character_set_database;

小結: 中文亂碼從:資料終端,作業系統,資料庫

資料庫從: 全域性資料庫server字符集——>資料庫字符集——> 表字符集——> 列字符集

10.4 表情亂碼不能識別

在my.cnf 中修改

shell> vim my.cnf
[mysqld] 
init-connect='SET NAMES utf8mb4' 
character-set-server=utf8mb4 
#登入資料庫執行以下語句
mysql> set @@global.character_set_server=utf8mb4;
mysql> set @@global.init_connect='SET NAMES utf8mb4';

  

11 can't opet file(errno:24)

有的時候,資料庫跑得好好的,突然報不能開啟資料庫檔案的錯誤了。

解決思路:

首先我們要先檢視資料庫的 error log。然後判斷是表損壞,還是許可權問題。還有可能磁碟空間不足導致的不能正常訪問表,作業系統的限制也要關注下,相關應用限制也要關注下;

#ulimit -n 檢視系統的最大開啟檔案數
[root@mysql-150 ~]# ulimit -n
65535

檢視資料庫的開啟檔案數

mysql> show variables like 'open_files_limit';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| open_files_limit | 5000  |
+------------------+-------+
# 根據業務實際情況修改開啟檔案數一般足夠用了,低版本的開啟檔案數,可能有點小, 注意一下

哪就是其他的問題了,可能是表的許可權,也可能是表出問題,根據錯誤日誌,具體分析  

處理方法

  • repair table tablename

  • chown mysql.mysql 許可權 目錄

  • 清理磁碟中的垃圾資料

12. sleep 執行緒過多怎麼解決

結果:嚴重消耗mysql伺服器資源(主要是cpu, 記憶體),並可能導致mysql崩潰。

12.1 知道 sleep 執行緒過多原因

首先要知道到底是什麼原因導致的 sleep 執行緒過多的:

  1. 程式邏輯問題,導致連線一直不釋放;

  2. mysql 引數的問題,是不是引數配置的不合理,一直不釋放連線;

  3. mysql 語句的問題,資料庫查詢不夠優化,過度耗時。

  4. 大併發情況問題,導致 sleep 情況過多;

12.2 臨時解決 sleep 執行緒

很多人都是重啟大法,重啟大法確實好, 能夠釋放,生產重啟對業務有影響的,不能隨便重啟的

shell指令碼+cron計劃任務,來kill sleep 執行緒,這個不靠譜啊, 你不知道 sleep 執行緒,裡面是不是還有事務還在執行沒有提交,也是sleep 狀態,這個kill 操作有點莽夫,對生產資料資料庫還是要理智啊。

我臨時解決的辦法:

  1. 對使用者資源做限制,看看那個使用者連線的sleep執行緒比較多,對這個使用者連線多的做一些限制,比如一個小時可以連線多少次啊等等

  2. 修改 mysql 引數問題 ,修改wait_timeoutinteractive_timeout預設都是28800秒有,也就是8個小時以後才釋放空連結。

例子:

 

 

不同使用者登入到資料庫,wait_timeout和interactive_timeout都是28800秒

修改引數,我生產環境設定的是半個小時,也就是1800秒

mysql> set global wait_timeout=1800;
mysql> set global interactive_timeout=1800;

shell> vim my.cnf
[mysqld]
wait_timeout=1800
interactive_timeout=1800

  

這樣修改完,由於已近保持的會話連線需要等到8個小時才會釋放, 所以修改了wait_timeout和interactive_timeout不會立即生效的原因,這時候就要修改連線過多的使用者資源了來釋放sleep執行緒了

如下:

#開啟兩個會話視窗
mysql> # 重新登入了會話,wait_timeout和interactive會生效
mysql> show variables like 'wait_timeout';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout  | 1800  |
+---------------+-------+
1 row in set (0.00 sec)

mysql> show variables like 'interactive_timeout';
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 1800  |
+---------------------+-------+
1 row in set (0.00 sec)

#session2
mysql> #這個是其他使用者連線的mysql,這個會話一直沒有斷開,引數還是28800
mysql> show variables like 'wait_timeout';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout  | 28800 |
+---------------+-------+
1 row in set (0.01 sec)

mysql> show variables like 'interactive_timeout';
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 28800 |
+---------------------+-------+
1 row in set (0.00 sec)

 

 

13.3 怎麼根本解決了

這時候就要知道 造成 sleep 執行緒過多的原因來解決:

  1. 程式執行完畢,應該顯式呼叫mysql_close

  2. 程式中根據業務訪問情況,選擇長連線還是短連線

  3. 能逐步分析系統的SQL查詢,找到查詢過慢的SQL優化

  4. 合理設定mysql引數值

 

 

 

 

 

 

  

 

相關文章