一、MySQL主從複製原理
Mysql之間數據複製的基礎時二進制日誌文件。一臺Mysql數據庫一旦啓用二進制日誌後,其作爲master,他的數據庫中所有操作都會以“事件”的方式記錄在二進制日誌中,其他數據庫作爲slave通過一個I/O線程與主服務器保持通信,並監控master的二進制日誌文件變化,則會把變化複製到自己的中繼日誌中,然後slave的一個SQL線程會把相關的“事件”執行到自己的數據庫中,因此實現從數據庫和主數據庫一致性,也就實現了主從複製。
在此過程中,主庫有個Binlog Dump線程,從庫生成了兩個線程,一個I/O線程,一個SQL線程
主從複製存在的問題
- 主庫宕機後,數據可能丟失
- 從庫只有一個SQL Thread,主庫寫壓力大,複製很可能出現延時(可能需要應用數據量較大,可能和從庫本身的一些操作有鎖和資源的衝突;主庫可以併發寫,但是SQL線程不可以;主要原因)
解決方法 - 半同步複製—解決數據丟失問題
- 並行複製—解決從庫複製延時問題
二、配置主從複製
主從庫的配置流程
- 主服務器:
(1)開啓二進制日誌
(2)配置唯一的server-id
(3)獲得master二進制日誌文件名及位置
(4)創建一個用於slave和master通信的用戶帳號 - 從服務器:
(1)配置唯一的server-id
(2)使用master分配的用戶帳號讀取master二進制日誌
(3)啓用slave服務
1、在server2(master)和server3(slave)解壓安裝數據庫
[root@server2 ~]# tar xf mysql-5.7.17-1.el6.x86_64.rpm-bundle.tar
[root@server2 ~]# yum install -y mysql-community-client-5.7.17-1.el6.x86_64.rpm mysql-community-common-5.7.17-1.el6.x86_64.rpm mysql-community-libs-5.7.17-1.el6.x86_64.rpm mysql-community-libs-compat-5.7.17-1.el6.x86_64.rpm mysql-community-server-5.7.17-1.el6.x86_64.rpm
2、查看數據庫的初始密碼
[root@server2 ~]# /etc/init.d/mysqld start
[root@server2 ~]# cat /var/log/mysqld.log |grep password
3、數據庫的安全初始化並且修改密碼(密碼必須是八位以上,並且包含數字大小寫,特殊字符)
[root@server2 ~]# mysql_secure_installation
4、設置server-id
[root@server2 ~]# vim /etc/my.cnf
29 server-id=1 #服務器id(兩臺主機的id號不能一樣)
30 log-bin=mysql-bin #開啓二進制日誌
[root@server2 ~]# /etc/init.d/mysqld start
5、Mysql主庫的配置
[root@server2 ~]# mysql -p
Enter password:
mysql> grant replication slave on *.* to 'server3'@'172.25.66.3' identified by 'Jyyc0106@'; #賦予server3'@'172.25.66.3主機登錄數據庫,並給slave複製的權力
Query OK, 0 rows affected, 1 warning (0.40 sec)
mysql> flush privileges; #刷新
Query OK, 0 rows affected (0.06 sec)
6、Mysql從庫的配置
[root@server3 ~]# mysql -p
Enter password:
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>change master to master_host='172.25.66.2',master_user='server3',master_password='Jyyc0106@',master_log_file='mysql-bin.000003',master_log_pos=1198; #這裏_host和_user是主庫中賦予的主機名還有ip,_log_file和_log_pos爲在主庫用show master status查看出來的
Query OK, 0 rows affected, 2 warnings (0.19 sec)
mysql> start slave; #開啓從庫
Query OK, 0 rows affected (0.03 sec)
遇到的問題
- 當出現Slave_IO_Running: Connecting的提示時,說明主庫和從庫沒有連接上,有以下三點原因:
- 網絡問題:檢查網絡連接是否能夠連接上
- 密碼或POS號錯誤:查看pos號和主庫的號是否對應
- 防火牆的問題:查看主庫防火牆的策略,數據庫是否拒絕外來連接,然後做相應的改動
我在實驗中也遇到了這個錯誤,原因是因爲在slave上
mysql>change master to master_host=‘172.25.66.2’,master_user=‘westos_jy’,master_password=‘Jyyc0106@’,master_log_file=‘mysql-bin.000003’,master_log_pos=1198;
我這裏的master_user=‘westos_jy’,寫錯了,在master上我寫的是‘server3’所以出現了這個問題
注意:這裏的master_host是你主庫的ip,master_user這個用戶是你在主庫中賦予哪個用戶權力,這裏就寫誰,master_password密碼就寫你主庫的mysql登錄密碼
- 語法錯誤
當輸完mysql> show slave status\G;,出現了
ERROR:
No query specified這個錯誤。
解決方法:mysql> show slave status\G這個後面不能分號,因爲\G在功能上就等於分號,這樣一加就相當於是兩個分號 - 在查看mysql日誌時出現的這個錯誤Error reading relay log event for channel ‘’: slave SQL thread was killed
解決辦法:執行mysql> stop slave;這個命令,然後在change,之後在start slave
7、主從複製測試:
主庫:
mysql> create database user; #新建user數據庫
Query OK, 1 row affected (0.11 sec)
mysql> use user #進入user數據庫
mysql> create table userinfo( #在user數據庫裏面建userinfo表
-> username varchar(10) not null, #username不超過10個字符,且不能爲空
-> password varchar(10) not null); #密碼不超過10個字符,且不能爲空
Query OK, 0 rows affected (0.59 sec)
mysql> desc userinfo; #顯示userinfo表的數據結構
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| username | varchar(10) | NO | | NULL | |
| password | varchar(10) | NO | | NULL | |
+----------+-------------+------+-----+---------+-------+
2 rows in set (0.01 sec)
mysql> insert into userinfo values ('jy','123'); #在userinfo表中插入值
Query OK, 1 row affected (0.08 sec)
mysql> select * from userinfo; #查詢user庫下userinfo表中的所有內容
+----------+----------+
| username | password |
+----------+----------+
| jy | 123 |
+----------+----------+
1 row in set (0.00 sec)
從庫
mysql> show databases;
mysql> use user;
mysql> show tables;
mysql> select * from userinfo;
二、利用Gtid實現主從複製
1、Gtid的工作原理
master更新數據時,會在事務前產生GTID,一同記錄到binlog日誌中。slave端的i/o 線程將變更的binlog,寫入到本地的relay log中。sql線程從relay log中獲取GTID,然後對比slave端的binlog是否有記錄。如果有記錄,說明該GTID的事務已經執行,slave會忽略。如果沒有記錄,slave就會從relay log中執行該GTID的事務,並記錄到binlog。在解析過程中會判斷是否有主鍵,如果沒有就用二級索引,如果沒有就用全部掃描。
2、基於Gtid複製的優點
一個事務對應一個唯一ID,一個GTID在一個服務器上只會執行一次。.GTID是用來代替傳統複製的方法,GTID複製與普通複製模式的最大不同就是不需要指定二進制文件名和位置。減少手工干預和降低服務故障時間,當主機掛了之後通過軟件從衆多的備機中提升一臺備機爲主機
3、修改配置文件(/etc/my.cng)並且重新啓動mysql(主庫和從庫都要配置)
[root@server3 ~]# vim /etc/my.cnf
31 gtid_mode=ON
32 enforce-gtid-consistency=1 #這裏也可以寫成true
[root@server3 ~]# /etc/init.d/mysqld restart
4、配置從數據庫
[root@server3 ~]# mysql -p
mysql>change master to master_host='172.25.66.2',master_user='server3',master_password='Jyyc0106@',master_auto_position=1;
5、測試
主庫
[root@server2 ~]# mysql -p
mysql> select * from userinfo;
+----------+----------+
| username | password |
+----------+----------+
| jy | 123 |
+----------+----------+
1 row in set (0.00 sec)
mysql> insert into userinfo values ('yh','234');
Query OK, 1 row affected (0.04 sec)
mysql> select * from userinfo;
+----------+----------+
| username | password |
+----------+----------+
| jy | 123 |
| yh | 234 |
+----------+----------+
2 rows in set (0.00 sec)
從庫
三、基於LOGICAL_CLOCK(組提交)的並行複製
1、mysql並行複製原理
在MySQL5.7引入基於Logical clock的並行複製方案前,MySQL使用基於Schema的並行複製,也就是基於庫的,使不同db下的DML操作可以在備庫併發回放。但是如果業務在Master端高併發寫入一個庫(或是一個表),那麼slave端就會出現較大的延遲。基於schema的並行複製,slave作爲只讀實例提供讀取功能時可以保證同schema下事務的因果序,而無法保證不同schema間的。例如當業務關注事務執行先後順序時,在Master端db1寫入T1,收到T1返回後,纔在db2執行T2,但在slave端可能先讀取到T2的數據,纔讀到T1的數據。
Mysql5.7的LOGICAL CLOCK並行複製,解除了schema的限制,使得主庫對一個db或一張表併發執行的事務到slave端也可以並行執行。
2、在從庫配置
[root@server3 ~]# vim /etc/my.cnf
slave-parallel-type=LOGICAL_CLOCK
slave-parallel-workers=16 #worker線程個數
master_info_repository=TABLE
relay_log_info_repository=TABLE
relay_log_recovery=ON
測試
現在就可以看出有更多的線程等着主線程的調用,這就大大解決了從庫複製延時問題
四、半同步複製
1、半同步複製的原理
他是介於異步複製和全同步複製之間的,主庫在執行完客戶端提交的事務後不是立刻返回給客戶端,而是等待至少一個從庫接收到並寫到relay log中才返回給客戶端。相對於異步複製,半同步複製提高了數據的安全性,同時它也造成了一定程度的延遲,這個延遲最少是一個TCP/IP往返的時間。所以,半同步複製最好在低延時的網絡中使用。在5.7版本後新增ack線程(單線程工作)用於接受應答,分攤dump線程的壓力
2、半同步複製的特點
- 5.5集成到mysql,以插件的形式存在,需要單獨安裝
- 確保事務提交後binlog至少傳輸到一個從庫
- 不保證從庫應用完這個事務的binlog
- 性能有一定的降低,響應時間會更長
- 網絡異常或從庫宕機,卡主主庫,直到超時或從庫恢復
3、配置半同步
主庫(server2)的配置
mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so'; #安裝master的插件
mysql> set global rpl_semi_sync_master_enabled=on;
mysql> set global rpl_semi_sync_master_enabled=1; #打開半同步複製master端的開關
mysql> show variables like 'rpl_semi_sync%' #查看各個參數
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | ON |
| rpl_semi_sync_master_timeout | 10000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_for_slave_count | 1 |
| rpl_semi_sync_master_wait_no_slave | ON |
| rpl_semi_sync_master_wait_point | AFTER_SYNC |
+-------------------------------------------+------------+
mysql> show status like 'rpl_semi_sync%' #查看各個參數的狀態
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 0 |
| Rpl_semi_sync_master_net_avg_wait_time | 0 |
| Rpl_semi_sync_master_net_wait_time | 0 |
| Rpl_semi_sync_master_net_waits | 0 |
| Rpl_semi_sync_master_no_times | 0 |
| Rpl_semi_sync_master_no_tx | 0 | #同步失敗的次數
| Rpl_semi_sync_master_status | ON |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 0 |
| Rpl_semi_sync_master_tx_wait_time | 0 |
| Rpl_semi_sync_master_tx_waits | 0 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 0 | #同步成功的次數
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
從庫(server3)的配置
mysql> install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
Query OK, 0 rows affected (0.47 sec)
mysql> set global rpl_semi_sync_slave_enabled=1;
stop slave io_thread;
start slave io_thread;
mysql> show variables like 'rpl_semi_sync%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_slave_enabled | ON |
+-------------------------------------------+------------+
mysql> show variables like 'rpl_semi_sync%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_slave_enabled | ON |
| rpl_semi_sync_slave_trace_level | 32 |
+-------------------------------------------+------------+
4、測試
在server2上插入數據,在server3上看是否同步,並查看成功的次數
在server2上
mysql> use user;
Database changed
mysql> show tables;
+----------------+
| Tables_in_user |
+----------------+
| userinfo |
+----------------+
1 row in set (0.00 sec)
mysql> select * from userinfo;
+----------+----------+
| username | password |
+----------+----------+
| jy | 123 |
+----------+----------+
1 row in set (0.00 sec)
mysql> insert into userinfo values('user1','222');
Query OK, 1 row affected (0.16 sec)
mysql> insert into userinfo values('user2','222');
Query OK, 1 row affected (0.51 sec)
mysql> insert into userinfo values('user3','222');
Query OK, 1 row affected (0.13 sec)
在主庫上測試看成功的次數
當關閉server3上的io線程,在server2上插入數據,查看成功次數
mysql> stop slave io_thread;