一、什么是GTID

   自MySQL 5.6引入的GTID(Global Transaction ID)使得其复制功能的配置、监控及管理变得更加易于实现,且更加健壮。官方文档在这篇文档里,我们可以知道全局事务 ID 的官方定义是:GTID = source_id:transaction_id

   gtid是一个 unique 唯一的表示符,他是由服务器的uuid 全局唯一标识,是由128位的随机符组成,mysql-5.6是依靠server-id和uuid 来标识复制架构中的每一个主机,因为是128位的随机字符串在全局都不会重复,server-id 和uuid以及每一个mysql事物的事物序号组成了唯一的gtid ,自从引进mysql-5.6之后每一个二进制日志当中在每一个事物的首部都会写上gtid 标记,因此gtid使得追踪和比较复制事物变得非常简单而且能够实现从崩溃中快速恢复。尤其是innodb 引擎要想实现高可用功能必须要借助于gtid来实现。

二、应用场景

   当使用mariadb实现高可用时,GTID是显得尤为重要,例如,在一个高可用环境中,一主多从的模式下,当主库宕机后(写延迟),集群资源管理器可以在节点列表中的多个从库中任选一个提升为主库,而不会影响到业务本身,而其它的从库将会以新提升起来为主库为当前集群中的主库,以后将从这个库上复制同步;虽然这样做完成了“瞬间”业务切换,但可能在主库未宕机之前,多个从库的复制同步落后于主库,这样一来,当主库宕机后,集群资源管理器正好切换到一个与自己事务提交可能不一致的从库(B)上,当有应用读取之前己在宕机的主库中提交的事务时,发现现在的主库没有,这样就会出现事务不一致,而GTID就能很好的解决这个问题。首先当一个从库被提升为一个主库时,那么之前指向原来主库的从库将全部指向新提升的主库,来进行之后的复制,而后,刚提升的主库B会整合所有从库中己完成的的事务,来添补自己缺少的部分,而B从那里知道,缺少的是那一个事务,有那么多的事务,这个事务是那一个呢,这就是通过GTID来标识的,因为GTID标识了来源服务器的标识与第多少个事务(Source_id:transaction_id),每一个改变是事件都会与GTID相关连起来记录于binlog日志中以供其它从库同步,从而做到全局唯一的标识。

   注: 在多级复制中GTID是不会改变的。 在GTID中如果主服务器中有多个数据库,要实现多线程复制是靠I/O线程复制到从库的中继日志中,在由多个SQL thread来进行应用于本地的。

三、多线程复制说明

   MySQL 5.6之前的版本,同步复制是单线程的,队列的,只能一个一个执行,在5.6里,可以做到多个库之间的多线程复制,例如数据库里,存放着用户表,商品表,价格表,订单表,那么将每个业务表单独放在一个库里,这时就可以做到多线程复制,但一个库里的表,多线程复制是无效的。

说明:

  •  每个数据库仅能使用一个线程,复制涉及到多个数据库时多线程复制才有意义; 

  • 同一个库的事务复制,就必须按先后顺序复制。 

  • 在复制模型中,同一个线程(I/O线程)可以对多个数据库提供服务。

四、实现过程

1、环境介绍

hostname IP 数据库 OS
Master master.mysql.com 192.168.1.8 MariaDB-10.0.21 CentOS 6.6
Slave slave.mysql.com 192.168.1.9 MariaDB-10.0.21 CentOS 6.6

2、Mariadb Master配置清单

⑴、编辑配置文件/etc/my.cnf

[root@master.mysql.com ~]# cat /etc/my.cnf[client]port        = 3306socket      = /tmp/mysql.sock[mysqld]port        = 3306socket      = /tmp/mysql.sockskip-external-lockingkey_buffer_size = 256Mmax_allowed_packet = 1Mtable_open_cache = 256sort_buffer_size = 1Mread_buffer_size = 1Mread_rnd_buffer_size = 4Mmyisam_sort_buffer_size = 64Mthread_cache_size = 8query_cache_size= 16Mthread_concurrency = 4datadir=/data/mydatalog-bin=/data/binlog/master-binbinlog_format=rowserver-id  = 100log-slave-update =Truemaster-info-repository=TABLErelay-log-info-repository=TABLEsync-master-info=1slave-parallel-threads=2master-verify-checksum=CRC32slave-sql-verify-checksum=1binlog-rows-query-log-events=1report-port = 3306report-host=master.mysql.com[mysqldump]quickmax_allowed_packet = 16M[mysql]no-auto-rehash[myisamchk]key_buffer_size = 128Msort_buffer_size = 128Mread_buffer = 2Mwrite_buffer = 2M[mysqlhotcopy]interactive-timeout

