將主數據庫中的DDL和DML操作通過二進制日志傳輸到從數據庫上,然后將這些日志重新執行(重做)一遍;從而使得從數據庫的數據與主數據庫保持一致。
1、MySQL 主從復制的基本介紹
MySQL支持單向、異步復制,復制過程中一個服務器充當主服務器,而一個或多個其它服務器充當從服務器。
MySQL復制是基於主服務器在二進制日志中跟蹤所有對數據庫的更改。因此,要進行復制,必須在主服務器上啟用二進制日志。每個從服務器從主服務器接收主服務器已經記錄到日志的數據。
當一個從服務器連接主服務器時,它通知主服務器從服務器在日志中讀取的最后一次成功更新的位置。從服務器接收從那時起發生的任何更新,並在本機上執行相同的更新。然后封鎖並等待主服務器通知新的更新。從服務器執行備份不會干擾主服務器,在備份過程中主服務器可以繼續處理更新。
2、什么是主從復制
簡單來說,是使用兩個或兩個以上相同的數據庫,將一個數據庫當做主數據庫,而另一個數據庫當做從數據庫。在主數據庫中進行相應操作時,從數據庫記錄下所有主數據庫的操作,使其二者一模一樣。
MySQL數據庫主從復制主要分為三步:
- master將修改數據的操作記錄到二進制日志(binary log)中(這些記錄叫做二進制日志事件,binary log events)。
- slave的io線程將master的binary log events拷貝到它的中繼日志(relay log)。
- slave的sql線程解析中繼日志中的事件並在從庫執行,保持與主庫的數據一致。
Binary log:主數據庫的二進制日志。
Relay log:從服務器的中繼日志。
注意:復制過程有一個很重要的限制——復制在slave上是串行化的,也就是說master上的並行更新操作不能在slave上並行操作。
1、從庫准備
- 從庫change master to 時,ip port user password binlog position寫入到master.info進行記錄。
- 從庫 start slave 時,會啟動IO線程和SQL線程。
2、同步的過程
- 從庫的IO線程,讀取master.info信息,獲取主庫信息並連接主庫。
- 主庫接收從庫的鏈接請求后,會生成一個准備binlog DUMP的線程,來響應從庫。
- 主庫一旦有新的日志生成,會發送“信號”給主庫的binlog dump線程,然后binlog dump線程會讀取binlog日志的更新。
- 通過binlog dump線程j將數據傳送給從庫的IO線程。
- IO線程將收到的日志存儲到了TCP/IP 緩存。
- 寫入TCP/IP緩存后,立即返回ACK消息給主庫 ,此時主庫工作完成。
- IO線程更新master.info文件、binlog 文件名和postion定位。
- IO線程將緩存中的數據,存儲到relay-log日志文件,此時io線程工作完成。
- 從庫SQL線程讀取relay-log.info文件,獲取到上次執行到的relay-log的位置,作為起點。
- 從庫SQL線程基於從步驟9中獲取到的起點,去中繼日志relay-log.000001獲取后續操作,在從庫回放relay-log中繼日志之中內從主庫復制過來的數據。
- SQL線程回放完成之后,會更新relay-log.info文件,把當前操作的位置記入,作為下一次操作的起點。
- relay-log會有自動清理的功能。
注:在tcp協議中通訊之前都要經過三次握手,請求方發出一個syn信號請求連接,對方收到並接受的時候就會發出ack消息,ack就是回應的意思。
3、主從復制的方式
MySQL的主從復制有兩種復制方式,分別是異步復制和半同步復制。
1、異步復制
我們之前介紹的就是異步復制,即客戶端線程提交一個寫操作,寫入主庫的binlog日志后就立即返回,並不需要等待從庫完成同步操作,而主庫的dump線程會監測binlog日志的變量然后主動將更新推送給從庫。
MySQL 主從復制默認是異步的模式。
1.1、主從復制實現
要實現主從復制,需要如下幾步:
1、在主庫上創建一個用於復制的賬號。
2、修改主庫配置文件,開啟主庫的Binlog,並設置server-id和重啟。
3、導出主庫中所有的數據,先導給從庫
4、修改從庫配置文件並重啟
5、配置主從復制
6、開啟主從復制
- 在主庫上創建一個用於復制的賬號
mysql> grant replication slave on *.* to 'shanhe'@'%' identified by '123456'; mysql> flush privileges;
- 修改主庫配置文件
[root@mysql-1 ~]# cat /etc/my.cnf [mysqld] basedir=/usr/local/mysql datadir=/data port=3306 socket=/usr/local/mysql/mysql.sock character-set-server=utf8 log-error=/var/log/mysqld.log pid-file=/tmp/mysqld.pid server-id=1 log-bin=/data/log-bin/binlog sync_binlog=1 binlog_format=row expire_logs_days=7 max_binlog_size=100m binlog_cache_size=4m max_binlog_cache_size=512m binlog-ignore-db=mysql auto-increment-offset=1 auto-increment-increment=1 slave-skip-errors=all [mysql] socket=/usr/local/mysql/mysql.sock [client] socket=/usr/local/mysql/mysql.sock # 重啟 [root@mysql-1 ~]# systemctl restart mysqld
- 導出主庫中所有的數據,先導給從庫
[root@mysql-1 ~]# mysqldump -uroot -p123456 -A -E -R --triggers --master-data=2 --single-transaction > /tmp/all.sql
- 將數據導入從庫
[root@mysql-1 ~]# scp /tmp/all.sql root@192.168.15.62:/tmp/ root@192.168.15.62''s password: all.sql 100% 853KB 3.5MB/s 00:00 [root@mysql-2 mysql]# mysql -uroot -p123456 < /tmp/all.sql
- 修改從庫配置文件並重啟
[root@mysql-2 mysql]# vim /etc/my.cnf [mysqld] basedir=/usr/local/mysql datadir=/data port=3306 socket=/usr/local/mysql/mysql.sock character-set-server=utf8 log-error=/var/log/mysqld.log pid-file=/tmp/mysqld.pid server-id=2 # 中繼日志 relay-log=mysql-relay-bin replicate-wild-ignore-table=mysql.% replicate-wild-ignore-table=test.% replicate-wild-ignore-table=information_schema.% [mysql] socket=/usr/local/mysql/mysql.sock [client] socket=/usr/local/mysql/mysql.sock [root@mysql-2 mysql]# systemctl restart mysqld
- 配置主從復制
配置主從復制,首先得在MySQL Master節點查出binlog日志狀態,然后配置主從復制
- 在MySQL Master節點查出binlog日志狀態
mysql> show master status ; +---------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +---------------+----------+--------------+------------------+-------------------+ | binlog.000001 | 154 | | mysql | | +---------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec)
- 在從庫配置主從復制
# 首先測試主庫是否連接正常 [root@mysql-2 mysql]# mysql -uroot -p123456 -h192.168.15.61 [root@slave1 ~]# mysql -uroot -p123456 # 登錄然后執行 change master to master_host='192.168.15.61', -- 庫服務器的IP master_port=3306, -- 主庫端口 master_user='shanhe', -- 主庫用於復制的用戶 master_password='123456', -- 密碼 master_log_file='binlog.000001', -- 主庫日志名 master_log_pos=154; -- 主庫日志偏移量,即從何處開始復制
- 查看主從復制結果
2、半同步復制
介於異步復制和全同步復制之間,主庫在執行完客戶端提交的事務后不是立刻返回給客戶端,而是等待至少一個從庫接收到並寫到relay log中才返回給客戶端。相對於異步復制,半同步復制提高了數據的安全性,同時它也造成了一定程度的延遲,這個延遲最少是一個TCP/IP往返的時間。所以,半同步復制最好在低延時的網絡中使用。
半同步復制超時則會切換回異步復制,正常后則切回半同步復制
在半同步復制時,如果主庫的一個事務提交成功了,在推送到從庫的過程當中,從庫宕機了或網絡故障,導致從庫並沒有接收到這個事務的Binlog,此時主庫會等待一段時間(這個時間由rpl_semi_sync_master_timeout的毫秒數決定),如果這個時間過后還無法推送到從庫,那MySQL會自動從半同步復制切換為異步復制,當從庫恢復正常連接到主庫后,主庫又會自動切換回半同步復制。
2.1、部署半同步復制
半同步模式是作為MySQL5.5的一個插件來實現的,主從庫使用的插件不一樣。
- 先確認主從的MySQL服務器是否支持動態增加插件
mysql> select @@have_dynamic_loading; +------------------------+ | @@have_dynamic_loading | +------------------------+ | YES | +------------------------+ 1 row in set (0.08 sec)
- 分別在主從庫上安裝對用插件
-- 主庫安裝插件 mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so'; Query OK, 0 rows affected (0.28 sec) -- 從庫安裝插件 mysql> install plugin rpl_semi_sync_slave soname 'semisync_slave.so'; Query OK, 0 rows affected (0.20 sec)
- 在主庫開啟半同步復制
mysql> set global rpl_semi_sync_master_enabled=1; Query OK, 0 rows affected (0.09 sec) mysql> set global rpl_semi_sync_master_timeout=30000; Query OK, 0 rows affected (0.00 sec) # 添加到配置文件 [root@mysql-1 ~]# vim /etc/my.cnf rpl_semi_sync_master_enabled=1 rpl_semi_sync_master_timeout=1000
- 在從庫開啟半同步復制
mysql> set global rpl_semi_sync_slave_enabled=1; Query OK, 0 rows affected (0.07 sec) mysql> stop slave io_thread; Query OK, 0 rows affected (0.09 sec) mysql> start slave io_thread; Query OK, 0 rows affected (0.06 sec) # 添加到配置文件之中 [root@mysql-2 ~]# vim /etc/my.cnf rpl_semi_sync_slave_enabled =1
- 在主庫上查看半同步復制的狀態
-- 主庫查看 mysql> show status like 'Rpl_semi_sync_master_status'; +-----------------------------+-------+ | Variable_name | Value | +-----------------------------+-------+ | Rpl_semi_sync_master_status | ON | +-----------------------------+-------+ 1 row in set (0.00 sec) -- 從庫查看 mysql> show status like 'Rpl_semi_sync_slave_status'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | ON | +----------------------------+-------+ 1 row in set (0.00 sec)
這兩個變量常用來監控主從是否運行在半同步復制模式下。
3、多主多從
在企業中,數據庫高可用一直是企業的重中之重,中小企業很多都是使用mysql主從方案,一主多從,讀寫分離等,但是單主存在單點故障,從庫切換成主庫需要作改動。因此,如果是雙主或者多主,就會增加mysql入口,增加高可用。不過多主需要考慮自增長ID問題,這個需要特別設置配置文件,比如雙主,可以使用奇偶,總之,主之間設置自增長ID相互不沖突就能完美解決自增長ID沖突問題。
3.1、MySQL雙主(主主)架構方案思路
- 兩台mysql都可讀寫,互為主備,默認只使用一台(masterA)負責數據的寫入,另一台(masterB)備用。
- masterA是masterB的主庫,masterB又是masterA的主庫,它們互為主從。
- 兩台主庫之間做高可用,可以采用keepalived等方案(使用VIP對外提供服務)。
- 所有提供服務的從服務器與masterB進行主從同步(雙主多從)。
- 建議采用高可用策略的時候,masterA或masterB均不因宕機恢復后而搶占VIP(非搶占模式)。
這樣做可以在一定程度上保證主庫的高可用,在一台主庫down掉之后,可以在極短的時間內切換到另一台主庫上(盡可能減少主庫宕機對業務造成的影響),減少了主從同步給線上主庫帶來的壓力;但是也有幾個不足的地方:
- masterB可能會一直處於空閑狀態。
- 主庫后面提供服務的從庫要等masterB先同步完了數據后才能去masterB上去同步數據,這樣可能會造成一定程度的同步延時。
3.2、修改主節點的配置文件
[mysqld] basedir=/usr/local/mysql datadir=/data port=3306 socket=/usr/local/mysql/mysql.sock character-set-server=utf8 log-error=/var/log/mysqld.log pid-file=/tmp/mysqld.pid # 節點ID,確保唯一 server-id = 1 #開啟mysql的binlog日志功能 log-bin=binlog #控制數據庫的binlog刷到磁盤上去 , 0 不控制,性能最好,1每次事物提交都會刷到日志文件中,性能最差,最安全 sync_binlog=1 #binlog日志格式 binlog_format=row #binlog過期清理時間 expire_logs_days=7 #binlog每個日志文件大小 max_binlog_size=100m #binlog緩存大小 binlog_cache_size=4m #最大binlog緩存大小 max_binlog_cache_size=512m #不生成日志文件的數據庫,多個忽略數據庫可以用逗號拼接,或者 復制黏貼下述配置項,寫多行 binlog-ignore-db=mysql # 表中自增字段每次的偏移量 auto-increment-offset=1 # 表中自增字段每次的自增量 auto-increment-increment=2 #跳過從庫錯誤 slave-skip-errors=all #將復制事件寫入binlog,一台服務器既做主庫又做從庫此選項必須要開啟 log-slave-updates=true gtid-mode=on enforce-gtid-consistency=true master-info-repository=file relay-log-info-repository=file sync-master-info=1 slave-parallel-workers=0 sync_binlog=0 binlog-checksum=CRC32 master-verify-checksum=1 slave-sql-verify-checksum=1 binlog-rows-query-log_events=1 max_binlog_size=1024M