一、MySQL主從複製
1.1 爲什麼需要主從?
數據庫損壞了(業務不能使用數據庫)
原因:
- 外在原因
- 網絡
- 業務應用有問題
- 本身原因
- 物理損壞:機器壞了、硬盤損壞、存儲壞了、數據文件損壞
- 邏輯損壞:錯誤的drop、delete、truncate、update。。。
解決方案:
- 備份(物理損壞丟失數據)
- 主從複製
1.2 MySQL複製介紹
複製是MySQL的一項功能,允許服務器將更改從一個實例複製到另一個實例。
- 主服務器將所有數據和結構更改記錄到二進制日誌中。
- 從屬服務器從主服務器請求該二進制日誌並在本地應用其內容。
1.2.1 複製作用:
- 高可用
- 輔助備份
- 分擔負載
1.2.2 應用場景:
- 應用場景1:從服務器作爲主服務器的實時數據備份
- 應用場景2:主從服務器實現讀寫分離,從服務器實現負載均衡
- 應用場景3:把多個從服務器根據業務重要性進行拆分訪問
1.2.3 複製前提
- 主服務器必須開啓二進制日誌(binlog)
- 多臺服務器
- 保證開始複製時主從服務器結構一致。
- 對於新建的數據庫可以直接進行復制;
- 對於運行了一段時間的數據庫,就要通過備份讓主從數據庫結構保證一致。
- 主庫必須要有對從庫複製請求的用戶
- 從庫需要有relay-log設置,存放從主庫傳來的binlog
- 首次連接,從庫需要change master to連接主庫
- 需要保存change master信息需要存放在master.info文件中
- 通過relay-log.info裏記錄的已經使用過的事件,從庫知道主庫發生了變化
- 複製中的線程
- 主庫
- dump thread:負責響應從庫的IO線程
- 從庫
- IO thread :負責連接主庫,請求binlog,接收binlog並寫入relay-log
- SQL thread :複製執行relay-log中的事件
- 主庫
1.2.4 複製原理
異步複製過程
總體來說,複製有3個步驟:
- 主服務器把數據更改記錄到二進制日誌中。(這叫做二進制日誌事件)
- 從服務器把主服務器的二進制日誌拷貝到自己的中繼日誌中。
- 從服務器和主服務器之間建立master/slave連接,相關信息存放在從服務器的master.info文件中
- 從服務器用IO線程詢問主服務器是否有新的binlog
- 主服務器用dump線程讀取binlog併發送給從服務器
- 從服務器接收binlog並存放在relay-log中
- 從服務器的SQL線程執行relay-log中的binlog,寫入數據庫
- 執行完畢的relay-log放在relay-log.info文件中
- 從服務器重放中繼日誌中的事件,把更改應用到自己的數據上。
1.3 主從複製部署
兩臺主機安裝mysql 5.6
注意在兩臺主機的配置文件中/etc/my.cnf中需要添加server-id=
參數。
-
創建複製用戶
grant replication slave on *.* to repl@'10.0.0.%' identified by '123';
-
初始化從庫
[root@db01 ~]# mysqldump -uroot -poldboy123 -A -F > /tmp/server.sql [root@db01 ~]# scp /tmp/server.sql 10.0.0.8:/tmp 在從庫中source執行
-
開啓主從複製
- 在主庫中查看binlog起始點:
mysql> mysql> show master; +----------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +----------------+----------+--------------+------------------+-------------------+ | log-bin.000013 | 120 | | | | +----------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec)
- 從庫開啓複製:
mysql> change master to master_host='10.0.0.51', master_port=3306, master_user='repl', master_password='123', master_log_file='log-bin.000013', master_log_pos=120;
注意:也可以設置relay-bin的名稱。
-
檢查狀態
start slave; show slave status\G
成功的標誌是:
Slave_IO_Running: Yes Slave_SQL_Running: Yes
1.4 監控主從複製狀態
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.0.0.51
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: log-bin.000014
Read_Master_Log_Pos: 120
Relay_Log_File: web01-relay-bin.000002
Relay_Log_Pos: 281
Relay_Master_Log_File: log-bin.000014
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 120
Relay_Log_Space: 454
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: d4519488-d005-11e7-a4ac-000c2924dc94
Master_Info_File: /application/mysql-5.6.38-linux-glibc2.12-x86_64/data/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
1 row in set (0.00 sec)
1.4.1 注意事項
-
Slave_*_Running:
- Slave_IO_RunningI/O 線程正在運行、未運行還是正在運行但尚未連接到主服務器。可能值分別爲Yes、No 或Connecting。
- Slave_SQL_RunningSQL 線程當前正在運行、未運行,可能值分別爲Yes、No
-
主服務器日誌座標:
- Master_Log_File和Read_Master_Log_Pos標識主服務器二進制日誌中I/O 線程已經傳輸的最近事件的座標。
- 如果Master_Log_File和Read_Master_Log_Pos的值遠遠落後於主服務器上的那些值,這表示主服務器與從屬服務器之間事件的網絡傳輸可能存在延遲。
-
中繼日誌座標:
- Relay_Log_File和Relay_Log_Pos列標識從屬服務器中繼日誌中SQL 線程已經執行的最近事件的座標。這些座標對應於Relay_Master_Log_File和Exec_Master_Log_Pos列標識的主服務器二進制日誌中的座標。
- 如果Relay_Master_Log_File和Exec_Master_Log_Pos列的輸出遠遠落後於Master_Log_File和Read_Master_Log_Pos列(表示I/O 線程的座標),這表示SQL 線程(而不是I/O 線程)中存在延遲。即,它表示複製日誌事件快於執行這些事件。
-
Last_IO_Error、Last_SQL_Error:
+分別導致I/O 線程或SQL 線程停止的最新錯誤的錯誤消息。在正常複製過程中,這些字段是空的。如果發生錯誤並導致消息顯示在以上任一字段中,則錯誤值也顯示在錯誤日誌中。 -
Last_IO_Errno、Last_SQL_Errno:
- 與分別導致I/O 線程或SQL 線程停止的最新錯誤關聯的錯誤編號。在正常複製過程中,這些字段包含編號0。
- Last_IO_Error_Timestamp、Last_SQL_Error_Timestamp:
- 分別導致I/O 線程或SQL 線程停止的最新錯誤的時間戳,格式爲YYMMDD HH:MM:SS。在正常複製過程中,這些字段是空的。
1.4.2 錯誤實例
-
從庫binlog落後於主庫
Master_Log_File: log-bin.000014 Read_Master_Log_Pos: 120
從庫的logbin比主庫的logbin慢的原因:
- 網絡問題
- 主庫dump線程繁忙
- 從庫IO線程繁忙
【擴展】
延時節點概念:是SQL線程延時,不是IO線程延時。 - SQL線程報錯
原因:- 主庫操作對象在從庫中不存在
- 主庫操作對象的屬性和從庫不一致
- 主從操作順序顛倒
解決方法:
- 跳過錯誤
stop slave; set global sql_slave_skip_counter = 1; start slave;
也可以在配置文件中跳過錯誤號碼:
[mysqld] slave-skip-errors = 1032,1062,1007
1.5 企業實例
- 背景:標準主從複製結構,在業務邏輯中有oldboy數據庫,oldboy數據庫下有t1表爲生產表。
- 故障原因:開發人員在從庫創建了一個oldgirl庫,覺得不對,後又在主庫中做了相同的操作。導致了從庫複製失效。
- 解決方案:
主從複製故障及解決stop slave; #<==臨時停止同步開關。 set global sql_slave_skip_counter= 1 ; #<==將同步指針向下移動一個,如果多次不同步,可以重複操作。 start slave; /etc/my.cnf slave-skip-errors = 1032,1062,1007
- 如何避免問題?
- 從庫設置爲只讀庫
在my.cnf中添加read_only=1 - 單獨在從庫創建一個只讀用戶
在主庫創建寫用戶
優點: - 配置時不需要重啓
- 故障切換時也不需要重啓
- 從庫設置爲只讀庫
1.6 主從架構演變
備份
- 相當於實時備份
- 使用從庫備份
問題:
如果從庫只是作爲備份服務器使用,那麼主庫的壓力會增加,因爲所有的業務都在主庫進行讀寫(dump線程讀取併發送給binlog)
解決方法:
- 一主一從
分出部分讀業務到從庫(讀寫分離) - 一主多從,分擔壓力(針對讀業務多的需求)
但是這種一主多從的模式會使dump線程壓力更大了 - 多級主從
使用中間庫分擔主庫dump線程讀取分發binlog的壓力,由於中間庫只作爲分發者,不需要其他操作,爲了提高中間庫的性能,可以使用blackhole存儲引擎。 - 雙主模型
- 環狀複製
1.7 高級應用架構
-
性能
- 讀寫分離——MySQLproxy、amoeba、xx-dbproxy等。
- 分庫分表——cobar、自主研發等。
比較依賴於業務 - 實施思路:
- 判斷語句類型
- 根據語句類型進行分發
- 負載均衡,分發到從庫
- 會話持續性(減少用戶認證之類的操作)
- 判斷語句是否執行過(提高性能,減少重複操作)
- 高可用
- MMM架構——mysql-mmm(google)(不在使用)
- MHA架構——mysql-master-ha(日本DeNa)
- MGR ——5.7 新特性MySQLGroup replication
- PXC、MySQLCluster架構
1.8 多級主從部署(級聯主從)
類似於一主一從的部署
不同之處在於主從之間多了一箇中間服務器
[mysqld]
basedir = /application/mysql/
datadir = /application/mysql/data/
socket = /application/mysql/tmp/mysql.sock
character_set_server=utf8
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
server-id = 2
log-bin=/tmp/log-bin
binlog-format=row
autocommit=1
log-slave-updates
[client]
socket = /application/mysql/tmp/mysql.sock
在中間服務器的my.cnf文件中需要開啓binlog並添加log-slave-updates
參數,表示強制刷新binlog,否則binlog日誌不會刷新。
相當於做了兩套主從。
reset slave;
重置slave(關閉狀態)
1.9 擴展
1.9.1 複製延時
- 問題:
主服務器的錯誤操作會同步到從服務器,導致數據恢復比較麻煩。 - 解決方法:
採用複製延時,這樣主服務器操作錯誤,從服務器由於延時複製可以在一段時間內避免應用錯誤操作,這樣就可以及時恢復數據。
複製延時是在SQL線程的層面進行控制,不允許SQL線程實時的執行relay log中的操作。
- 如何設置:
stop slave;
change master to master_delay = 30; #單位是秒
start slave;
結果:
```shell
mysql> show slave status\G
SQL_Delay: 30
SQL_Remaining_Delay: NULL
生產場景中一般延時3-6小時
1.9.2 半同步複製
- 注重安全,不注重性能
- 普通異步主從中從庫的同步率是不可控的,總會有延時的
- 對於安全性要求比較高的應用場景,比如金融、運營商等不會使用普通異步主從架構。
- 爲了讓MySQL更加能夠適用於高安全性的場景纔有了半同步複製。
- 半同步基於dump線程和IO線程,省略了SQL線程讀取寫入的部分
部署:
1、加載插件
主:
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
從:
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
2、查看是否加載成功:
show plugins;
3、啓動:
主:
SET GLOBAL rpl_semi_sync_master_enabled = 1;
從:
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
只是臨時啓動,需要寫入配置文件中。
4、重啓從庫上的IO線程
STOP SLAVE IO_THREAD;
START SLAVE IO_THREAD;
5、查看是否在運行
主:
show status like 'Rpl_semi_sync_master_status';
從:
show status like 'Rpl_semi_sync_slave_status';
1.9.3 主從同步的故障轉移(failover)
MHA設計理念:
主服務器宕掉了,但是多臺從服務器的數據和主服務器同步不完整,這時就需要整合多臺從服務器中的同步的數據到新的主服務器中,儘量保證數據的完整性。
- 選擇新主
- 數據補償:判斷新主服務器和其他從節點數據的新舊,補全自己的數據,儘量恢復到比較新的數據,或者去舊主服務器中獲取binlog日誌補全自己的數據
- 啓動新主,將其他從服務器指向新主
- 公佈新主
1.9.4 GTID複製
中繼日誌(relay log):記錄了events和position號
在執行的事務中打上一個唯一標籤,這樣就可以保證事務之間的連續性及唯一性
爲了failover出現的更好的複製,5.6出現,5.7完善
GTID(Global Transaction ID)是對於一個已提交事務的編號,並且是一個全局唯一的編號。
它的官方定義如下:
GTID = source_id :transaction_id
7E11FA47-31CA-19E1-9E56-C43AA21293967:29
[root@web01 ~]# cat /application/mysql/data/auto.cnf
[auto]
server-uuid=0b920fba-d0fa-11e7-aae4-000c292741de
注意:如果是克隆的mysql數據庫,那麼server-uuid相同會導致slave-IO無法啓動,需要修改server-uuid
- 部署過程
環境:
需要兩臺mysql數據庫服務器,一臺爲主服務器,一臺爲從服務器。
1、修改配置文件
主:
[mysqld]
log_bin = /tmp/log-bin
binlog-format = row
basedir = /application/mysql/
datadir = /application/mysql/data
socket = /application/mysql/tmp/mysql.sock
server_id = 1
gtid-mode = on #啓用gtid類型,否則就是普通的複製架構
enforce-gtid-consistency = true #強制GTID的一致性
log-slave-updates = 1 #slave更新是否記入日誌
autocommit = 1
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
character_set_server=utf8
[client]
socket = /application/mysql/tmp/mysql.sock
從:
[mysqld]
log_bin = /tmp/log-bin
binlog-format=ROW
basedir = /application/mysql/
datadir = /application/mysql/data/
server_id = 2
socket = /application/mysql/tmp/mysql.sock
gtid-mode = on
enforce-gtid-consistency = true
log_slave_updates = 1
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
character_set_server=utf8
autocommit = 1
[client]
socket = /application/mysql/tmp/mysql.sock
注意:如果是新建的數據庫可以不需要從庫初始化;如果不是需要從庫初始化,同步主從的結構屬性
2、在主服務器添加複製用戶
grant replication slave on *.* to repl@'10.0.0.%' identified by '123';
3、在從服務器上設置change master
mysql> change master to
master_host='10.0.0.51',
master_port=3306,
master_user='repl',
master_password='123',
master_auto_position=1;
4、開啓slave
start slave;
5、查看效果
在主庫中添加一個數據,查看master
mysql> show master status;
+----------------+----------+--------------+------------------+------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+----------------+----------+--------------+------------------+------------------------------------------+
| log-bin.000003 | 552 | | | ff185ff4-cec5-11e7-9c86-000c2924dc94:1-2 |
+----------------+----------+--------------+------------------+------------------------------------------+
1 row in set (0.00 sec)
再從庫中查看slave:
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.0.0.51
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: log-bin.000003
Read_Master_Log_Pos: 552
Relay_Log_File: db02-relay-bin.000004
Relay_Log_Pos: 442
Relay_Master_Log_File: log-bin.000003
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 552
Relay_Log_Space: 1252
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: ff185ff4-cec5-11e7-9c86-000c2924dc94
Master_Info_File: /application/mysql-5.6.38/data/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set: ff185ff4-cec5-11e7-9c86-000c2924dc94:1-2
Executed_Gtid_Set: ff185ff4-cec5-11e7-9c86-000c2924dc94:1-2
Auto_Position: 1
1 row in set (0.00 sec)