配置文件说明:

  • binlog-format :二进制日志的格式,有row、statement和mixed几种类型; 当设置隔离级别为READ-COMMITED必须设置二进制日志格式为ROW,现在MySQL官方认为STATEMENT这个已经不再适合继续使用;但mixed类型在默认的事务隔离级别下,可能会导致主从数据不一致; 

  • log-slave-updates、report-port和report-host :用于启动GTID及满足附属的其它需求; 

  • master-info-repository和relay-log-info-repository :启用此两项,可用于实现在崩溃时保证二进制及从服务器安全的功能; 

  • sync-master-info :启用之可确保无信息丢失; 

  • slave-paralles-threads :设定从服务器的SQL线程数;0表示关闭多线程复制功能; 

  • binlog-checksum、master-verify-checksum和slave-sql-verify-checksum :启用复制有关的所有校验功能; 

  • binlog-rows-query-log-events :启用之可用于在二进制日志记录事件相关的信息,可降低故障排除的复杂度; 

  • log-bin :启用二进制日志,这是保证复制功能的基本前提; 

  • server-id :同一个复制拓扑中的所有服务器的id号必须惟一 ;

  • log_slave_updates :记录从服务器的更新;

由于配置文件中我的二进制文件指定了路径,由于事先没有该目录故需要创建,之后就是启动mysql:

[root@master.mysql.com ~]# mkdir /data/binlog[root@master.mysql.com ~]# chown -R mysql.mysql /data/binlog[root@master.mysql.com ~]# service mysqld startStarting MySQL. SUCCESS!

⑵、授权复制节点及用户

MariaDB [(none)]> grant replication slave on *.* to 'repluser'@'192.168.1.9' identified by 'redhat';Query OK, 0 rows affected (0.12 sec)MariaDB [(none)]> flush privileges;Query OK, 0 rows affected (0.00 sec)

⑶、备份Master节点的数据,同步于slave(此操作并非必须的,如果Master上有大量数据需要同步到新的Slave上,Slave是新上线的此操作才有意义)

[root@slave.mysql.com ~]# mysqldump -uroot -h localhost -p --all-databases --lock-all-tables --flush-logs --master-data=2 > /tmp/all.sqlEnter password: [root@slave.mysql.com ~]# scp /tmp/all.sql 192.168.1.9:/tmp

⑷、查看一下Master GTID标识

MariaDB [(none)]> show variables like '%gtid%';+------------------------+---------+| Variable_name          | Value   |+------------------------+---------+| gtid_binlog_pos        | 0-100-2 || gtid_binlog_state      | 0-100-2 || gtid_current_pos       | 0-100-2 || gtid_domain_id         | 0       || gtid_ignore_duplicates | OFF     || gtid_seq_no            | 0       || gtid_slave_pos         |         || gtid_strict_mode       | OFF     || last_gtid              | 0-100-2 |+------------------------+---------+9 rows in set (0.06 sec)MariaDB [(none)]> show master status;+-------------------+----------+--------------+------------------+| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB |+-------------------+----------+--------------+------------------+| master-bin.000002 |      367 |              |                  |+-------------------+----------+--------------+------------------+1 row in set (0.00 sec)

3、Mariadb Slave配置清单

[root@slave.mysql.com ~]# cat /etc/my.cnf[client]port        = 3306socket      = /tmp/mysql.sock[mysqld]port        = 3306socket      = /tmp/mysql.sockskip_external_lockingkey_buffer_size = 256Mmax_allowed_packet = 1Mtable_open_cache = 256sort_buffer_size = 1Mread_buffer_size = 1Mread_rnd_buffer_size = 4Mmyisam_sort_buffer_size = 64Mthread_cache_size = 8query_cache_size= 16Mthread_concurrency = 4datadir=/data/mydatalog_bin=/data/binlog/slave-binrelay_log=relay-log-binserver_id=20binlog_format=ROWlog_slave_updates=truemaster_info_repository=TABLErelay_log_info_repository=TABLEsync_master_info=1slave_parallel_threads=2binlog_checksum=CRC32master_verify_checksum=1slave_sql_verify_checksum=1binlog_rows_query_log_events=1report_port=3306report_host=slave.mysql.com[mysqldump]quickmax_allowed_packet = 16M[mysql]no-auto-rehash[myisamchk]key_buffer_size = 128Msort_buffer_size = 128Mread_buffer = 2Mwrite_buffer = 2M[mysqlhotcopy]interactive-timeout
[root@slave.mysql.com ~]# mkdir /data/binlog[root@slave.mysql.com ~]# chown -R mysql.mysql /data/binlog[root@slave.mysql.com ~]# /etc/init.d/mysqld startStarting MySQL. SUCCESS!

