1. 程式人生 > >MySQL主從同步詳解

MySQL主從同步詳解

一:mysql主從原理

1.1 基本介紹
MySQL 內建的複製功能是構建大型,高效能應用程式的基礎。將 MySQL 的 數億分佈到到多個系統上去,這種分步的機制,是通過將 MySQL 的某一臺主機的資料複製到其它主機( Slave )上,並重新執行一遍來實現的。複製過程中一個伺服器充當伺服器,而一個或多個其它伺服器充當從伺服器。主伺服器將更新寫入二進位制日誌,並維護檔案的一個索引以跟蹤日誌迴圈。這些日誌可以記錄傳送到從伺服器的更新。當一個從伺服器連線主伺服器時,它通知主伺服器從伺服器在日誌中讀取的最後一次成功更新的位置,從伺服器接收從那時起發生的任何更新,然後封鎖等等主伺服器通知新的更新。
請注意當你進行復制時,所有對複製中的表的更新必須在主伺服器上進行。否則,你必須要小心,以避免使用者對主伺服器上的表進行的更新與對伺服器上的表所進行的更新之間的衝突

1.2 MySQL支援的複製型別
基於語句的複製。 在主伺服器上執行的 SQL 語句,在從伺服器上執行同樣的語句。否則,你必須要小心,以避免使用者對主伺服器上的表進行的更新與對伺服器上的表所進行的更新之間的衝突,配置:binlog_format = ‘STATEMENT’
基於行的複製。把改變的內容複製過去,而不是把命令在從伺服器上執行一遍,從 MySQL 5.0開始支援,配置:binlog_format = ‘ROW’
混合型別的複製。預設採用基於語句的複製,一旦發現基於語句的無法精確的複製時,就會採用基於行的複製,配置:binlog_format = ‘MIXED’

1.3 mysql複製解決的問題
資料分佈
負載平衡
備份
高可用性和容錯行

1.4 複製是如何工作的
MySQL之間資料複製的基礎是二進位制日誌檔案(binary log file)。一臺MySQL資料庫一旦啟用二進位制日誌後,其作為master,它的資料庫中所有操作都會以“事件”的方式記錄在二進位制日誌中,其他資料庫作為slave通過一個I/O執行緒與主伺服器保持通訊,並監控master的二進位制日誌檔案的變化,如果發現master二進位制日誌檔案發生變化,則會把變化複製到自己的中繼日誌中,然後slave的一個SQL執行緒會把相關的“事件”執行到自己的資料庫中,以此實現從資料庫和主資料庫的一致性,也就實現了主從複製。
可以簡化為三個步驟(如下圖):
Master 將改變記錄到二進位制日誌中。
Slave 將 Master 的二進位制日誌拷貝到它的中繼日誌( Relay_log )
Slave 重做中繼日誌中的事件,將改變反映它自己的資料

Master 記錄二進位制的日誌。在每個事務更新資料之前,Master 在二進位制日誌記錄這些改變。 MySQL 將事務日誌寫入二進位制日誌,事務中的語句都是交叉執行的。在事件寫入二進位制日誌完成後,Master 通知儲存引擎提交事務。
Slave 將 Master 的 Binary log 拷貝到它自己的中繼日誌。首先 Slave 開始一個工作執行緒–I/O執行緒。I/O 執行緒在 Master 上開啟一個連線,然後開始從二進位制日誌中讀取事件,如果已經連上 Master,它會並等待master產生新的事件。I/O執行緒就這些事件寫入中繼日誌。
SQL Slave Thread ( SQL從執行緒)處理該過程的最後一步。SQL純種從中繼日誌讀取事件,並重放其中的事件而更新 Slave 的資料。使其它與 Master 中的資料保持一致。只要該執行緒與 I/O 執行緒保持一致,中繼日誌通常會位於 OS 的快取中,所以中繼日誌的開銷很小。
此處,在 Master 中也有一個工作執行緒,和其他 MySQL 的連線一樣,Slave 在 Master 中開啟一個連線也會使得 Master 開始一個執行緒。複製過程有一個很重要的限制—複製在 Slave 上是序列化的,也就是說 Master 上的並行更新操作不能在 Slave 上並行操作。
1.5 複製實現細節分析

