目錄
1、爲什麼需要主從複製?
1、在業務複雜的系統中,有這麼一個情景,有一句sql語句需要鎖表,導致暫時不能使用讀的服務,那麼就很影響運行中的業務,使用主從複製,讓主庫負責寫,從庫負責讀,這樣,即使主庫出現了鎖表的情景,通過讀從庫也可以保證業務的正常運作。
2、做數據的熱備
3、架構的擴展。業務量越來越大,I/O訪問頻率過高,單機無法滿足,此時做多庫的存儲,降低磁盤I/O訪問的頻率,提高單個機器的I/O性能。
2、什麼是mysql主從複製
Mysql主從複製是指數據可以從一個mysql數據庫服務器節點複製到另一個或者多個節點。
Mysql默認採用異步複製方式,這樣從節點不用一直訪問主服務器來跟新自己的數據,數據的更新可以在遠程連接上進行,從節點可以複製主數據庫中的所有數據庫或者特定的數據庫,或者特定的表。
3、Mysql複製原理
原理:
(1)master服務器將數據的改變記錄二進制binlog日誌,當master的數據發生改變是,這將改變寫入二進制日誌中;
(2)slave服務器會在一定時間間隔內對master二進制日誌進行探測是否發生改變,如果發生該百年,則開始一個IO線程請求master二進制時間
(3)同時主節點爲每個IO線程啓動一個dump線程,用於向其發送二進制事件,並保存至從節點本地的中繼日誌中,從節點將啓動sql線程從中繼日誌讀取二進制日誌,在本地重放,使得其數據和主節點的保持一致,最後IO線程和sql線程進入睡眠狀態,等待下一次喚醒
- 從庫會生成兩個線程,一個I/O線程,一個SQL線程;
- I/O線程會去請求主庫的binlog,並將得到的binlog寫到本地的relay-log(中繼日誌)文件中;
- 主庫會生成一個log dump線程,用來給從庫I/O線程傳binlog;
- SQL線程,會讀取relay log文件中的日誌,並解析成sql語句逐一執行;
注意:
1、master將操作語句記錄到binlog日誌中,然後授予slave遠程連接的權限(master一定要開啓binlog二進制日誌功能;通常爲了數據安全考慮,slave也開啓binlog功能)。
2、slave開啓兩個線程:IO線程和SQL線程。其中:IO線程負責讀取master的binlog內容到中繼日誌relay log裏;SQL線程負責從relay log日誌裏讀出binlog內容,並更新到slave的數據庫裏,這樣就能保證slave數據和master數據保持一致了。
3、Mysql複製至少需要兩個Mysql的服務,當然Mysql服務可以分佈在不同的服務器上,也可以在一臺服務器上啓動多個服務。
4、Mysql複製最好確保master和slave服務器上的Mysql版本相同(如果不能滿足版本一致,那麼要保證master主節點的版本低於slave從節點的版本)
5、master和slave兩節點間時間需同步
4、主從複製簡介
1.1. 基於二進制日誌複製的
1.2. 主庫的修改操作會記錄二進制日誌
1.3. 從庫會請求新的二進制日誌並回放,最終達到主從數據同步
1.4. 主從複製核心功能:
輔助備份,處理物理損壞
擴展新型的架構:高可用,高性能,分佈式架構等
前提
- 2.1 兩臺以上mysql實例 ,server_id,server_uuid不同
- 2.2 主庫開啓二進制日誌
- 2.3 專用的複製用戶
- 2.4 保證主從開啓之前的某個時間點,從庫數據是和主庫一致(補課)
- 2.5 告知從庫,複製user,passwd,IP port,以及複製起點(change master to)
- 2.6 線程(三個):Dump thread IO thread SQL thread 開啓(start slave)
主從複製搭建過程
清理主庫數據
rm -rf /data/3307/data/*
重新初始化3307
mysqld --initialize-insecure --user=mysql --basedir=/app/mysql --datadir=/data/3307/data
修改my.cnf ,開啓二進制日誌功能
[root@db01 3307]# vim /data/3307/my.cnf
log_bin=/data/3307/data/mysql-bin
啓動所有節點
[root@db01 3307]# systemctl start mysqld3307
[root@db01 3307]# systemctl start mysqld3308
[root@db01 3307]# systemctl start mysqld3309
[root@db01 3307]# ps -ef |grep mysqld
mysql 3684 1 4 09:59 ? 00:00:00 /app/mysql/bin/mysqld --defaults-file=/data/3307/my.cnf
mysql 3719 1 7 09:59 ? 00:00:00 /app/mysql/bin/mysqld --defaults-file=/data/3308/my.cnf
mysql 3754 1 8 09:59 ? 00:00:00 /app/mysql/bin/mysqld --defaults-file=/data/3309/my.cnf
[root@db01 3307]# mysql -S /data/3307/mysql.sock -e "select @@server_id"
[root@db01 3307]# mysql -S /data/3308/mysql.sock -e "select @@server_id"
[root@db01 3307]# mysql -S /data/3309/mysql.sock -e "select @@server_id"
主庫中創建複製用戶
[root@db01 3307]# mysql -S /data/3307/mysql.sock
db01 [(none)]>grant replication slave on *.* to repl@'10.0.0.%' identified by '123';
db01 [(none)]>select user,host from mysql.user;
備份主庫並恢復到從庫
[root@db01 3307]# mysqldump -S /data/3307/mysql.sock -A --master-data=2 --single-transaction -R --triggers >/backup/full.sql
-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=653;
[root@db01 3307]# mysql -S /data/3308/mysql.sock
db01 [(none)]>source /backup/full.sql
告知從庫關鍵複製信息
ip port user password binlog position
[root@db01 3307]# mysql -S /data/3308/mysql.sock
db01 [mysql]>help change master to
CHANGE MASTER TO
MASTER_HOST='10.0.0.51', #主庫ip地址
MASTER_USER='repl', #複製用戶
MASTER_PASSWORD='123', #密碼
MASTER_PORT=3307, #端口
MASTER_LOG_FILE='mysql-bin.000001', #複製binlog的起始位置
MASTER_LOG_POS=653, #起始位置的pos好
MASTER_CONNECT_RETRY=10; #嘗試連接次數
開啓主從專用線程
start slave ;
檢查複製狀態
db01 [mysql]>show slave status \G
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
主從複製原理描述:
- 1.change master to 時,ip pot user password binlog position寫入到master.info進行記錄
- 2. start slave 時,從庫會啓動IO線程和SQL線程
- 3.IO_T,讀取master.info信息,獲取主庫信息連接主庫
- 4. 主庫會生成一個準備binlog DUMP線程,來響應從庫
- 5. IO_T根據master.info記錄的binlog文件名和position號,請求主庫DUMP最新日誌
- 6. DUMP線程檢查主庫的binlog日誌,如果有新的,TP(傳送)給從從庫的IO_T
- 7. IO_T將收到的日誌存儲到了TCP/IP 緩存,立即返回ACK給主庫 ,主庫工作完成
- 8.IO_T將緩存中的數據,存儲到relay-log日誌文件,更新master.info文件binlog 文件名和postion,IO_T工作完成
- 9.SQL_T讀取relay-log.info文件,獲取到上次執行到的relay-log的位置,作爲起點,回放relay-log
- 10.SQL_T回放完成之後,會更新relay-log.info文件。
- 11. relay-log會有自動清理的功能。
- 細節:
- 1.主庫一旦有新的日誌生成,會發送“信號”給binlog dump ,IO線程再請求
5、 延時從庫
是我們認爲配置的一種特殊從庫.人爲配置從庫和主庫延時N小時.
爲什麼要有延時從?
數據庫故障?
物理損壞
主從複製非常擅長解決物理損壞.
邏輯損壞
普通主從複製沒辦法解決邏輯損壞
配置延時從庫
SQL線程延時:數據已經寫入relaylog中了,SQL線程"慢點"運行
一般企業建議3-6小時,具體看公司運維人員對於故障的反應時間
mysql>stop slave;
mysql>CHANGE MASTER TO MASTER_DELAY = 300;
mysql>start slave;
mysql> show slave status \G
SQL_Delay: 300
SQL_Remaining_Delay: NULL
延時從庫應用
1主1從,從庫延時5分鐘,主庫誤刪除1個庫
1. 5分鐘之內 偵測到誤刪除操作
2. 停從庫SQL線程
3. 截取relaylog
起點 :停止SQL線程時,relay最後應用位置
終點:誤刪除之前的position(GTID)
4. 恢復截取的日誌到從庫
5. 從庫身份解除,替代主庫工作
故障模擬及恢復
1.主庫數據操作
db01 [(none)]>create database relay charset utf8;
db01 [(none)]>use relay
db01 [relay]>create table t1 (id int);
db01 [relay]>insert into t1 values(1);
db01 [relay]>drop database relay;
2. 停止從庫SQL線程
stop slave sql_thread;
3. 找relaylog的截取起點和終點
起點:
Relay_Log_File: db01-relay-bin.000002
Relay_Log_Pos: 482
終點:
show relaylog events in 'db01-relay-bin.000002'
| db01-relay-bin.000002 | 1046 | Xid | 7 | 2489 | COMMIT /* xid=144 */ |
| db01-relay-bin.000002 | 1077 | Anonymous_Gtid | 7 | 2554 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
mysqlbinlog --start-position=482 --stop-position=1077 /data/3308/data/db01-relay-bin.000002>/tmp/relay.sql
4.從庫恢復relaylog
source /tmp/relay.sql
5.從庫身份解除
db01 [relay]>stop slave;
db01 [relay]>reset slave all
6、半同步複製
解決主從數據一致性問題
半同步複製工作原理的變化
- 1. 主庫執行新的事務,commit時,更新 show master status\G ,觸發一個信號給
- 2. binlog dump 接收到主庫的 show master status\G信息,通知從庫日誌更新了
- 3. 從庫IO線程請求新的二進制日誌事件
- 4. 主庫會通過dump線程傳送新的日誌事件,給從庫IO線程
- 5. 從庫IO線程接收到binlog日誌,當日志寫入到磁盤上的relaylog文件時,給主庫ACK_receiver線程
- 6. ACK_receiver線程觸發一個事件,告訴主庫commit可以成功了
- 7. 如果ACK達到了我們預設值的超時時間,半同步複製會切換爲原始的異步複製.
配置半同步複製
加載插件
主:
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
從:
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
查看是否加載成功:
show plugins;
啓動:
主:
SET GLOBAL rpl_semi_sync_master_enabled = 1;
從:
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
重啓從庫上的IO線程
STOP SLAVE IO_THREAD;
START SLAVE IO_THREAD;
查看是否在運行
主:
show status like 'Rpl_semi_sync_master_status';
從:
show status like 'Rpl_semi_sync_slave_status';
7、過濾複製
主庫:
show master status;
Binlog_Do_DB
Binlog_Ignore_DB
從庫:
show slave status\G
Replicate_Do_DB:
Replicate_Ignore_DB:
實現過程
mysqldump -S /data/3307/mysql.sock -A --master-data=2 --single-transaction -R --triggers >/backup/full.sql
vim /backup/full.sql
-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000002', MASTER_LOG_POS=154;
[root@db01 ~]# mysql -S /data/3309/mysql.sock
source /backup/full.sql
CHANGE MASTER TO
MASTER_HOST='10.0.0.51',
MASTER_USER='repl',
MASTER_PASSWORD='123',
MASTER_PORT=3307,
MASTER_LOG_FILE='mysql-bin.000002',
MASTER_LOG_POS=154,
MASTER_CONNECT_RETRY=10;
start slave;
[root@db01 ~]# vim /data/3309/my.cnf
replicate_do_db=ppt
replicate_do_db=word
[root@db01 ~]# systemctl restart mysqld3309
主庫:
Master [(none)]>create database word;
Query OK, 1 row affected (0.00 sec)
Master [(none)]>create database ppt;
Query OK, 1 row affected (0.00 sec)
Master [(none)]>create database excel;
Query OK, 1 row affected (0.01 sec)
8、GTID複製
GTID介紹
GTID(Global Transaction ID)是對於一個已提交事務的唯一編號,並且是一個全局(主從複製)唯一的編號。
它的官方定義如下:
GTID = source_id :transaction_id
7E11FA47-31CA-19E1-9E56-C43AA21293967:29
什麼是sever_uuid,和Server-id 區別?
核心特性: 全局唯一,具備冪等性
GTID核心參數
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
gtid-mode=on --啓用gtid類型,否則就是普通的複製架構
enforce-gtid-consistency=true --強制GTID的一致性
log-slave-updates=1 --slave更新是否記入日誌
GTID複製配置過程:
主庫db01:
cat > /etc/my.cnf <<EOF
[mysqld]
basedir=/data/mysql/
datadir=/data/mysql/data
socket=/tmp/mysql.sock
server_id=51
port=3306
secure-file-priv=/tmp
autocommit=0
log_bin=/data/binlog/mysql-bin
binlog_format=row
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
[mysql]
prompt=db01 [\\d]>
EOF
slave1(db02):
cat > /etc/my.cnf <<EOF
[mysqld]
basedir=/data/mysql
datadir=/data/mysql/data
socket=/tmp/mysql.sock
server_id=52
port=3306
secure-file-priv=/tmp
autocommit=0
log_bin=/data/binlog/mysql-bin
binlog_format=row
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
[mysql]
prompt=db02 [\\d]>
EOF
slave2(db03):
cat > /etc/my.cnf <<EOF
[mysqld]
basedir=/data/mysql
datadir=/data/mysql/data
socket=/tmp/mysql.sock
server_id=53
port=3306
secure-file-priv=/tmp
autocommit=0
log_bin=/data/binlog/mysql-bin
binlog_format=row
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
[mysql]
prompt=db03 [\\d]>
EOF
初始化數據
mysqld --initialize-insecure --user=mysql --basedir=/data/mysql --datadir=/data/mysql/data
啓動數據庫
/etc/init.d/mysqld start
構建主從
master:51
slave:52,53
51:
grant replication slave on *.* to repl@'10.0.0.%' identified by '123';
52\53:
change master to
master_host='10.0.0.51',
master_user='repl',
master_password='123' ,
MASTER_AUTO_POSITION=1;
start slave;
GTID 從庫誤寫入操作處理
查看監控信息:
Last_SQL_Error: Error 'Can't create database 'oldboy'; database exists' on query. Default database: 'oldboy'. Query: 'create database oldboy'
Retrieved_Gtid_Set: 71bfa52e-4aae-11e9-ab8c-000c293b577e:1-3
Executed_Gtid_Set: 71bfa52e-4aae-11e9-ab8c-000c293b577e:1-2,
7ca4a2b7-4aae-11e9-859d-000c298720f6:1
注入空事物的方法:
stop slave;
set gtid_next='99279e1e-61b7-11e9-a9fc-000c2928f5dd:3';
begin;commit;
set gtid_next='AUTOMATIC';
這裏的xxxxx:N 也就是你的slave sql thread報錯的GTID,或者說是你想要跳過的GTID。
最好的解決方案:重新構建主從環境
GTID 複製和普通複製的區別
CHANGE MASTER TO
MASTER_HOST='10.0.0.51',
MASTER_USER='repl',
MASTER_PASSWORD='123',
MASTER_PORT=3307,
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=444,
MASTER_CONNECT_RETRY=10;
change master to
master_host='10.0.0.51',
master_user='repl',
master_password='123' ,
MASTER_AUTO_POSITION=1;
start slave;
(0)在主從複製環境中,主庫發生過的事務,在全局都是由唯一GTID記錄的,更方便Failover
(1)額外功能參數(3個)
(2)change master to 的時候不再需要binlog 文件名和position號,MASTER_AUTO_POSITION=1;
(3)在複製過程中,從庫不再依賴master.info文件,而是直接讀取最後一個relaylog的 GTID號
(4) mysqldump備份時,默認會將備份中包含的事務操作,以以下方式
SET @@GLOBAL.GTID_PURGED='8c49d7ec-7e78-11e8-9638-000c29ca725d:1';
告訴從庫,我的備份中已經有以上事務,你就不用運行了,直接從下一個GTID開始請求binlog就行。
9. 主從故障監控\分析\處理
主庫:
show full processlist;
每個從庫都會有一行dump相關的信息
HOSTS:
db01:47176
State:
Master has sent all binlog to slave; waiting for more updates
如果現實非以上信息,說明主從之間的關係出現了問題
從庫:
db01 [(none)]>show slave status \G
*************************** 1. row ***************************
主庫相關信息監控
Master_Host: 10.0.0.51
Master_User: repl
Master_Port: 3307
Master_Log_File: mysql-bin.000005
Read_Master_Log_Pos: 444
從庫中繼日誌的應用狀態
Relay_Log_File: db01-relay-bin.000002
Relay_Log_Pos: 485
從庫複製線程有關的狀態
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
過濾複製有關的狀態
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
主從延時相關狀態(非人爲)
Seconds_Behind_Master: 0
延時從庫有關的狀態(人爲)
SQL_Delay: 0
SQL_Remaining_Delay: NULL
GTID 複製有關的狀態
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
主從複製故障分析
(1) 用戶 密碼 IP port
Last_IO_Error: error reconnecting to master '[email protected]:3307' - retry-time: 10 retries: 7
[root@db01 ~]# mysql -urepl -p123333 -h 10.0.0.51 -P 3307
ERROR 1045 (28000): Access denied for user 'repl'@'db01' (using password: YES)
原因:
密碼錯誤
用戶錯誤
skip_name_resolve
地址錯誤
端口
處理方法
stop slave
reset slave all
change master to
start slave
主庫連接數上線,或者是主庫太繁忙
show slave staus \G
Last_IO_Errno: 1040
Last_IO_Error: error reconnecting to master '[email protected]:3307' - retry-time: 10 retries: 7
處理思路:
拿複製用戶,手工連接一下
[root@db01 ~]# mysql -urepl -p123 -h 10.0.0.51 -P 3307
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1040 (HY000): Too many connections
處理方法:
db01 [(none)]>set global max_connections=300;
(3) 防火牆,網絡不通
請求二進制日誌
主庫缺失日誌
從庫方面,二進制日誌位置點不對
Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'could not find next log; the first event 'mysql-bin.000001' at 154, the last event read from '/data/3307/data/mysql-bin.000002' at 154, the last byte read from '/data/3307/data/mysql-bin.000002' at 154.'
注意: 在主從複製環境中,嚴令禁止主庫中reset master; 可以選擇expire 進行定期清理主庫二進制日誌
解決方案:
重新構建主從
SQL 線程故障
SQL線程功能:
(1)讀寫relay-log.info
(2)relay-log損壞,斷節,找不到
(3)接收到的SQL無法執行
導致SQL線程故障原因分析:
1. 版本差異,參數設定不同,比如:數據類型的差異,SQL_MODE影響
2.要創建的數據庫對象,已經存在
3.要刪除或修改的對象不存在
4.DML語句不符合表定義及約束時.
歸根揭底的原因都是由於從庫發生了寫入操作.
Last_SQL_Error: Error 'Can't create database 'db'; database exists' on query. Default database: 'db'. Query: 'create database db'
處理方法(以從庫爲核心的處理方案):
方法一:
stop slave;
set global sql_slave_skip_counter = 1;
#將同步指針向下移動一個,如果多次不同步,可以重複操作。
start slave;
方法二:
/etc/my.cnf
slave-skip-errors = 1032,1062,1007
常見錯誤代碼:
1007:對象已存在
1032:無法執行DML
1062:主鍵衝突,或約束衝突
但是,以上操作有時是有風險的,最安全的做法就是重新構建主從。把握一個原則,一切以主庫爲主.
一勞永逸的方法:
(1) 可以設置從庫只讀.
db01 [(none)]>show variables like '%read_only%';
注意:
只會影響到普通用戶,對管理員用戶無效。
(2)加中間件
讀寫分離。
主從延時監控及原因
主庫做了修改操作,從庫比較長時間才能追上.
外在因素
網絡
主從硬件差異較大
版本差異
參數因素
主庫
(1) 二進制日誌寫入不及時
[rep]>select @@sync_binlog;
(2) CR的主從複製中,binlog_dump線程,事件爲單元,串行傳送二進制日誌(5.6 5.5)
1. 主庫併發事務量大,主庫可以並行,傳送時是串行
2. 主庫發生了大事務,由於是串行傳送,會產生阻塞後續的事務.
解決方案:
1. 5.6 開始,開啓GTID,實現了GC(group commit)機制,可以並行傳輸日誌給從庫IO
2. 5.7 開始,不開啓GTID,會自動維護匿名的GTID,也能實現GC,我們建議還是認爲開啓GTID
3. 大事務拆成多個小事務,可以有效的減少主從延時.
從庫
SQL線程導致的主從延時
在CR複製情況下: 從庫默認情況下只有一個SQL,只能串行回放事務SQL
1. 主庫如果併發事務量較大,從庫只能串行回放
2. 主庫發生了大事務,會阻塞後續的所有的事務的運行
解決方案:
1. 5.6 版本開啓GTID之後,加入了SQL多線程的特性,但是隻能針對不同庫(database)下的事務進行併發回放.
2. 5.7 版本開始GTID之後,在SQL方面,提供了基於邏輯時鐘(logical_clock),binlog加入了seq_no機制,
真正實現了基於事務級別的併發回放,這種技術我們把它稱之爲MTS(enhanced multi-threaded slave).
3. 大事務拆成多個小事務,可以有效的減少主從延時.
[https://dev.mysql.com/worklog/task/?id=6314]