什麼,你還不會Mysql主從複製???快來看

目錄

1、爲什麼需要主從複製?

2、什麼是mysql主從複製

3、Mysql複製原理

4、主從複製簡介

5、 延時從庫

6、半同步複製

7、過濾複製

8、GTID複製

9. 主從故障監控\分析\處理

主從複製故障分析


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]

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章