MySQL主從複製功能使用三個執行緒實現,一個在主伺服器上,兩個在從伺服器上

1.5.1 Binlog轉儲執行緒。

當從伺服器與主伺服器連線時,主伺服器會建立一個執行緒將二進位制日誌內容傳送到從伺服器。
該執行緒可以使用 語句 SHOW PROCESSLIST(下面有示例介紹) 在伺服器 sql 控制檯輸出中標識為Binlog Dump執行緒。

二進位制日誌轉儲執行緒獲取伺服器上二進位制日誌上的鎖,用於讀取要傳送到從伺服器的每個事件。一旦事件被讀取,即使在將事件傳送到從伺服器之前,鎖會被釋放。

1.5.2 從伺服器I/O執行緒。

當在從伺服器sql 控制檯發出 START SLAVE語句時,從伺服器將建立一個I/O執行緒,該執行緒連線到主伺服器,並要求它傳送記錄在主伺服器上的二進位制更新日誌。

從機I/O執行緒讀取主伺服器Binlog Dump執行緒傳送的更新 (參考上面 Binlog轉儲執行緒 介紹),並將它們複製到自己的本地檔案二進位制日誌中。

該執行緒的狀態顯示詳情 Slave_IO_running 在輸出端 使用 命令SHOW SLAVE STATUS

使用\G語句終結符,而不是分號,是為了,易讀的垂直佈局

這個命令在上面 檢視從伺服器狀態 用到過

mysql> SHOW SLAVE STATUS\G
1
1.5.3 從伺服器SQL執行緒。

從伺服器建立一條SQL執行緒來讀取由主伺服器I/O執行緒寫入的二級制日誌,並執行其中包含的事件。

在前面的描述中,每個主/從連線有三個執行緒。主伺服器為每個當前連線的從伺服器建立一個二進位制日誌轉儲執行緒,每個從伺服器都有自己的I/O和SQL執行緒。
從伺服器使用兩個執行緒將讀取更新與主伺服器更新事件,並將其執行為獨立任務。因此,如果語句執行緩慢,則讀取語句的任務不會減慢。

例如,如果從伺服器開始幾分鐘沒有執行,或者即使SQL執行緒遠遠落後,它的I/O執行緒也可以從主伺服器建立連線時,快速獲取所有二進位制日誌內容。

如果從伺服器在SQL執行緒執行所有獲取的語句之前停止,則I/O執行緒至少獲取已經讀取到的內容,以便將語句的安全副本儲存在自己的二級制日誌檔案中,準備下次執行主從伺服器建立連線,繼續同步。

二 搭建主從同步資料庫服務
1 從庫自動同步主庫的資料到本地
配置:
(1) 主庫啟動binlog日誌,主庫上的執行緒為Binlog轉儲執行緒
(2 )從庫上會有2個子程式,IO和SQL執行緒
IO將主庫上的binlog日誌檔案裡的SQL命令讀取到本地的relay-log(中繼日誌)檔案裡
SQL執行緒執行relay-log日誌裡的sql語句,重現master的資料操作

2 在配置主從同步之前,需要將主從兩臺伺服器的資料保持一致,如果主伺服器上的資料在從資料庫伺服器上不存在,那麼從庫的兩個執行緒會宕掉

4 基本構建思路
–確保資料相同:從庫必須要有主庫上的資料
–配置主伺服器:啟用binlog日誌,授權使用者,檢視當前正使用的Binlog日誌(從庫IO執行緒取SQL命令的地方)
–配置從伺服器:設定server_id(標識自己的身份),指定主庫資訊
–測試配置: 客戶端連線主庫寫入資料,在從庫上也能查詢到

5 配置主資料庫伺服器(mysql51)
(1)修改資料庫配置檔案