⑵、应用完全备份

[root@slave.mysql.com ~]# mysql < /tmp/all.sql

⑶、连接Master

MariaDB [(none)]> change master to master_host='192.168.1.8',master_user='repluser',master_password='redhat',master_use_gtid=current_pos;Query OK, 0 rows affected (0.07 sec)MariaDB [(none)]> start slave ;Query OK, 0 rows affected (0.00 sec)MariaDB [(none)]> show slave status\G;*************************** 1. row ***************************               Slave_IO_State: Waiting for master to send event                  Master_Host: 192.168.1.8                  Master_User: repluser                  Master_Port: 3306                Connect_Retry: 60              Master_Log_File: master-bin.000004          Read_Master_Log_Pos: 452               Relay_Log_File: relay-log-bin.000002                Relay_Log_Pos: 654        Relay_Master_Log_File: master-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: 452              Relay_Log_Space: 949              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: 0Master_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: 100               Master_SSL_Crl:           Master_SSL_Crlpath:                   Using_Gtid: Current_Pos                  Gtid_IO_Pos: 0-100-51 row in set (0.00 sec)ERROR: No query specified

若从上启动好了,在主上进行查看是否能查到:

MariaDB [(none)]> show slave hosts;+-----------+-----------------+------+-----------+| Server_id | Host            | Port | Master_id |+-----------+-----------------+------+-----------+|        20 | slave.mysql.com | 3306 |       100 |+-----------+-----------------+------+-----------+1 row in set (0.00 sec)

主上创建数据:

MariaDB [(none)]> create database testdb;Query OK, 1 row affected (0.02 sec)MariaDB [(none)]> use testdbDatabase changedMariaDB [testdb]> create table tt(name char(20));Query OK, 0 rows affected (0.03 sec)MariaDB [testdb]> insert into tt values('bols'),('longls');Query OK, 2 rows affected (0.08 sec)Records: 2  Duplicates: 0  Warnings: 0MariaDB [testdb]> select * from tt;+--------+| name   |+--------+| bols   || longls |+--------+2 rows in set (0.00 sec)

从查看数据:

MariaDB [(none)]> select * from testdb.tt;+--------+| name   |+--------+| bols   || longls |+--------+2 rows in set (0.00 sec)

查看从的二进制日志:

[root@example.com ~]# mysqlbinlog /data/binlog/slave-bin.000003# at 1113#150903 21:50:29 server id 100  end_log_pos 1155 CRC32 0xd995d81b GTID 0-100-6/*!100001 SET @@session.gtid_seq_no=6*//*!*/;# at 1155#150903 21:50:29 server id 100  end_log_pos 1254 CRC32 0x13d1ba72 Querythread_id=4exec_time=0error_code=0use `testdb`/*!*/;SET TIMESTAMP=1441288229/*!*/;create table tt(name char(20))/*!*/;# at 1254#150903 21:50:56 server id 100  end_log_pos 1296 CRC32 0x3481b11b GTID 0-100-7/*!100001 SET @@session.gtid_seq_no=7*//*!*/;BEGIN/*!*/;# at 1296# at 1345#150903 21:50:56 server id 100  end_log_pos 1345 CRC32 0x585cade6 Table_map: `testdb`.`tt` mapped to number 70#150903 21:50:56 server id 100  end_log_pos 1392 CRC32 0x6e271cb0 Write_rows: table id 70 flags: STMT_END_F

在主上查看gtid号:

MariaDB [testdb]> show global variables like 'gtid_current_pos';+------------------+---------+| Variable_name    | Value   |+------------------+---------+| gtid_current_pos | 0-100-7 |+------------------+---------+1 row in set (0.00 sec)

五、基于SSL进行传输

1、将主服务器做成CA服务器:

