第1章 MySQL主從復制
1.1 數據庫損壞了?
主要理解為:業務不能使用數據庫
外在原因:
1、網絡問題
2、業務應用有問題,客戶端損壞
數據庫本身的原因:
1、物理損壞:機器壞了、硬盤壞了、存儲壞了
2、邏輯損壞:誤drop、delete、truncate、、update。
解決方案:
1、備份
2、主從復制
1.2 MySQL主從復制
1.2.1 MySQL復制概念
指將主數據庫的DDL和DML操作通過二進制日志傳到復制服務器上,然后在復制服務器上將這些日志文件重新執行,從而使復制服務器和主服務器的數據保持同步。復制過程中一個服務器充當主服務器(master),而一個或多個其它服務器充當從服務器(slaves)。主服務器將更新重新寫入二進制日志文件,並維護文件的一個索引以跟蹤日志循環。這些日志可以記錄發送到從服務器的更新。當一個從服務器連接主服務器時,它通知主服務器、從服務器在日志中讀取的最后一次成功更新的位置。從服務器接受從那時起發生的任何更新,然后封鎖並等待主服務器通知新的更新。
復制的用途:
通過主從復制(master-slave)的方式來同步數據,再通過讀寫分離(mysql-proxy)來提升數據庫的並發負載能力,或者用來作為主備機的設計,保證在主機停止響應之后在很短的時間內就可以將應用切換到備機上繼續運行。
優勢:
(1)數據庫集群系統具有多個數據庫節點,在單個節點出現故障的情況下,其他正常節點可以繼續提供服務。
(2)如果主服務器上出現了問題可以切換到從服務器上
(3)通過復制可以在從服務器上執行查詢操作,降低了主服務器的訪問壓力,實現數據分布和負載均衡
(4)可以在從服務器上進行備份,以避免備份期間影響主服務器的服務。
1.2.2 主從復制簡介
1、能做什么?
² 高可用
² 輔助備份
² 分擔負載
2.主從是怎么實現的?
² 通過二進制日志
² 至少兩台(主, 從)
² 主服務器的二進制日志”拿“到從服務器上再運行一遍。
² 通過網絡連接兩台機器,一般都會出現延遲的狀態。也可以說是異步。
3.MySQL主從復制的企業應用場景
² 應用場景1:從服務器作為主服務器的實時數據備份
² 應用場景2:主從服務器實現讀寫分離,從服務器實現負載均衡
² 應用場景3:把多個從服務器根據業務重要性進行拆分訪問
1.2.3 主從復制原理
1. 前提
² 主服務器一定要打開二進制日志
² 必須兩台服務器(或者是多個實例)
² 從服務器需要一次數據初始化
² 如果主從服務器都是新搭建的話,可以不做初始化
² 如果主服務器已經運行很長時間了,可以通過備份將主庫數據恢復到從庫
² 主庫必須要有對從庫復制請求的用戶。
² 從庫需要有relay-log設置,存放從主庫傳過來的二進制日志
² 在第一次的時候,從庫需要change master to 去連接主庫
² change mater 信息需要存放到master.info中
² 從庫通過relay-log.info記錄了已經應用過的relaylog信息發現了主庫發生了新的變化
² 在復制過程中涉及到的線程
n 從庫會開啟一個IO thread 負責連接主庫,請求binlog,接收binlog並寫入relay-log中。
n 從庫會開啟一個SQ thread 負責執行relay-log中的事件
n 主庫會開啟一個dump thread 復制響應從IO thread中請求
復制過程
復制原理
原理:第一次開啟主從
1.從庫通過change master to 語句連接主庫,並且讓從庫知道,二進制日志的起點位置(file名 position號)
2.從庫的IO和主庫的dump線程建立連接
3.從庫根據changemaster to 語句提供的file名和position號,IO線程向主庫發起binlog請求
4.主庫dump線程根據從庫的請求,將本地binlog以events方式發給從庫IO線程
5.從庫IO線程接收binlog events,並存放到本地relay-log中,傳送過來的信息,會記錄到master.info中。
6.從庫應用relay-log,並且把應用過來的記錄到relay-log.info,默認情況下,已經應用過來餓的relay會自動被清理purge。
到此,一次主從復制就完成,一旦主從運行起來,就不需要手工執行changemaster to,因為信息都會被存放到master.info 中,其他的過程也是一樣的。
注意:如果不是新搭建的主從,需要將原來的數據全備,進行同步到從庫中,然后在開啟change master to。
過程:
1.初始化數據,使用備份將主庫數據恢復到從庫。
2.主庫開啟binlog server id,從庫開啟server id 默認開啟relay log,建議自己設置relay-log,防止從庫突然修改主機名,relaylog會生成新的名字,原來的就找不到了
3.主庫中創建復制用戶
4.從庫開啟change change master to
5.開啟slave
6.驗證主從
1.1.1 配置主從
1.在主庫中添加用戶權限
grant replication slave on *.* to repl@'10.0.0.%' identified by '123456';
flush privileges;
初始化數據
2.mysqldump -uroot -p123456 -A -B -F --master-data=2 >/tmp/full.sql
scp /tmp/server.sql 10.0.0.53:/tmp
在從庫中進行source恢復
3.進入二進制文件找position號
在主庫中查看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)
[root@db02 ~]# vi /tmp/full.sql
-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=120;
4.進入從庫開啟主從復制
set sql_log_bin=0;
CHANGE MASTER TO
MASTER_HOST='10.0.0.52 ',
MASTER_USER='repl',
MASTER_PASSWORD='123456',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000004',
MASTER_LOG_POS=120;
注意:也可以設置relay-bin的名稱。
檢查狀態
start slave;
show slave status\G
成功的標志是:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
查看狀態
[root@db02 ~]# mysql -S /data/3307/mysql.sock -e "show slave status\G"|egrep "_Running|Behind_Master"|head -3
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
[root@db02 ~]#mysql> show slave status\G #查看狀態
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.0.0.52
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000005
Read_Master_Log_Pos: 544
Relay_Log_File: DB03-relay-bin.000002
Relay_Log_Pos: 754
Relay_Master_Log_File: mysql-bin.000005
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: 544
Relay_Log_Space: 957
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: 52
Master_UUID: 9cd29dbb-d003-11e7-a49d-000c29310c49
Master_Info_File: /application/mysql-5.6.36/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: 9cd29dbb-d003-11e7-a49d-000c29310c49:1-2
Executed_Gtid_Set: 9cd29dbb-d003-11e7-a49d-000c29310c49:1-2,
e19e3a5b-d33f-11e7-b9b6-000c29af0efd:1-123
Auto_Position: 1
1 row in set (0.00 sec)
mysql>
出現故障:
1、原因:
² 主機沒啟動,或者宕機
² 網絡通信問題
² 防火牆
² 復制用戶密碼(用戶密碼錯誤)
2、故障
Slave_IO_Running: Connecting
Slave_SQL_Running: Yes
或者
Slave_IO_Running: NO
Slave_SQL_Running: NO
原因:在change master to 的時候,填錯了用戶密碼,端口
1.1.2 配置主從注意事項
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.1.3 復制過濾
主庫方面:
白名單:只記錄白名單中列出的庫的二進制日志
binlog-do-db
黑名單:不記錄黑名單列出的庫的二進制日志
binlog-ignore-db
從庫
白名單:只執行白名單中列出的庫或者表的中繼日志
--replicate-do-db=test
--replicate-do-table=test.t1
--replicate-wild-do-table=test.x*
黑名單:不執行黑名單中列出的庫或者表的中繼日志
--replicate-ignore-db
--replicate-ignore-table
--replicate-wild-ignore-table
1.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.3 企業實例
背景:標准主從復制結構,在業務邏輯中有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.4 主從架構演變
1.4.1 演變
備份
- 相當於實時備份
- 使用從庫備份
問題:
如果從庫只是作為備份服務器使用,那么主庫的壓力會增加,因為所有的業務都在主庫進行讀寫(dump線程讀取並發送給binlog)
解決方法:
- 一主一從
分出部分讀業務到從庫(讀寫分離) - 一主多從,分擔壓力(針對讀業務多的需求)
但是這種一主多從的模式會使dump線程壓力更大了 - 多級主從
使用中間庫分擔主庫dump線程讀取分發binlog的壓力,由於中間庫只作為分發者,不需要其他操作,為了提高中間庫的性能,可以使用blackhole存儲引擎。 - 雙主模型
- 環狀復制
1.4.2 高級應用架構
性能
² 讀寫分離——MySQLproxy、amoeba、xx-dbproxy等。
² 分庫分表——cobar、自主研發等。
² 比較依賴於業務
² 實施思路:
² 判斷語句類型
² 根據語句類型進行分發
² 負載均衡,分發到從庫
² 會話持續性(減少用戶認證之類的操作)
² 判斷語句是否執行過(提高性能,減少重復操作)
高可用
² MMM架構——mysql-mmm(google)(不在使用)
² MHA架構——mysql-master-ha(日本DeNa)
² MGR ——5.7 新特性MySQLGroup replication
² PXC、MySQLCluster架構
1.4.3 多級主從部署(級聯主從)
類似於一主一從的部署
不同之處在於主從之間多了一個中間服務器
[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(關閉狀態)