[[email protected] ~]# vim /etc/my.cnf
[mysqld]
log_bin=master51
server_id=51
binlog_format=mixed
validate_password_policy=0
validate_password_length=6
另外還可以指定當主、從網路中斷時的重試超時時間(slave-net-timeout=60 )等
[[email protected] ~]# systemctl restart mysqld  重啟服務
mysql> show master status;   檢視主庫資訊
指定當主、從網路中斷時的重試超時時間(slave-net-timeout=60 )等等

(2)使用者授權(從伺服器以此使用者名稱和密碼指定主庫資訊)

新建一個備份使用者,授予複製許可權,//同步授權只能授予全域性許可權,指定所有庫,所有表
mysql> grant replication slave on *.* to [email protected]"192.168.4.52" identified by "123456"    
 新建一個客戶端登入使用者,授予查詢插入許可權
mysql> grant insert,select on *.* to [email protected]"192.168.4.%"  identified  by "123456"     

(3)檢視binlog日誌檔案

[[email protected] ~]# ls /var/lib/mysql/master*
/var/lib/mysql/master51.000001  /var/lib/mysql/master51.index

[[email protected] ~]# mysqlbinlog /var/lib/mysql/master51.000001 | grep -i grant

6 配置從資料庫(mysql52)
(1) 關閉從庫binlog日誌(/etc/my.cnf 檔案)
配置檔案關閉binlog日誌,開啟server_id=52即可,隨後重啟mysqld服務
此時再檢視主庫或從庫資訊均為空

mysql> show slave status;
Empty set (0.00 sec)
mysql> show master status;
Empty set (0.00 sec)

(2)查詢主庫授權資訊(mysql51)

mysql> select user,host from mysql.user;
+-----------+---------------+
| user      | host          |
+-----------+---------------+
| repluer   | 192.168.4.52   |
| yaya100 | 192.168.4.%   |
| admin     | localhost     |
| mysql.sys | localhost     |
| root      | localhost     |
+-----------+---------------+

(3)檢視主庫狀態資訊

mysql> show master status;
+-----------------+----------+--------------+------------------+-------------------+
| File            | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-----------------+----------+--------------+------------------+-------------------+
| master51.000001 |      452 |              |                  |                   |
+-----------------+----------+--------------+------------------+---``

(4)在從庫上指定主庫資訊
注意:任何選項指定不正確,都會導致IO和SQL執行緒不工作

mysql> change master to  master_host="192.168.4.51",master_user="repluser",
    -> master_password="123456",
    -> master_log_file="master51.000001",
    -> master_log_pos=452;  
    (指定從當前主庫日誌檔案的當前偏移量來進行資料同步,當然也可以指定其他的日誌檔案和偏移量來進行同步,只要檔案和偏移量存在即可)

(5)檢查Slave伺服器的同步狀態

mysql> start slave;
mysql> show slave status\G;
Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.4.51          主庫ip
                  Master_User: repluser                   被授權的使用者名稱
                  Master_Port: 3306
                Connect_Retry: 60                           60s後重新連線
              Master_Log_File: master51.000001         主binlog日誌檔案
          Read_Master_Log_Pos: 452                          主binlog日誌檔案偏移量
               Relay_Log_File: mysql52-relay-bin.000002       從庫中繼日誌檔案
                Relay_Log_Pos: 319                                   從庫中繼日誌檔案偏移量   
        Relay_Master_Log_File: master51.000001      
             Slave_IO_Running: Yes                        從庫執行緒IO開啟狀態
            Slave_SQL_Running: Yes                      從庫執行緒SQL開啟狀態
            ………………

注意 :如果IO和SQL執行緒沒有執行,那麼Last_IO_Error 和 Last_SQL_Error
這兩個欄位有值,會標明原因。如果出錯,執行stop slave 停止從伺服器,修改好後再執行start slave.
注意: master資訊會自動儲存在/mysql/master.info 檔案中
以後要更改master資訊時需要先stop slave.
注意:停止stop slave後,可以更改主庫資訊的單條選項資訊,再開啟start slave

