環境介紹:
centos 7.5
MySQL 5.7.29
Mysql主服務器:192.168.2.128
Mysql從服務器:192.168.2.129
Mysql主從同步原理:
當master服務器上的數據發生改變時(增、刪、改),則將其改變寫入二進制binlog日志中;slave服務器會在一定時間間隔內對master二進制日志進行探測其是否發生改變,如果發生改變,則開啟一個I/O 線程請求master二進制事件,同時主節點為每個I/O線程啟動一個dump線程,用于向其發送二進制事件,并保存至從庫本地的中繼日志中,從庫(從節點)將啟動SQL線程從中繼日志中讀取二進制日志,在本地重放,使得其數據和主節點的保持一致,最后IO線程和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兩節點間時間需同步。
如上圖所示:
Mysql復制過程的第一部分就是master記錄二進制日志。在每個事務更新數據完成之前,master在二日志記錄這些改變。MySQL將事務串行的寫入二進制日志,即使事務中的語句都是交叉執行的。在事件寫入二進制日志完成后,master通知存儲引擎提交事務。
第二部分就是slave將master的binary log拷貝到它自己的中繼日志。首先,slave開始一個工作線程——I/O線程。I/O線程在master上打開一個普通的連接,然后開始binlog dump process。Binlog dump process從master的二進制日志中讀取事件,如果已經跟上master,它會睡眠并等待master產生新的事件。I/O線程將這些事件寫入中繼日志。
SQL slave thread(SQL從線程)處理該過程的最后一步。SQL線程從中繼日志讀取事件,并重放其中的事件而更新slave的數據,使其與master中的數據一致。只要該線程與I/O線程保持一致,中繼日志通常會位于OS的緩存中,所以中繼日志的開銷很小。
此外,在master中也有一個工作線程:和其它MySQL的連接一樣,slave在master中打開一個連接也會使得master開始一個線程。復制過程有一個很重要的限制——復制在slave上是串行化的,也就是說master上的并行更新操作不能在slave上并行操作。
構建主從同步:
主從同步介紹
-實現數據自動同步的服務結構
-主服務器: 接受客戶端訪問連接
-從服務器: 自動同步主服務器數據
- Master(主庫)
-啟用binlog日志 - Slave(從庫)
`-Slave_IO線程:復制master主機binlog日志文件里的SQL命令到本機的relay-log文件里。`
`-Slave_SQL線程:執行本機relay-log文件里的SQL語句,實現與Master數據一致。`
構建思路
- 配置主庫
-啟用binlog日志、授權用戶、查看 binlog日志信息 - 配置從服務器
-`確保與主服務器數據一致。`
-`設置server_id、指定主庫信息、啟動slave程序`
配置Mysql主服務器
1.修改主配置文件
-啟用binlog日志并重啟服務
[root@localhost ~]# vim /etc/my.cnf
[mysqld]
log-bin=db128 //啟用binlog日志,指定日志名以db128開頭
server_id=128 //指定服務器ID號
...
[root@localhost ~]# systemctl restart mysqld
[root@localhost ~]# ls /var/lib/mysql/db128.*
/var/lib/mysql/db128.000001 /var/lib/mysql/db128.index
2.授權用戶
-用戶權限為 replication slave
-用戶名自定義
-客戶端地址允許從庫連接
-密碼自定義
注意:
replication slave權限代表: 允許slave主機通過此用戶連接master以便建立主從復制關系。
[root@localhost ~]# mysql -uroot -p123qqq...A
...
Server version: 5.7.29-log MySQL Community Server (GPL)
...
mysql> grant replication slave on *.* to mysqluser@"%" identified by "123qqq...A";
//為主數據庫授權用戶mysqluser,權限為replication、slave,允許客戶端地址為所有主機,允許訪問所有庫所有表*.*,授權用戶密碼為123qqq...A
3.查看日志信息
[root@localhost ~]# mysql -uroot -p123qqq...A
...
Server version: 5.7.29-log MySQL Community Server (GPL)
...
mysql> show master status; //查看binlog記錄日志信息的偏移量position
+--------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+--------------+----------+--------------+------------------+-------------------+
| db128.000001 | 704787 | | | |
+--------------+----------+--------------+------------------+-------------------+
配置從服務器
1.修改主配置文件
-指定server_id,不允許與主庫的server_id值相同
[root@test2 ~]# vim /etc/my.cnf
[mysqld]
server_id=129
...
[root@test2 ~]# systemctl restart mysqld
2.確保與主庫數據一致
-在主庫上備份數據,備份文件拷貝給從庫
-在從庫上使用備份文件恢復數據
-從庫查看備份數據對應的binlog日志信息
主數據庫操作:
--master-data: 在備份文件中添加這次備份的數據對應的binlog日志名以及備份后數據的節點編號(偏移量),以便從庫同步數據時,可以知道從哪個節點開始同步數據,保證主庫與從庫數據完全一致.
[root@localhost ~]# mysqldump -uroot -p123qqq...A --master-data test > /root/test.sql
//在主庫上備份數據庫test,并且記錄當前備份數據對應的binlog日志信息,備份文件名為test.sql
[root@localhost ~]# ls test.sql
test.sql
[root@localhost ~]# scp test.sql root@192.168.2.129:/root/
從數據庫操作:
[root@test2 ~]# ls test.sql
test.sql
[root@test2 ~]# mysql -uroot -p123qqq...A;
mysql> create database test; //從庫上必須有需要恢復的庫,因為要恢復test庫,所以先創建空庫test
[root@test2 ~]# mysql -uroot -p123qqq...A test < /root/test.sql
[root@test2 ~]# mysql -uroot -p123qqq...A;
mysql> use test;
+----------------+
| Tables_in_test |
+----------------+
| lss |
| money |
+----------------+
3.指定主庫信息
命令格式:
change master to
master_host="主庫IP地址",
master_user="用戶名",
master_password="密碼",
master_log_file="binlog日志文件名",
master_log_poss=偏移量;
主數據庫查看binlog日志名及偏移量:
[root@localhost ~]# mysql -uroot -p123qqq...A
...
mysql> show master status; //主庫查看binlog記錄日志信息的日志名及偏移量
+--------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+--------------+----------+--------------+------------------+-------------------+
| db128.000001 | 2261338 | | | |
+--------------+----------+--------------+------------------+-------------------+
從庫指定主庫信息:
[root@test2 ~]# mysql -uroot -p123qqq...A;
...
mysql> show slave status;
Empty set (0.00 sec)
mysql> change master to
-> master_host="192.168.2.128", //指定主庫IP地址
-> master_user="mysqluser", //主庫授權用戶
-> master_password="123qqq...A", //授權用戶的密碼
-> master_log_file="db128.000001", //主庫binlog日志文件名
-> master_log_pos=2261338; //備份文件的日志偏移量
Query OK, 0 rows affected, 2 warnings (0.02 sec)
mysql> start slave; //啟動slave進程
注意:
1.指定的master信息會自動保存到/var/lib/mysql/master.info文件中。
2.若要更改指定的主庫信息,需先執行stop slave,修改完成后在執行start slave。
4.查看slave狀態
-確定IO線程、SQL線程都是Yes狀態
報錯解決:
UUID問題報錯解決:Fatal error: The slave I/O thread stops because master and slave have equal MySQL server UUIDs…
mysql> show slave statusG;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.2.128 //主庫IP地址
Master_User: mysqluser
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: db128.000002
Read_Master_Log_Pos: 452757169
Relay_Log_File: test2-relay-bin.000002
Relay_Log_Pos: 3205508
Relay_Master_Log_File: db128.000002
Slave_IO_Running: Yes //IO線程已運行
Slave_SQL_Running: Yes //SQL線程已運行
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: 452757169
Relay_Log_Space: 3205715
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: 128
Master_UUID: e46c9961-5780-11ea-bf2f-000c128a8b6b
Master_Info_File: /var/lib/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
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
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)
5.相關文件
-存放在從庫數據庫目錄下
master.info //主庫信息
relay-log.info //中繼日志信息
主機名-relay-bin.xxxx //中繼日志
主機名-relay-bin.index //索引文件
查看從庫服務器數據庫目錄下配置從庫時生成的4種文件
[root@test2 ~]# ls /var/lib/mysql
...
test2-relay-bin.index master.info relay-log.info
test2-relay-bin.000001 test2-relay-bin.000002
驗證主從同步效果
主數據庫操作:
在主數據庫服務器中創建一個新表test并賦值,再去從服務器上查看數據是否同步。
[root@localhost ~]# mysql -uroot -p123qqq...A
mysql: [Warning] Using a password on the comm
...
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| db1 |
| mysql |
| performance_schema |
| sys |
| test |
| zabbix |
+--------------------+
mysql> use test;
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| money |
+----------------+
mysql> create table test(name varchar(25),city varchar(30),age int);
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| money |
| test |
+----------------+
mysql> insert into test.test values("mysql","china",11);
mysql> select * from test.test;
+-------+-------+------+
| name | city | age |
+-------+-------+------+
| mysql | china | 11 |
+-------+-------+------+
從數據庫操作:
查看主服務器上新增的數據是否已經同步到從服務器上。
[root@test2 ~]# mysql -uroot -p123qqq...A
...
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| db1 |
| mysql |
| performance_schema |
| sys |
| test |
| zabbix |
+--------------------+
mysql> use test;
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| money |
| test |
+----------------+
mysql> select * from test.test;
+-------+-------+------+
| name | city | age |
+-------+-------+------+
| mysql | china | 11 |
+-------+-------+------+
可以看到我們在主數據庫服務器上新增的數據已經成功同步到從服務器上了。
延伸:
MySql主從同步的延遲問題(如何產生):
主庫針對寫操作,順序寫binlog日志,從庫單線程去主庫順序讀”寫操作的binlog”,從庫取到binlog在本地原樣執行(隨機寫),來保證主從數據邏輯上一致。mysql的主從復制都是單線程的操作,主庫對所有增、刪、改產生binlog日志,binlog是順序寫,所以效率很高,slave的Slave_IO_Running線程到主庫取日志,效率比較高,下一步,問題來了,slave的Slave_SQL_Running線程將主庫的增、刪、改操作在slave實施。增、刪、改的IO操作是隨機的,不是順序的,成本高很多,還可能與slave上的其他查詢操作產生lock爭用,由于Slave_SQL_Running也是單線程的,所以一個增、刪、改操作卡住了,需要執行10分鐘,那么所有之后的增、刪、改操作會等待這個增、刪、改操作執行完才會繼續執行,這就導致了延時。(主庫是多進程,從庫單進程(回放relaylog),所以在高并發時,會出現延遲。)
MySQL主從同步延遲怎么解決?
把主從同步配置為異步模式 ,保證至少有1臺數據庫服務器與主服務器數據一致。