MySQL在5.6版本以前複製一直是基於二進制日誌的複製,到了MySQL5.6時開始支持基於事務(GTIDs)的複製,而且開始支持多線程複製;但MySQL5.6版本的多線程只能基於多庫。這就牽扯到了一個應用場景,就是從基於日誌的複製在線變動到基於事務的複製,在MySQL5.6版本時這一動做只能重啓主服務器才能夠作到。可是到了MySQL 5.7版本時已經能夠支持在線變動複製類型了,也就是在線從基於二進制日誌的複製變動爲基於事務的複製。固然MySQL5.7在複製方面的改進不止這一點,還作到了基於表的多線程複製,以及多源複製。這篇文章只針對在線把基於日誌的複製變動爲基於事務的複製,其餘方面的改進,如多線程複製和多源複製能夠看其餘文章。mysql
詳情可看:MySQL 5.7多方式安裝sql
首先從MySQL官方網站下載YUM源,地址:http://dev.mysql.com/doc/mysql-yum-repo-quick-guide/en/bash
這裏我選擇MySQL 5.7的源進行安裝MySQL 5.7,手動添加一個YUM源。服務器
$ cat /etc/yum.repos.d/mysql.repo
# Enable to use MySQL 5.7 [mysql57-community] name=MySQL 5.7 Community Server baseurl=http://repo.mysql.com/yum/mysql-5.7-community/el/6/$basearch/ enabled=1 gpgcheck=0 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql
檢查一下mysql repo微信
$ yum repolist enabled | grep mysql mysql57-community MySQL 5.7 Community Server 146
安裝啓動配置MySQLsession
$ yum install mysql-community-server-5.7.16 mysql-community-devel-5.7.16 mysql-community-client-5.7.16 mysql-community-common-5.7.16
MySQL 5.7以及安裝完成了,爲了簡便,我這裏使用多實例的方式進行測試在線變動複製類型,開兩個端口3306和3307。多線程
首先建立一些標準目錄。socket
$ mkdir /data/mysql/3306/{conf,data,log,tmp} -p $ mkdir /data/mysql/3306/log/{binlog,relaylog,slowlog} -p $ mkdir /data/mysql/3307/{conf,data,log,tmp} -p $ mkdir /data/mysql/3307/log/{binlog,relaylog,slowlog} -p $ chown mysql.mysql -R /data/mysql
下面開始進行初始化操做。tcp
$ mysqld --initialize-insecure --user=mysql --datadir=/data/mysql/3306/data $ mysqld --initialize-insecure --user=mysql --datadir=/data/mysql/3307/data
看一下初始化後的目錄文件,以下:ide
$ ll /data/mysql/3306/data/ total 122908 -rw-r----- 1 mysql mysql 56 Nov 21 10:43 auto.cnf -rw-r----- 1 mysql mysql 260 Nov 21 11:58 ib_buffer_pool -rw-r----- 1 mysql mysql 50331648 Nov 21 11:58 ib_logfile0 -rw-r----- 1 mysql mysql 50331648 Nov 21 10:43 ib_logfile1 -rw-r----- 1 mysql mysql 12582912 Nov 21 11:58 ibdata1 -rw-r----- 1 mysql mysql 12582912 Nov 21 11:58 ibtmp1 drwxr-x--- 2 mysql mysql 4096 Nov 21 10:43 mysql drwxr-x--- 2 mysql mysql 4096 Nov 21 10:43 performance_schema drwxr-x--- 2 mysql mysql 12288 Nov 21 10:43 sys
1)給每一個實例提供配置文件
給3306(master)實例提供一份配置文,以下:
$ cat /data/mysql/3306/conf/my.cnf [mysqld] ############################basic settings################# port=3306 bind-address=0.0.0.0 datadir=/data/mysql/3306/data socket=/data/mysql/3306/mysql.sock pid-file=/data/mysql/3306/mysql.pid user=mysql server-id = 10 character_set_server = utf8mb4 skip_name_resolve = 1 max_allowed_packet = 16777216 max_connections = 800 max_connect_errors = 1000 tmpdir = /data/mysql/3306/tmp tmp_table_size = 67108864 explicit_defaults_for_timestamp = 1 join_buffer_size = 134217728 interactive_timeout = 1800 wait_timeout = 1800 read_buffer_size = 16777216 read_rnd_buffer_size = 33554432 sort_buffer_size = 33554432 key_buffer_size = 256M thread_cache_size = 8 transaction_isolation = READ-COMMITTED ###########################log settings##################### log-bin = /data/mysql/3306/log/binlog/mysql-bin log_bin_index = /data/mysql/3306/log/binlog/mysql-bin.index expire_logs_days = 30 binlog_format = ROW log_error = /data/mysql/3306/log/error.log slow_query_log = 1 long_query_time = 2 log_slow_admin_statements = 1 log_slow_slave_statements = 1 slow_query_log_file = /data/mysql/3306/log/slowlog/slow.log min_examined_row_limit = 100 binlog-rows-query-log_events = 1 ##########################innodb settings################### innodb_buffer_pool_size = 512m innodb_sort_buffer_size = 27108864 innodb_buffer_pool_load_at_startup = 1 innodb_buffer_pool_dump_at_shutdown = 1 innodb_lock_wait_timeout = 5 innodb_flush_method = O_DIRECT innodb_file_format = Barracuda innodb_file_format_max = Barracuda innodb_thread_concurrency = 24 innodb_flush_neighbors = 1 innodb_purge_threads = 4 innodb_large_prefix = 1 innodb_print_all_deadlocks = 1 innodb_strict_mode = 1 innodb_file_per_table = ON innodb_flush_log_at_trx_commit=2 ##########################start gtid########################### #gtid-mode = on #enforce-gtid-consistency = true #master-info-repository = table #relay-log-info-repository = table #log-slave-updates = true #binlog-checksum = CRC32 #master-verify-checksum = 1 #slave-sql-verify-checksum = 1 #slave_allow_batching = 1
給3307(slave)實例提供一份配置文件,以下:
$ cat /data/mysql/3307/conf/my.cnf [mysqld] ############################basic settings################# port=3307 bind-address=0.0.0.0 datadir=/data/mysql/3307/data socket=/data/mysql/3307/mysql.sock pid-file=/data/mysql/3307/mysql.pid user=mysql server-id = 20 character_set_server = utf8mb4 skip_name_resolve = 1 max_allowed_packet = 16777216 max_connections = 800 max_connect_errors = 1000 tmpdir = /data/mysql/3307/tmp tmp_table_size = 67108864 explicit_defaults_for_timestamp = 1 join_buffer_size = 134217728 interactive_timeout = 1800 wait_timeout = 1800 read_buffer_size = 16777216 read_rnd_buffer_size = 33554432 sort_buffer_size = 33554432 key_buffer_size = 256M thread_cache_size = 8 transaction_isolation = READ-COMMITTED ###########################log settings##################### #log-bin = /data/mysql/3307/log/binlog/mysql-bin #log_bin_index = /data/mysql/3307/log/binlog/mysql-bin.index #expire_logs_days = 30 #binlog_format = ROW log_error = /data/mysql/3307/log/error.log #slow_query_log = 1 #long_query_time = 2 #log_slow_admin_statements = 1 #log_slow_slave_statements = 1 #slow_query_log_file = /data/mysql/3307/log/slowlog/slow.log #min_examined_row_limit = 100 ##########################innodb settings################### innodb_buffer_pool_size = 512m innodb_sort_buffer_size = 27108864 innodb_buffer_pool_load_at_startup = 1 innodb_buffer_pool_dump_at_shutdown = 1 innodb_lock_wait_timeout = 5 innodb_flush_method = O_DIRECT innodb_file_format = Barracuda innodb_file_format_max = Barracuda innodb_thread_concurrency = 24 innodb_flush_neighbors = 1 innodb_purge_threads = 4 innodb_large_prefix = 1 innodb_print_all_deadlocks = 1 innodb_strict_mode = 1 innodb_file_per_table = ON innodb_flush_log_at_trx_commit=2 #########################replication######################### relay-log = /data/mysql/3307/log/relaylog/relay-log skip-slave-start = true #####################start gtid############################## #gtid-mode = on #enforce-gtid-consistency = true #slave-parallel-workers = 1 #binlog-checksum=CRC32 #master-verify-checksum = 1 #slave-sql-verify-checksum = 1 #slave_allow_batching = 1 #relay_log_purge = 1 #relay_log_recovery = 1 #master-info-repository = table #relay-log-info-repository = table #report-port = 3308 #report-host = 10.0.60.143
PS:配置文件很清楚,若是想使用GTID,開啓對應的參數便可。
從新賦予一下權限
$ chown mysql.mysql -R /data/mysql
1)啓動兩個實例
$ nohup mysqld --defaults-file=/data/mysql/3306/conf/my.cnf & $ nohup mysqld --defaults-file=/data/mysql/3307/conf/my.cnf &
$ netstat -nplt | grep mysqld tcp 0 0 10.0.60.143:3306 0.0.0.0:* LISTEN 39854/mysqld tcp 0 0 10.0.60.143:3307 0.0.0.0:* LISTEN 40256/mysqld
PS:進入MySQL須要使用-S指定各自的mysql.sock文件。
2)Master(3306)建立具備複製權限的用戶
mysql> grant replication slave on *.* to 'mysql_slave'@'%' identified by '123456'; mysql> flush privileges;
3)Slave(3307)鏈接至主庫(3306)
mysql> reset slave all; mysql> change master to master_host='10.0.60.143',master_user='mysql_slave',master_password='123456',MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=1;
master_host #主服務器地址; master_port #主服務器端口(不要指定雙引號); master_log_file #指定從主服務器哪一個二進制日誌文件開始複製; master_log_pos #指定從主服務器哪一個二進制日誌文件的位置開始複製(不須要雙引號); master_user #鏈接到主服務器的用戶; master_password #鏈接到主服務器的用戶密碼;
4)啓動Slave
mysql> start slave;
mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 10.0.60.143 Master_User: mysql_slave Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000001 Read_Master_Log_Pos: 591 Relay_Log_File: relay-log.000003 Relay_Log_Pos: 804 Relay_Master_Log_File: mysql-bin.000001 Slave_IO_Running: Yes Slave_SQL_Running: Yes ........
OK,如今基於日誌的主從複製已經完成了。
接下來,就是在線切換複製類型了。首先肯定主從的gtid_mode都是off狀態。
mysql> show variables like '%gtid_mode%'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | gtid_mode | OFF | +---------------+-------+ 1 row in set (0.00 sec)
1)Master(3306)操做
爲了更加模擬線上環境,咱們寫一個腳本,一直往主庫插入數據,同時從庫也一直再同步主庫的數據。
mysql> CREATE DATABASE `test`; mysql> USE test; mysql> CREATE TABLE `tt` ( `id` int(11) NOT NULL AUTO_INCREMENT, `count` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
而後建立一個腳本一直往test.tt表中插入數據。
$ cat test.sh #!/bin/bash for i in `seq 1 1000000`;do mysql -S /data/mysql/3306/mysql.sock -e "insert into test.tt(count) values($i);" done
如今就能夠開始丟在後臺執行。
接下來,在主庫(3306)和把enforce_gtid_consistency變成warn。
mysql> set global enforce_gtid_consistency=warn; Query OK, 0 rows affected (0.00 sec)
ENFORCE_GTID_CONSISTENCY這個參數主要有以下設置,主要用於不讓違反GTID設置的操做執行,若是執行會報錯如:Statement violates GTID consistency: CREATE TABLE … SELECT.
ENFORCE_GTID_CONSISTENCY=WARN是肯定事務都支持gtid,不會在err log中出現警告以下:
2016-11-21T22:35:24.322055Z 55 [Warning] Statement violates GTID consistency: CREATE TABLE ... SELECT.
而後看一下error log。
$ tail /data/mysql/3306/log/error.log 2016-11-21T05:24:10.064466Z 5 [Note] Start binlog_dump to master_thread_id(5) slave_server(20), pos(, 4) 2016-11-21T08:12:52.480029Z 8 [Note] Changed ENFORCE_GTID_CONSISTENCY from OFF to WARN.
若是沒有錯誤信息,才能夠進行下一步開啓enforce_gtid_consistency操做。
mysql> set global enforce_gtid_consistency=on; Query OK, 0 rows affected (0.00 sec)
而後進行開啓gtid_mode操做,gtid_mode這個參數有四個值,必定要按照以下順序進行gtid_mode的開啓操做(關閉是相反的)。
mysql> set global gtid_mode=off; Query OK, 0 rows affected (0.03 sec) mysql> set global gtid_mode=off_permissive; Query OK, 0 rows affected (0.04 sec)
當主庫設置完gtid_mode=off_permissive以後,這個時候也要在從庫執行到這一步,爲的是slave能夠應用匿名事務和GTID事務。切記,主從設置是交叉的,若是從庫沒有設置到gtid_mode=off_permissive,而主庫下一步操做gtid_mode = on_permissive後,從庫的IO線程就會斷掉。帶來的後果就是若是主從有延遲,那麼主從數據頗有可能會不一致。而且這種狀況下,也不能算是一個完整的在線切換複製類型,只能算是半在線。
正確的作法就是以下操做,在從庫也先進行以下設置:
2)Slave(3307)操做
mysql> set global enforce_gtid_consistency=warn; Query OK, 0 rows affected (0.00 sec) mysql> set global enforce_gtid_consistency=on; Query OK, 0 rows affected (0.00 sec) mysql> set global gtid_mode=off; Query OK, 0 rows affected (0.03 sec) mysql> set global gtid_mode=off_permissive; Query OK, 0 rows affected (0.04 sec)
當從庫也設置完gtid_mode=off_permissive以後,就能夠在主庫進行開啓GTID了。
3)Master(3306)操做
mysql> set global gtid_mode=on_permissive; Query OK, 0 rows affected (0.03 sec)
開啓以後,因爲腳本在一直寫數據,你能夠立馬看見二進制狀態的變化。
mysql> show master status; +------------------+----------+--------------+------------------+--------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+--------------------------------------------+ | mysql-bin.000004 | 28388 | | | 3b4cc092-af94-11e6-8a81-001dd8b71e2b:1-117 | +------------------+----------+--------------+------------------+--------------------------------------------+ 1 row in set (0.00 sec)
查看肯定已經沒有匿名事務了,這個值ONGOING_ANONYMOUS_TRANSACTION_COUNT有一次爲0便可。
mysql> show status like 'ONGOING_ANONYMOUS_TRANSACTION_COUNT'; +-------------------------------------+-------+ | Variable_name | Value | +-------------------------------------+-------+ | Ongoing_anonymous_transaction_count | 0 | +-------------------------------------+-------+ 1 row in set (0.00 sec)
mysql> SELECT @@GLOBAL.GTID_OWNED;
肯定此時的Retrieved_Gtid_Set/Executed_Gtid_Set正常增加(甚至你能夠在slave上使用:SELECT MASTER_POS_WAIT(file, position);
來強制等待slave端直到指定位置,這個位置就是你肯定的使用GTID事務的位置)。
能夠看到當主庫設置完gtid_mode = on_permissive後,二進制狀態變成了GTID模式。這個時候就能夠在從庫開啓gtid_mode = on_permissive了。
4)Slave(3307)操做
mysql> set global gtid_mode=on_permissive; Query OK, 0 rows affected (0.03 sec)
接下來show slave status就能夠看到從庫已經切換爲GTID複製了。
mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 10.0.60.143 Master_User: mysql_slave Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000004 Read_Master_Log_Pos: 207164 Relay_Log_File: relay-bin.000005 Relay_Log_Pos: 207377 Relay_Master_Log_File: mysql-bin.000004 Slave_IO_Running: Yes Slave_SQL_Running: Yes 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: 207164 Relay_Log_Space: 207636 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: 103306 Master_UUID: 6edc34c8-d23d-11e6-b440-fa163e2a6390 Master_Info_File: /data/mysql/3307/data/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: 6edc34c8-d23d-11e6-b440-fa163e2a6390:1-135 Executed_Gtid_Set: 6edc34c8-d23d-11e6-b440-fa163e2a6390:1-135 Auto_Position: 0 Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version: 1 row in set (0.01 sec)
5)Master&Slave都操做
下面就能夠在主庫和從庫上分別設置gtid_mod = on了。
mysql> set global gtid_mode=on; Query OK, 0 rows affected (0.04 sec)
查看gtid的開啓。
mysql> show variables like '%gtid%'; +----------------------------------+-----------+ | Variable_name | Value | +----------------------------------+-----------+ | binlog_gtid_simple_recovery | ON | | enforce_gtid_consistency | ON | | gtid_executed_compression_period | 1000 | | gtid_mode | ON | | gtid_next | AUTOMATIC | | gtid_owned | | | gtid_purged | | | session_track_gtids | OFF | +----------------------------------+-----------+ 8 rows in set (0.01 sec)
至此,主從複製在線切換GTID已經完成了。最後別忘了把gtid相關信息寫進配置文件中,否則重啓MySQL後就又失效了。具體能夠看:MySQL基於GTIDs的複製實現。
change master
mysql> stop slave; mysql> CHANGE MASTER TO MASTER_HOST='10.0.60.143',MASTER_PORT=3306,master_user='mysql_slave',MASTER_PASSWORD='123456',MASTER_AUTO_POSITION=1; mysql> start slave;
若是出現主從不一致的狀況,那麼可使用pt工具進行修復便可,詳情請看這篇文章:使用pt-table-checksum&pt-table-sync檢查和修復主從數據一致性。
1)Slave操做
mysql> stop slave;
而後記錄slave status
Exec_Master_Log_Pos: 7631438 Relay_Master_Log_File: bin_log.000016
從新執行CHANGE MASTER
mysql> CHANGE MASTER TO MASTER_AUTO_POSITION = 0,MASTER_LOG_FILE = 'bin_log.000016', MASTER_LOG_POS = 7631438;
從新開啓Slave。
mysql> start slave;
2)Master&slave操做
生成的是GTID事物,slave能夠應用匿名和GTID事物。
mysql> SET @@GLOBAL.GTID_MODE = ON_PERMISSIVE;
3)Master&Slave操做
生成的是匿名事物,slave能夠應用匿名和GTID事物。
mysql> SET @@GLOBAL.GTID_MODE = OFF_PERMISSIVE;
4)Master&Slave操做
mysql> SELECT @@GLOBAL.GTID_OWNED;
等到主庫和備庫此顯示爲空,而且Retrieved_Gtid_Set/Executed_Gtid_Set再也不變更。(甚至你能夠在slave上使用:SELECT MASTER_POS_WAIT(file, position);來強制等待slave端直到指定位置,這個位置就是你肯定的沒有使用GTID事務的位置)
完成這一步實際上GTID事物已經沒有生成和應用了。
5)Master&Slave操做
mysql> SET @@GLOBAL.GTID_MODE = OFF;
最後別忘記修改配置文件my.cnf,使其永久生效。
爲了方便你們交流,本人開通了微信公衆號(關注看更多精彩)和QQ羣,QQ羣1(291519319)和QQ羣2(659336691)。喜歡技術的一塊兒來交流吧