1、主從複製概述mysql
主從複製是Mysql內建的複製功能,它是構建高性能應用程序的基礎,技術成熟,應用也很普遍。其原理就是經過將Mysql主庫的sql語句複製到從庫上,並從新執行一遍來實現的。複製過程當中主庫將更新寫入二進制日誌文件,並維護文件的一個索引以跟蹤日誌循環。這些日誌能夠記錄發送到從庫的更新。每次從庫鏈接主庫時,它會通知主庫最後一次成功更新的位置。從庫接收從那時起發生的任何更新。在進行主從複製時,全部命令都必須在主庫上進行,從庫不作操做。不然,會引發主從庫之間的數據不一樣步,複製會中斷。sql
下面列舉了主從模式時常見的一些問題,都是平時工做中實際遇到過的實戰案例。數據庫
2、錯誤及解決辦法服務器
問題: 從數據庫沒法同步app
Slave_SQL_Running 值爲 NO,或 Seconds_Bebind_Master 值爲 Nullsocket
緣由:post
1. 程序有可能在 slave 上進行了寫操做性能
2. 也有多是 slave 機器重啓後,事務回滾形成的ui
解決方法一:編碼
msyql> stop slave;
msyql> set GLOBAL SQL_SLAVE_SKIP_COUNTER=1;
msyql> start slave;
解決方法二:
msyql> stop slave;
#查看主服務器上當前的 bin-log 日誌名和偏移量
msyql> show master status;
#獲取到以下內容:
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000005 | 286 | | |
+------------------+----------+--------------+------------------+
#而後到從服務器上執行手動同步
msyql> change master to
->master_host="192.168.10.1",
->master_user="user",
->master_password="123456",
->master_post=3306,
->master_log_file="mysql-bin.000005",
->master_log_pos=286;
msyql> start slave;
場景1:主庫上用系統命令複製或刪除表數據文件
【模擬異常】:
主庫上直接copy表數據文件,或直接rm表數據文件主庫上拷貝test表數據文件後,執行insert into test values('111');或主庫上執行rm -f test05.*後,執行create table test05(a int(11));
【錯誤日誌】:
從庫日誌:SHOW SLAVE STATUS \G;
Last_Errno: 1146
Last_Error:Error 'Table'testdb.test 'doesn't exist'on query' insertinto test values('111') '.
Default database: 'testdb'. Query: 'insert into testvalues('111')'
或者以下:
Last_Error: Error 'Table’test05 'already exists' on query.
【錯誤緣由】:
表的建立或刪除不是經過執行sql,未寫入binlog,從庫上沒有相關表;
【解決方案】:
在從庫上手動建立此表(建表語句可參考主庫);
之後,主庫上對錶的操做請經過sql完成,避免使用系統命令拷貝或刪除
場景2:數據不一致:包括刪除失敗、主鍵重複、更新丟失
【問題1】:
主鍵重複:在slave已經有該記錄,又在master上插入了同一條記錄。
從庫日誌:SHOW SLAVE STATUS \G;
Last_Errno: 1062
Last_Error: Error 'Duplicate entry 'xxxn-66-77' for key1' on query. Default database: 'guild'. Query: 'insert into pynpcrecord setMapCode = 'xxxn', UpdateTime = '2015-08-07 00:00:32''
【解決方案】:
方案1:在從庫上將重複的主鍵記錄刪除,再次重啓主從;
deletefrom xxxx where 主鍵=yyyy;
stopslave;start slave;
方案2:停掉主從同步,忽略一次錯誤,再開啓同步:
stop slave;
set global sql_slave_skip_counter=1;startslave;
如果新配主從,忽略3次還報此錯,還能夠在my.cnf里加
一 行: slave-skip-errors=1062
而後重啓實例,再重啓主從同步;
stop slave; start slave;
【問題2】刪除失敗:在master上刪除一條記錄,而slave上找不到。
從庫日誌:SHOW SLAVE STATUS \G;
Last_Errno: 1032;
Last_Error: Could not execute Delete_rows event ontable hcy.t1;
Can't find record in 't1',
【解決方案】:
因爲master要刪除一條記錄,而slave上找不到而報錯,這種狀況主庫都將其刪除了,從庫能夠直接跳過。
可用命令:
stop slave;
set global sql_slave_skip_counter=1;startslave;
【問題3】:更新丟失:在master上更新一條記錄,而slave上找不到,丟 失了數據。
從庫日誌:SHOW SLAVE STATUS \G;
Last_Errno: 1032;
Last_Error: Could not execute Update_rows event ontable hcy.t1; Can't find record in 't1',
【解決方案】:
把丟失的數據在slave上填補,而後跳過報錯便可。
場景3:字段不一致:包括字段丟失、不夠長等
【問題1】
從庫日誌:SHOW SLAVE STATUS \G;
Slave_IO_Running: Yes
Slave_SQL_Running: No
Last_Errno: 1264
Last_Error: Error 'Out of range value for column 'JFNow' at row 1' onquery. Default database: 'guild'. Query: 'update pyPHBWS set JFNow =JFNow -1 wherePlayerName = '狂魔''
雖然從庫該字段和主庫的一致,但從庫仍是報錯:
guild> desc pyPHBWS;
+------------+---------------------+------+-----+---------+-------+
| Field |Type | Null | Key |Default | Extra |
+------------+---------------------+------+-----+---------+-------+
| PlayerName | varchar(30) | NO | PRI | NULL | |
| JFNow | int(10)unsigned | YES | |NULL | |
| JFAll |int(10) unsigned | NO | | NULL | |
【解決方案】
修改字段:
ALTER TABLE guild.pyPHBWS MODIFY JFNowbigint(20) unsigned;
重啓主從:stop slave;start slave;
【問題2】
從庫日誌:
SHOW SLAVE STATUS \G;
Slave_IO_Running: Yes
Slave_SQL_Running: No
Last_Errno: 1054
Last_Error:Error 'Unknown column 'qdir' in 'field list''on query. Default database: 'club'. Query: 'insert into club.question_del (id,pid, ques_name, title, intime, order_d, endtime,qdir) select id, pid,ques_name, title, intime, order_d, endtime ,qdir from club.question whereid=7330212'
【解決方案】
主庫:查詢 desc club.question_del,發現club.question_del表裏面沒有qdir這個字段;
從庫:執行 alter table question_del add qdirvarchar(30) not null;
場景4:超出MyISAM數據表大小限制
【錯誤日誌】
從庫日誌:
SHOW SLAVE STATUS \G;
Slave_IO_Running: No
Slave_SQL_Running: Yes
Last_Errno : 1114
Last_Error : Error 'The table 'tbleventlog' is full' onquery. Default database: 'dblog'. Query: 'insert into `tbleventlog`(`PlayerName`, `ACTION`, `VALUE`, `PARAM`, `TIME`) values ('ĺɫ', '־', '620',':2,:397842703', '2015-07-28 06:56:04')'
找到該實例所在的目錄,發現該表大小超過4GB;
【解決方案】
對於MyISAM數據表,單個.MYD和.MYI默認4GB。
利用AVG_ROW_LENGTH和MAX_ROWS建表選項能夠把這個最大值擴大到800萬TB.
max_rows主要對myisam生效.
從庫:調整max_rows並重啓slave.
use dblog; ALTER TABLE tbleventlog MAX_ROWS=1000000000;
stop slave; start slave;
場景5:slave的中繼日誌relay-bin損壞
【模擬異常】
SLAVE在宕機,或者非法關機,例如電源故障、主板燒了等,形成中繼日誌損壞,同步停掉。
【錯誤日誌】
從庫日誌:SHOW SLAVE STATUS \G;
Slave_IO_Running: Yes
Slave_SQL_Running: No
Last_Errno: 1593
Last_Error: Error initializing relay log position: I/Oerror reading event at position 4
【解決方案】
在主庫上找到同步的binlog和POS點,而後從新作同步,這樣就能夠有新的中繼日誌了。
mysql> CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000010',MASTER_LOG_POS=821;
場景6:binlog index記錄不一致
【模擬異常】
主數據庫忽然中止或問題終止,更改了mysql-bin.xxx日誌,slave服務器找不到這個文件。
【錯誤日誌】
從庫日誌:SHOW SLAVE STATUS \G;
Master_Log_File: mysql-bin.000029
Last_Errno: 1594
Last_Error: Relay log read failure: Could not parserelay log event entry.
【解決方案】
找到同步的點和日誌文件,而後chage master便可:
change master to master_log_file='mysql-bin.000025',master_log_pos=1010663436;
場景7:時區不一致致使主從數據不一致
【模擬異常】:主從服務器設置不一樣的時區
主庫:show variables like '%timezone%'; #.看到變量timezone值爲EDT
從庫:showvariables like '%timezone%'; #.看到變量timezone值爲 CST
【錯誤日誌】:主庫執行insert into tbname(dtime)values(now());
主庫: select* from tbname; #.看到字段dtime值爲 2013-05-08 18:40:18
從庫: select* from tbname; #.看到字段dtime值爲 2013-05-09 06:40:18
【解決方案】:
設置主從爲相同時區,並儘可能使用相同時間服務器
若是對時間字段用now()寫入,在刪除時候用delete * from tbname where dtime='xxx',因爲主從時間是不一致的,就會形成刪除的非同一條記錄,也會引發主鍵衝突問題。
場景8:字段集不一致
【模擬異常】
1.主庫:版本MySQL 4.0.18,字符集gb2312,主鍵字段PlayerName
show create table pybcsltscore;
PRIMARY KEY (`PlayerName`) ENGINE=MyISAM DEFAULT CHARSET=gb2312
2.從庫:版本MySQL 4.1.22,字符集latin1,主鍵字段PlayerName
show create table pybcsltscore;
PRIMARY KEY (`PlayerName`) ENGINE=MyISAM DEFAULT CHARSET=latin1
3.主庫:執行sql:
mysql> insert into pybcsltscore set PlayerName = '怒☆斬', PT = 'pchg.c8';
mysql> insert into pybcsltscore set PlayerName = '怒★斬', PT = 'pchg.c8';
4. 主庫:查詢正常
select * from pybcsltscore where playername='怒☆斬' or playername='怒★斬';
【錯誤日誌】
從庫:查詢異常,查詢實心星號,結果卻出現空心星號
select * from pybcsltscore_bak where playername='怒★斬';
從庫: 從庫狀態:
Last_Errno: 1062
Last_Error: Error 'Duplicate entry '怒★斬' for key 1' on query. Defaultdatabase: 'test0505'. Query: 'insert into pybcsltscore set PlayerName = '怒★斬', PT = 'pchg.cs68'‘
從庫:插入playname=’怒★斬’ 的記錄,會提示主鍵衝突
insert into score_bak set PlayerName = '怒★斬', PT = 'pchg.cs68';
ERROR 1062 (23000): Duplicate entry '怒★斬' for key 1
【解決方案】
方案1. 從庫:去掉主鍵
alter table pybcsltscore_test0513 drop primary key;
stop slave sql_thread; start slave sql_thread;
方案2. 從庫:修改默認編碼爲gb2312
mysql --default-character-set=gb2312 -S mysql3307.sock
從庫:再次查詢:
select * from pybcsltscore where playername='怒☆斬' or playername='怒★斬';
場景9:max_allowed_packet過小
【模擬異常】
1. 主庫:設置max_allowed_packet爲特小值,好比12K:
mysql> set global max_allowed_packet=12*1024;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like 'max_allowed_packet';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| max_allowed_packet | 12288 |
+--------------------+-------+
2. 重啓slave io thread
#說明:slave若是不重啓的話,我的以爲主從關係所使用的主庫的變
不會改變,重啓以便從新加載一些變量
3. 主庫:導入r2.txt(僅一行記錄
# du -sh r2.txt 80K r2.txt)
./bin/mysql test0505 -e "load data infile'/tmp/r2.txt' into table test2;"
4. 查看從庫狀態
Slave_IO_Running: No
Slave_SQL_Running: Yes
5. 從庫:
mysql> show variables like 'max_allowed_packet';
+--------------------+----------+
| Variable_name | Value |
+--------------------+----------+
| max_allowed_packet | 16776192|
+--------------------+----------+
【錯誤日誌】
查看從數據庫的錯誤日誌,找到以下信息:
[ERROR] Got fatal error 1236: 'log event entry exceededmax_allowed_packet;
Increase max_allowed_packet on master' from master whenreading data from binary log
或相似:
[ERROR] Error reading packet from server: Got packetbigger than 'max_allowed_packet' bytes (server_errno=2020)
應該是master上的dump線程在從binlog讀取數據時,讀取的結果集超出了max_allowed_packet限制,形成往slave發送失敗。
【解決方案】
修改max_allowed_packet的大小,而後重啓slave。建議主從一致
mysql> set global max_allowed_packet=16*1024*1024;
重啓slave, stop salve;start slave;
場景10:臨時表太大致使磁盤寫滿
【錯誤日誌】:從庫日誌
Last_Errno: 3
Last_Error: Error 'Error writing file '/tmp/FeqMc' (Errcode: 28)'on query.
Default database: 'evt'. Query: 'delete from goodslogwhere OpTime<'2015-07-01''
【錯誤緣由】
1. tmp目錄不可寫,或磁盤沒有空間;
2. tmp還有空間,可是原表太大,因此查詢時生成的臨時表過大,所以出 錯。
【解決方案】
1. 確認/tmp可寫入,同時磁盤未寫滿;
2. 修改socket目錄到空間較大的分區,再重啓實例;
把 socket = /tmp/mysql.sock 改成 socket = /app/mysql.sock