[root@master.mysql.com ~]# cd /etc/pki/CA/[root@master.mysql.com CA]# (umask 077; openssl genrsa -out private/cakey.pem 2048)[root@master.mysql.com CA]# openssl req -x509 -new -key private/cakey.pem -out cacert.pem -days 3650[root@master.mysql.com CA]# touch index.txt serial[root@master.mysql.com CA]# echo 02 > serial

2、主服务器申请证书

[root@master.mysql.com CA]# cd /usr/local/mysql/[root@master.mysql.com mysql]# mkdir ssl[root@master.mysql.com mysql]# cd ssl/[root@master.mysql.com ssl]# (umask 077;openssl genrsa -out master.key 1024)[root@master.mysql.com ssl]# openssl req -new -key master.key -out master.csr[root@master.mysql.com ssl]# openssl ca -in master.csr -out master.crt -days 365[root@master.mysql.com ssl]# chown -R mysql.mysql ../ssl/

3、从服务器向主服务器申请证书

从服务器

[root@slave.mysql.com ~]# cd /usr/local/mysql/[root@slave.mysql.com mysql]# mkdir ssl[root@slave.mysql.com mysql]# cd ssl/[root@slave.mysql.com ssl]# (umask 077;openssl genrsa -out slave.key 1024)[root@slave.mysql.com ssl]# openssl req -new -key slave.key -out slave.csr -days 365[root@slave.mysql.com ssl]# scp slave.csr master:/tmp

主服务器

[root@master.mysql.com ssl]# cd /tmp/[root@master.mysql.com tmp]# openssl ca -in slave.csr -out slave.crt -days 3650[root@master.mysql.com tmp]# scp slave.crt slave:/usr/local/mysql/ssl/[root@master.mysql.com tmp]# scp /etc/pki/CA/cacert.pem slave:/usr/local/mysql/ssl/

从服务器

[root@slave.mysql.com ssl]# chown -R mysql.mysql ../ssl/

注意:在无论是在Master服务器上还是在Slave服务器上一定要修改ssl目录及其文件的属主属主为mysql。

4、在主服务器上开启ssl功能

[root@master.mysql.com ~]# service mysqld stopShutting down MySQL.. SUCCESS! [root@master.mysql.com ~]# vim /etc/my.cnfsslssl-ca=/etc/pki/CA/cacert.pemssl-cert=/usr/local/mysql/ssl/master.crtssl-key=/usr/local/mysql/ssl/master.key[root@master.mysql.com ~]# service mysqld startStarting MySQL. SUCCESS!

5、验证ssl是否开启成功

MariaDB [(none)]> show global variables like '%ssl%';+---------------+---------------------------------+| Variable_name | Value                           |+---------------+---------------------------------+| have_openssl  | YES                             || have_ssl      | YES                             || ssl_ca        | /etc/pki/CA/cacert.pem          || ssl_capath    |                                 || ssl_cert      | /usr/local/mysql/ssl/master.crt || ssl_cipher    |                                 || ssl_crl       |                                 || ssl_crlpath   |                                 || ssl_key       | /usr/local/mysql/ssl/master.key |+---------------+---------------------------------+9 rows in set (0.00 sec)MariaDB [(none)]> show master status;+-------------------+----------+--------------+------------------+| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB |+-------------------+----------+--------------+------------------+| master-bin.000008 |      658 |              |                  |+-------------------+----------+--------------+------------------+1 row in set (0.00 sec)

6、创建允许使用ssl连接的用户

MariaDB [(none)]> grant replication client,replication slave on *.* to rpssl@192.168.1.9 identified by 'redhat' require ssl;Query OK, 0 rows affected (0.00 sec)MariaDB [(none)]> flush privileges;Query OK, 0 rows affected (0.01 sec)

7、在从服务器上开启ssl功能

[root@slave.mysql.com ~]# vim /etc/my.cnfssl[root@slave.mysql.com ~]# /etc/init.d/mysqld restartShutting down MySQL.. SUCCESS! Starting MySQL. SUCCESS!

验证ssl是否开启成功:

MariaDB [(none)]> show global variables like '%ssl%';+---------------+-------+| Variable_name | Value |+---------------+-------+| have_openssl  | YES   || have_ssl      | YES   || ssl_ca        |       || ssl_capath    |       || ssl_cert      |       || ssl_cipher    |       || ssl_crl       |       || ssl_crlpath   |       || ssl_key       |       |+---------------+-------+9 rows in set (0.00 sec)