什麼時候sql執行緒會宕掉?sql執行緒在執行中繼日誌檔案中的sql命令時,如果從庫裡沒有主庫裡操作的庫或者表,sql執行緒會立刻宕掉,Last_SQL_Error:欄位會記錄出錯原因,如果出錯,先關閉slave,把需要的庫或者表建好,在開啟slave即可。

7 客戶端mysql50客戶機測試:
(1)登入192.168.4.51的資料庫

[[email protected] ]# mysql -h 192.168.4.51 -uwebadmin1 -p123456

(2)查詢許可權

mysql> show grants;
+--------------------------------------------------+
| Grants for [email protected]%                             |
+--------------------------------------------------+
| GRANT USAGE ON *.* TO 'webadmin1'@'192.168.4.%'              |
| GRANT SELECT, INSERT ON `db1`.* TO 'webadmin1'@'%' |
+--------------------------------------------------+

(3)插入資料

mysql> insert into db1.a values (1);
Query OK, 1 row affected (0.08 sec)
mysql> insert into db1.a values (2);
Query OK, 1 row affected (0.09 sec)
mysql> insert into db1.a values (3);
Query OK, 1 row affected (0.07 sec)

(4)主資料庫51伺服器檢視資料是否插入

mysql> select * from db1.a;
+------+
| id   |
+------+
|    1 |
|    2 |
|    3 |
+------+

(5)從資料庫52伺服器檢視資料是否從51同步

mysql> select * from db1.a
    -> ;
+------+
| id   |
+------+
|    1 |
|    2 |
|    3 |
+------+
至此,驗證完成mysql資料庫主從同步

(6)將從伺服器資料庫還原至正常的資料庫
–刪除4個從伺服器配置檔案,再重啟即可
master.info
reloy_log.info
主機名-relay-bin.xxxxxx
主機名-relay-bin.index
–重啟Mysqld服務即可,進入資料庫驗證: show slave status;

三 配置主從從同步結構
具體要求如下:
配置主機192.168.4.51為主資料庫伺服器
配置主機192.168.4.52為51主機的從庫伺服器
配置主機192.168.4.53為52主機的從庫伺服器
客戶端連線主資料庫伺服器51主機建立的資料,連線52和53主機時,也可以訪問到庫、表、記錄。
在上例中,mysql51和mysql52的主從同步已搭建完成,本例在之前的服務基礎上繼續搭建,完成 “主—>(從主)—>從” 的同步結構
注意:此時,mysql52主機作為mysql51主機的從伺服器的同時,它還是mysql53主機的主伺服器。
1 配置mysql52為mysql53的主master伺服器(為了在啟用binlog日誌及同步之前保持主、從庫的一致性,主從同步未配置之前,要保證從庫上要有主庫上的資料)
1.1配置檔案開啟binlog日誌

[[email protected] mysql]# vim /etc/my.cnf
[mysqld]
log_slave_updates
log_bin=master52
#replicate-do-db=db1
server_id=52
binlog_format="mixed"
log_slave_updates                          //允許級聯複製
[[email protected] mysql]# systemctl restart mysqld

1.2 mysql52對從庫授權

新建一個備份使用者,授予複製許可權
mysql> grant replication slave on *.* to [email protected]"192.168.4.53" identified by "123456"    
 新建一個客戶端登入使用者,授予查詢插入許可權
mysql> grant insert,select on *.* to [email protected]"192.168.4.%"  identified  by "123456"  

1.3 確保/var/lib/mysql下面最少有兩個檔案

[[email protected] mysql]# ls /var/lib/mysql/master52.*`
/var/lib/mysql/master52.000001  /var/lib/mysql/master52.000004  /var/lib/mysql/master52.index
/var/lib/mysql/master52.000002  /var/lib/mysql/master52.000005
/var/lib/mysql/master52.000003  /var/lib/mysql/master52.000006

1.4 檢視正在使用的日誌資訊

mysql> show master status;
+-----------------+----------+--------------+------------------+-------------------+
| File            | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-----------------+----------+--------------+------------------+-------------------+
| master52.000006 |     1352 |              |                  |                   |
+-----------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.4.51
                  Master_User: repluser
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master51.000008
          Read_Master_Log_Pos: 1352
               Relay_Log_File: mysql52-relay-bin.000016
                Relay_Log_Pos: 1517
        Relay_Master_Log_File: master51.000008
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes

