MySQL 5.7新特性:在线开启和关闭基于GTID的复制

1、前言

MySQL在5.6版本以前复制一直是基于二进制日志的复制,到了MySQL5.6时开始支持基于事务(GTIDs)的复制,而且开始支持多线程复制;但MySQL5.6版本的多线程只能基于多库。这就牵扯到了一个应用场景,就是从基于日志的复制在线变动到基于事务的复制,在MySQL5.6版本时这一动做只能重启主服务器才能够作到。可是到了MySQL 5.7版本时已经能够支持在线变动复制类型了,也就是在线从基于二进制日志的复制变动为基于事务的复制。固然MySQL5.7在复制方面的改进不止这一点,还作到了基于表的多线程复制,以及多源复制。这篇文章只针对在线把基于日志的复制变动为基于事务的复制,其余方面的改进,如多线程复制和多源复制能够看其余文章。mysql

2、安装MySQL 5.7.16

详情可看: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

 

3、启动多实例

 

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


4、基于日志作主从复制配置

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,如今基于日志的主从复制已经完成了。

5、在线开启基于GTID的复制

接下来,就是在线切换复制类型了。首先肯定主从的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.

  • OFF:全部操做容许。
  • ON:不容许违反gtid的操做,而且报错。
  • WARN:在MySQL 5.7.6添加,全部操做容许,可是违反GTID的操做会报出警告。

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的开启操做(关闭是相反的)。 

  • off:生成的是匿名事务,slave也只能应用匿名事务。
  • off_permissive:生成的是匿名事务,slave能够应用匿名事务和GTID事务。
  • on_permissive:生成的是GTID事务,slave能够应用匿名事务和GTID事务。(这一步操做完成后,主节点二进制日志就会变成gtid模式)
  • on:生成的是GTID事务,slave也只能应用GTID事务。
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检查和修复主从数据一致性


6、在线关闭基于GTID的复制

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)。喜欢技术的一块儿来交流吧

相关文章
相关标签/搜索