8、从服务器连接主服务器

MariaDB [(none)]> stop slave;Query OK, 0 rows affected (0.01 sec)MariaDB [(none)]> change master to master_host='192.168.1.8',master_user='rpssl',master_password='redhat',master_log_file='master-bin.000008',master_ssl=1,master_ssl_ca='/usr/local/mysql/ssl/cacert.pem',master_ssl_cert='/usr/local/mysql/ssl/slave.crt',master_ssl_key='/usr/local/mysql/ssl/slave.key';Query OK, 0 rows affected (0.01 sec)MariaDB [(none)]>  change master to master_host='192.168.1.8',master_user='repluser',master_password='redhat',master_use_gtid=current_pos;Query OK, 0 rows affected (0.02 sec)MariaDB [(none)]> start slave;Query OK, 0 rows affected (0.01 sec)MariaDB [(none)]> show slave status\G;*************************** 1. row ***************************               Slave_IO_State: Waiting for master to send event                  Master_Host: 192.168.1.8                  Master_User: repluser                  Master_Port: 3306                Connect_Retry: 60              Master_Log_File: master-bin.000008          Read_Master_Log_Pos: 658               Relay_Log_File: relay-log-bin.000002                Relay_Log_Pos: 654        Relay_Master_Log_File: master-bin.000008             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: 658              Relay_Log_Space: 949              Until_Condition: None               Until_Log_File:                Until_Log_Pos: 0           Master_SSL_Allowed: Yes           Master_SSL_CA_File: /usr/local/mysql/ssl/cacert.pem           Master_SSL_CA_Path:              Master_SSL_Cert: /usr/local/mysql/ssl/slave.crt            Master_SSL_Cipher:               Master_SSL_Key: /usr/local/mysql/ssl/slave.key        Seconds_Behind_Master: 0Master_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: 100               Master_SSL_Crl: /usr/local/mysql/ssl/cacert.pem           Master_SSL_Crlpath:                   Using_Gtid: Current_Pos                  Gtid_IO_Pos: 0-100-101 row in set (0.00 sec)

9、测试

主上插入数据:

MariaDB [testdb]> insert into tt values('xiaozels');Query OK, 1 row affected (0.01 sec)MariaDB [testdb]> insert into tt values('xiaozels1');Query OK, 1 row affected (0.00 sec)MariaDB [testdb]> insert into tt values('xiaozels2');Query OK, 1 row affected (0.00 sec)MariaDB [testdb]> insert into tt values('xiaozels3');Query OK, 1 row affected (0.01 sec)

从服务查是否同步:

MariaDB [(none)]> select * from testdb.tt;+-----------+| name      |+-----------+| bols      || longls    || xiaozels  || tianls    || xiaozels  || xiaozels1 || xiaozels2 || xiaozels3 |+-----------+8 rows in set (0.00 sec)

主上抓包查看是否能加密传输:

[root@master.mysql.com ~]# tcpdump -i eth0 -nn -XX ip dst host 192.168.1.8 and tcp dst port 3306tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes11:08:38.879929 IP 192.168.1.9.44082 > 192.168.1.8.3306: Flags [.], ack 1565043767, win 1181, options [nop,nop,TS val 2920065 ecr 2985061], length 00x0000:  000c 29b0 0427 000c 29fe b80d 0800 4508  ..)..'..).....E.0x0010:  0034 3a4f 4000 4006 7d0b c0a8 0109 c0a8  .4:O@.@.}.......0x0020:  0108 ac32 0cea ff5b 6e13 5d48 ac37 8010  ...2...[n.]H.7..0x0030:  049d a372 0000 0101 080a 002c 8e81 002d  ...r.......,...-0x0040:  8c65                                     .e11:08:56.320688 IP 192.168.1.9.44082 > 192.168.1.8.3306: Flags [.], ack 200, win 1271, options [nop,nop,TS val 2937505 ecr 3002501], length 00x0000:  000c 29b0 0427 000c 29fe b80d 0800 4508  ..)..'..).....E.0x0010:  0034 3a50 4000 4006 7d0a c0a8 0109 c0a8  .4:P@.@.}.......0x0020:  0108 ac32 0cea ff5b 6e13 5d48 acfe 8010  ...2...[n.]H....0x0030:  04f7 1a11 0000 0101 080a 002c d2a1 002d  ...........,...-0x0040:  d085