2 配置從伺服器mysql53 :192.168.4.53
2.1 修改配置檔案

[[email protected] ~]# vim /etc/my.cnf
[mysqld]
server_id=53         //指定id時確保不與其他主機的id重複
replicate-ignore-db=db1            //不同步資料庫db1的資料
validate_password_policy=0
validate_password_length=6
character_set_server=utf8
[[email protected] ~]# systemctl restart mysqld

2.2 以本機資料庫root身份進入資料庫為本機指定主伺服器

mysql> change master to master_host="192.168.4.52",master_user="kenji",master_password="123456",master_log_file="master52.000006",master_log_pos=1352;

2.3 啟動slave程序,並檢視slave狀態

mysql> start slave;
mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.4.52
                  Master_User: repluser
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master52.000006
          Read_Master_Log_Pos: 1352
               Relay_Log_File: mysql53-relay-bin.000024
                Relay_Log_Pos: 1563
        Relay_Master_Log_File: master52.000006
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: db1             //不記錄db1庫的資料

3 客戶端驗證配置
3.1 檢視伺服器資料此刻是否一樣

資料庫192.168.4.51上檢視
mysql> use db2;
Database changed
mysql> show tables;
+---------------+
| Tables_in_db2 |
+---------------+
| game          |
+---------------+
mysql> desc game;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
mysql> select * from game;
Empty set (0.00 sec)
資料庫伺服器52 和53依次檢視,資料一致

3.2 利用51資料庫的授權登入使用者在客戶端50上登入192.168.4.51的資料庫,插入資料

[[email protected] aa]# mysql -h192.168.4.51 -uyaya100 -p123456
mysql> use db2;
Database changed
mysql> show tables;
+---------------+
| Tables_in_db2 |
+---------------+
| game          |
+---------------+
mysql> desc game;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
mysql> select * from game;
Empty set (0.00 sec)
mysql> insert into game values (111);
Query OK, 1 row affected (0.06 sec)

mysql> insert into game values (111);
Query OK, 1 row affected (0.07 sec)

3.3 分別進入三臺伺服器的資料庫內檢視資料是否同步更新

進入192.168.4.51檢視
mysql> select * from game;
+------+
| id   |
+------+
|  111 |
|  111 |
+------+
2 rows in set (0.00 sec)
進入192.168.4.52檢視
mysql> select * from game;
+------+
| id   |
+------+
|  111 |
|  111 |
+------+
2 rows in set (0.00 sec)
進入192.168.4.53檢視
mysql> select * from game;
+------+
| id   |
+------+
|  111 |
|  111 |
+------+
2 rows in set (0.00 sec)
至此三臺資料庫伺服器完成了主從同步

3.4 在伺服器192.168.4.53的配置檔案裡寫入了不同步更新db1的資料的欄位
replicate-ignore-db=db1 ,現在來驗證一下

1  客戶端連線51資料庫,在db1表內插入資料
mysql> use db1;
mysql> show tables;
+---------------+
| Tables_in_db1 |
+---------------+
| a             |
+---------------+
mysql> select * from a;
+------+
| id   |
+------+
|    2 |
+------+
插入資料
mysql> insert into a values(3);
Query OK, 1 row affected (0.09 sec)

mysql> insert into a values(4);
Query OK, 1 row affected (0.08 sec)

2 檢視51資料庫內寫入的內容
mysql> select * from a;
+------+
| id   |
+------+
|    2 |
|    3 |
|    4 |
+------+
3 rows in set (0.00 sec)

3 檢視52上資料是否同步更新
mysql> select * from a;
+------+
| id   |
+------+
|    2 |
|    3 |
|    4 |
+------+
  //更新成功

4 檢視53的是否有更新,原理上是不更新資料
mysql> select * from a;
+------+
| id   |
+------+
|    2 |
+------+
1 row in set (0.00 sec)  //保持和初始狀態一樣