一 MySQL 的三種復制方式
1.1 簡介
asynchronous 異步復制
fully synchronous 全同步復制
Semisynchronous 半同步復制
從MySQL5.5 開始,MySQL 以插件的形式支持半同步復制。
1.2 異步復制(Asynchronous replication)
MySQL 默認的復制是異步的,主庫在執行完客戶端提交的事務后會立即將結果返給給客戶端,並不關心從庫是否已經接收並處理,這樣就會有一個問題,主如果crash 掉了,此時主上已經提交的事務可能並沒有傳到從上,如果此時,將從提升為主,可能導致新主上的數據不完整。
原理:在異步復制中,master 寫數據到binlog 且sync,slave request binlog 后寫入relay‐log 並flush disk
優點:復制的性能最好
缺點:master 掛掉后,slave 可能會丟失數據
1.3 全同步復制(Fully synchronous replication)
指當主庫執行完一個事務,所有的從庫都執行了該事務才返回給客戶端。因為需要等待所有從庫執行完該事務才能返回,所以全同步復制的性能必然會收到嚴重的影響。
優點:數據不會丟失
缺點:會阻塞master session,性能太差,非常依賴網絡
1.4 半同步復制(Semisynchronous replication)
介於異步復制和全同步復制之間,主庫在執行完客戶端提交的事務后不是立刻返回給客戶端,而是等待至少一個從庫接收到並寫到relay log 中才返回給客戶端。相對於異步復制,半同步復制提高了數據的安全性,同時它也造成了一定程度的延遲,這個延遲最少是一個TCP/IP 往返的時間。所以,半同步復制最好在低延時的網絡中使用。
優點:會有數據丟失風險(低)
缺點:會阻塞master session,性能差,非常依賴網絡,由於master 是在三段提交的最后commit 階段完成后才等待,所以master 的其他session 是可以看到這個提交事務的,所以這時候master 上的數據和slave 不一致,master crash 后,slave 數據丟失。
1.5 增強版的半同步復制(lossless replication)
原理: 在半同步復制中,master 寫數據到binlog 且sync,然后一直等待ACK. 當至少一個slave request bilog 后寫入到relay‐log 並flush disk,就返回ack(不需要回放完日志)
優點:數據零丟失(前提是讓其一直是lossless replication),
性能好
缺點:會阻塞master session,非常依賴網絡由於master 是在三段提交的第二階段sync binlog 完成后才等待, 所以master 的其他session 是看不見這個提交事務的,所以這時候master 上的數據和slave 一致,master crash 后,slave 沒有丟失數據
二 實驗
2.1 查看plugins
mysql> show plugins;
+----------------------------+----------+--------------------+---------+---------+
| Name | Status | Type | Library | License |
+----------------------------+----------+--------------------+---------+---------+
| binlog | ACTIVE | STORAGE ENGINE | NULL | GPL |
| mysql_native_password | ACTIVE | AUTHENTICATION | NULL | GPL |
| sha256_password | ACTIVE | AUTHENTICATION | NULL | GPL |
| CSV | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MEMORY | ACTIVE | STORAGE ENGINE | NULL | GPL |
| InnoDB | ACTIVE | STORAGE ENGINE | NULL | GPL |
| INNODB_TRX | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_LOCKS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_LOCK_WAITS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_CMP | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_CMP_RESET | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_CMPMEM | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_CMPMEM_RESET | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_CMP_PER_INDEX | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_CMP_PER_INDEX_RESET | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_BUFFER_PAGE | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_BUFFER_PAGE_LRU | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_BUFFER_POOL_STATS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_TEMP_TABLE_INFO | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_METRICS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_FT_DEFAULT_STOPWORD | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_FT_DELETED | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_FT_BEING_DELETED | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_FT_CONFIG | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_FT_INDEX_CACHE | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_FT_INDEX_TABLE | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_TABLES | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_TABLESTATS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_INDEXES | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_COLUMNS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_FIELDS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_FOREIGN | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_FOREIGN_COLS | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_TABLESPACES | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_DATAFILES | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| INNODB_SYS_VIRTUAL | ACTIVE | INFORMATION SCHEMA | NULL | GPL |
| MyISAM | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MRG_MYISAM | ACTIVE | STORAGE ENGINE | NULL | GPL |
| PERFORMANCE_SCHEMA | ACTIVE | STORAGE ENGINE | NULL | GPL |
| ARCHIVE | ACTIVE | STORAGE ENGINE | NULL | GPL |
| BLACKHOLE | ACTIVE | STORAGE ENGINE | NULL | GPL |
| FEDERATED | DISABLED | STORAGE ENGINE | NULL | GPL |
| partition | ACTIVE | STORAGE ENGINE | NULL | GPL |
| ngram | ACTIVE | FTPARSER | NULL | GPL |
+----------------------------+----------+--------------------+---------+---------+
或者查詢INFORMATION_SCHEMA.PLUGINS 表
[root@master1 ~]# cd /usr/lib64/mysql/plugin/
[root@master1 plugin]# ll
-rwxr-xr-x. 1 root root 103728 Apr 13 10:36 adt_null.so -rwxr-xr-x. 1 root root 356976 Apr 13 10:36 authentication_ldap_sasl_client.so -rwxr-xr-x. 1 root root 43552 Apr 13 10:36 auth_socket.so -rwxr-xr-x. 1 root root 940312 Apr 13 10:36 connection_control.so drwxr-xr-x. 2 root root 4096 Jul 3 11:31 debug -rwxr-xr-x. 1 root root 21640312 Apr 13 10:36 group_replication.so -rwxr-xr-x. 1 root root 483520 Apr 13 10:36 ha_example.so -rwxr-xr-x. 1 root root 968432 Apr 13 10:36 innodb_engine.so -rwxr-xr-x. 1 root root 957088 Apr 13 10:36 keyring_file.so -rwxr-xr-x. 1 root root 460064 Apr 13 10:36 keyring_udf.so -rwxr-xr-x. 1 root root 1184680 Apr 13 10:36 libmemcached.so -rwxr-xr-x. 1 root root 8973984 Apr 13 10:36 libpluginmecab.so -rwxr-xr-x. 1 root root 21424 Apr 13 10:36 locking_service.so -rwxr-xr-x. 1 root root 53928 Apr 13 10:36 mypluglib.so -rwxr-xr-x. 1 root root 41088 Apr 13 10:36 mysql_no_login.so -rwxr-xr-x. 1 root root 22243648 Apr 13 10:37 mysqlx.so -rwxr-xr-x. 1 root root 49504 Apr 13 10:36 rewrite_example.so -rwxr-xr-x. 1 root root 590936 Apr 13 10:36 rewriter.so -rwxr-xr-x. 1 root root 933904 Apr 13 10:36 semisync_master.so #主庫安裝 -rwxr-xr-x. 1 root root 159928 Apr 13 10:36 semisync_slave.so #備庫安裝 -rwxr-xr-x. 1 root root 209520 Apr 13 10:36 validate_password.so -rwxr-xr-x. 1 root root 506320 Apr 13 10:36 version_token.so
2.2 主庫配置
查看是否支持動態加載的MySQL 服務器
mysql> show variables like '%dynamic%'; +----------------------+-------+ | Variable_name | Value | +----------------------+-------+ | have_dynamic_loading | YES | +----------------------+-------+ 1 row in set (0.00 sec) mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so'; #安裝庫 Query OK, 0 rows affected (0.00 sec) mysql> show variables like '%rpl_semi%'; +-------------------------------------------+------------+ | Variable_name | Value | +-------------------------------------------+------------+ | rpl_semi_sync_master_enabled | OFF | #修改為on狀態 | rpl_semi_sync_master_timeout | 10000 | #修改為1s | 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 | +-------------------------------------------+------------+
修改my.cnf
[root@master1 ~]# vim /etc/my.cnf
rpl_semi_sync_master_enabled = 1 rpl_semi_sync_master_timeout = 1000
[root@master1 ~]# systemctl restart mysqld
mysql> show variables like '%rpl_semi%'; +-------------------------------------------+------------+ | Variable_name | Value | +-------------------------------------------+------------+ | rpl_semi_sync_master_enabled | ON | | rpl_semi_sync_master_timeout | 1000 | | 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 '%semi%'; +--------------------------------------------+-------+ | 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 | +--------------------------------------------+-------+
rpl_semi_sync_master_timeout
一個以毫秒為單位的值,用於控制主服務器等待來自從服務器的確認提交並恢復到異步復制的時間,超過這個值就是超時。 默認值是10000(10 秒)。超時之后,就從半同步復制,返回到異步復制。
Rpl_semi_sync_master_yes_tx:從庫成功確認的提交數量。
Rpl_semi_sync_master_no_tx:從庫未成功確認的提交數量。
2.3 備份服務器配置
mysql> show variables like '%dynamic%'; +----------------------+-------+ | Variable_name | Value | +----------------------+-------+ | have_dynamic_loading | YES | +----------------------+-------+ mysql> install plugin rpl_semi_sync_slave soname 'semisync_slave.so'; mysql> show variables like '%rpl_semi%'; +---------------------------------+-------+ | Variable_name | Value | +---------------------------------+-------+ | rpl_semi_sync_slave_enabled | OFF | #打開為on | rpl_semi_sync_slave_trace_level | 32 | +---------------------------------+-------+
修改my.cnf
[root@slave ~]# vim /etc/my.cnf
rpl_semi_sync_slave_enabled = 1
[root@slave ~]# systemctl restart mysqld
mysql> show variables like '%rpl_semi%'; +---------------------------------+-------+ | Variable_name | Value | +---------------------------------+-------+ | rpl_semi_sync_slave_enabled | ON | | rpl_semi_sync_slave_trace_level | 32 | +---------------------------------+-------+ mysql> show status like '%semi%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | ON | +----------------------------+-------+
2.4 主庫驗證
mysql> show status like '%semi%'; +--------------------------------------------+-------+ | Variable_name | Value | +--------------------------------------------+-------+ | Rpl_semi_sync_master_clients | 1 | | Rpl_semi_sync_master_net_avg_wait_time | 0 | | Rpl_semi_sync_master_net_wait_time | 0 | | Rpl_semi_sync_master_net_waits | 2 | | Rpl_semi_sync_master_no_times | 1 | | Rpl_semi_sync_master_no_tx | 1 | | 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 | +--------------------------------------------+-------+ mysql> insert into test values (2); mysql> insert into test values (2); mysql> insert into test values (2); mysql> show status like '%semi%'; +--------------------------------------------+-------+ | Variable_name | Value | +--------------------------------------------+-------+ | Rpl_semi_sync_master_clients | 1 | | Rpl_semi_sync_master_net_avg_wait_time | 0 | | Rpl_semi_sync_master_net_wait_time | 0 | | Rpl_semi_sync_master_net_waits | 5 | | Rpl_semi_sync_master_no_times | 1 | | Rpl_semi_sync_master_no_tx | 1 | | Rpl_semi_sync_master_status | ON | | Rpl_semi_sync_master_timefunc_failures | 0 | | Rpl_semi_sync_master_tx_avg_wait_time | 1013 | | Rpl_semi_sync_master_tx_wait_time | 3041 | | Rpl_semi_sync_master_tx_waits | 3 | | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | | Rpl_semi_sync_master_wait_sessions | 0 | | Rpl_semi_sync_master_yes_tx | 3 | #增加數據.這個之會增加 +--------------------------------------------+-------+
從端數據已經同步
mysql> select * from master1.test; +------+ | id | +------+ | 1 | | 2 | | 2 | | 2 | +------+
2.5 測試AFTER_SYNC 和AFTER_COMMIT
主庫設置超時時間為1000 秒,備庫停掉復制,模擬timeout
mysql> set global rpl_semi_sync_master_timeout=1000000; mysql> stop slave; mysql> insert into test values (11); #會一直卡住 mysql> select * from master1.test; +------+ | id | +------+ | 1 | | 2 | | 2 | | 2 | +------+
重啟主庫數據庫,模擬主庫宕機,從看數據記錄
[root@master1 ~]# systemctl start mysqld [root@master1 ~]# mysql -uroot -p123456 mysql> select * from master1.test; +------+ | id | +------+ | 1 | | 2 | | 2 | | 2 | | 11 | #主庫有記錄 +------+
備庫開啟slave
mysql> start slave; Query OK, 0 rows affected (0.00 sec) mysql> select * from master1.test; +------+ | id | +------+ | 1 | | 2 | | 2 | | 2 | | 11 | #數據已經同步,沒有丟失 +------+
無損的半同步復制是在write binlog 之后。需要得到備庫的確認。所以這時候主庫宕機,不會發生丟數據。當主庫啟動后,插入的數據重新可見。
將rpl_semi_sync_master_wait_point 設置為AFTER_COMMIT,
再次測試:
主庫設置超時時間為1000 秒,備庫停掉復制,模擬timeout
mysql> set global rpl_semi_sync_master_wait_point=AFTER_COMMIT; mysql> set global rpl_semi_sync_master_timeout=1000000; mysql> show variables like '%semi%'; +-------------------------------------------+--------------+ | Variable_name | Value | +-------------------------------------------+--------------+ | rpl_semi_sync_master_enabled | ON | | rpl_semi_sync_master_timeout | 1000000 | | 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_COMMIT | +-------------------------------------------+--------------+
mysql> insert into master1.test values (12); #一直卡住
[root@master1 ~]# mysql -uroot -p123456 #另開一個窗口,發現已經有12這個數據 mysql> select * from master1.test; +------+ | id | +------+ | 1 | | 2 | | 2 | | 2 | | 11 | | 12 | +------+
這樣當從庫起來之后,數據已經提交,從庫就會缺少這個數據
再開一個窗口查詢這條數據,發現可以查詢到。這時候主庫宕機,會發生數據丟失。
主庫重新啟動,備庫啟動slave 會同步到備庫。