ITPub博客

首页 > 数据库 > MySQL > mysql replication之GTID

mysql replication之GTID

原创 MySQL 作者:myownstars 时间:2015-02-10 18:47:31 0 删除 编辑
 原理

5.6引入,在整个复制拓扑结构内,每个事务拥有自己全局唯一标识,,每个GTID对应一个事务;

GTID包含两个部分,一部分是实例的UUID,另一部分是实例内递增的整数,GTID = server_uuid:transaction_id,

注:第一次启动mysql会生成server_uuid并存于数据目录的auto.cnf

一旦GTID对应的事务被执行,会加入当前实例的gtid_executed集合,后续相同GTID的事务则直接被忽略;

流程

主库执行事务,分配GTID并记入binlog

2 slave解析binlog,将GTID赋给gtid_next并于下一个事务使用此GTIDgtid_next位于session context

3 slave使用GTID前先做检查,确保其既没有被使用过(gtid_executed) 也没有被其他session正在使用(gtid_owned),多个客户端不可并发运行同一个事务;


配置参数

主备都要声明 gtid-mode = ON & enforce-gtid-consistency = ON

Gtid_executed实例已执行的所有GTID集合;服务器启动时,读取最新的binlog,赋予union(previous_gtids_log_event, gtid_log_event)

Gtid_purged:已清除的binlog中包含的GTID集合;服务器启动时,读取最旧binlogprevious_gtid_log_event并将其赋值;每purge一个binlog,则重置一次;reset master会将其重置为空;

注:任意时刻,实例当前binlog中可以找到的事务范围为GTID_SUBTRACT(@@global.gtid_executed, @@global.gtid_purged)

Gtid_owned:只读变量,分别描述sessionglobal当前拥有的gtid集合;

Gtid_next

指定下一个GTID获取的方式,有3种选项

1 automatic,使用下一个自动产生的GTID

2 anonymous,不使用GTID

3 GTID(格式为UUID:NUMBER),即下一个事务采用此GTID



转化replication

对已有的replication,将其转化为GTID模式;

将主备都设置为read-only,并确保slavemaster同步;

2 masterslave,开启GTIDbinloglog-slave-updates(备库)

slaveauto-positioning方式连接到master

去除masterslaveread-only限制;


具体步骤

1

主备均执行SET @@global.read_only = ON;

2

重启master

mysqld_safe --gtid_mode=ON --log-bin --log-slave-updates --enforce-gtid-consistency &

重启slave,禁止启动IOSQL thread

mysqld_safe --gtid_mode=ON --log-bin --log-slave-updates  --skip-slave-start --enforce-gtid-consistency &

: --gtid_mode为枚举而非布尔类型故只能声明on或者off,赋值01会出现问题;--enforce-gtid-consistency则是禁止gtid unsafe语句执行;

3 slave指向master

mysql> CHANGE MASTER TO

     >     MASTER_HOST = host,

     >     MASTER_PORT = port,

     >     MASTER_USER = user,

     >     MASTER_PASSWORD = password,

     >     MASTER_AUTO_POSITION = 1;

注:若要使用file+position模式,须将MASTER_AUTO_POSITION = 0

开启slave IOSQL线程,

mysql> START SLAVE;

去除masterslaveread-only限制

SET @@global.read_only = OFF;


除此之外还有其他方法http://dev.mysql.com/doc/refman/5.6/en/replication-gtids-failover.html



限制

GTID依附于事务,

更新非事务引擎

非事务引擎单条DML被视为一个事务,倘若在一个事务中和innodb混合使用,可能导致单条事务生成多个GTID;

2 create table … select

若采用row-based,该sql常常被分解成两个独立事件:创建表;插入数据;

事务可能会为这两个事件分配同一个GTID,导致slave把后面的insert忽略;

临时表

手工创建的临时表应该独立于任何事务之外(autocommit = 1)

不支持sql_slave_skip_counter

slave若要跳过事务,需设置gtid_executed或gtid_next;

5 mysql_upgrade

其会更新系统表(MyISAM),因此使用时须设置—gtid-mode=off,或者—write-binlog=off(5.6.7默认开启);



GTID的开销

 previous_gtid_log_event

位于binlog开头,记录此binlog之前的GTID列表(对应@@gtid_purged ),外加40字节 * number_of_masters

 Gtid_log_event

44字节,位于每个事务之前;

https://blogs.oracle.com/MySQL/entry/deep_dive_into_gtids_and

 


以下内容来源于  淘宝内部分享:怎么跳出MySQL的10个大坑http://www.csdn.net/article/2015-01-16/2823591  
GTID mutex的改进
GTID的分配包含两种方式,一种是自动分配,另外一种是显式设置session.gtid_next,下面简单介绍下这两种方式:
自动分配
如果没有设置session级别的变量gtid_next,所有事务都走自动分配逻辑。分配GTID发生在GROUP COMMIT的第一个阶段,也就是flush stage,大概可以描述为:
Step 1:事务过程中,碰到第一条DML语句需要记录Binlog时,分配一段Gtid事件的cache,但不分配实际的GTID
Step 2:事务完成后,进入commit阶段,分配一个GTID并写入Step1预留的Gtid事件中,该GTID必须保证不在gtid_owned集合和gtid_executed集合中, 分配的GTID随后被加入到gtid_owned集合中。
Step 3:将Binlog 从线程cache中刷到Binlog文件中。
Step 4:将GTID加入到gtid_executed集合中。
Step 5:在完成sync stage 和commit stage后,各个会话将其使用的GTID从gtid_owned中移除。

问题
由于在实例内,GTID需要保证唯一性,因此不管是操作gtid_executed集合和gtid_owned集合,还是分配GTID,都需要加上一个大锁。我们的优化主要集中在第一种GTID分配方式。
对于GTID的分配,由于处于Group Commit的第一个阶段,由该阶段的leader线程为其follower线程分配GTID及刷Binlog,因此不会产生竞争。
而在Step 5,各个线程在完成事务提交后,各自去从gtid_owned集合中删除其使用的gtid。这时候每个线程都需要获取互斥锁,很显然,并发越高,这种竞争就越明显,我们很容易从pt-pmp输出中看到如下类似的
trace: ha_commit_trans—>MySQL_BIN_LOG::commit—>MySQL_BIN_LOG::ordered_commit—>MySQL_BIN_LOG::finish_commit—>Gtid_state::update_owned_gtids_impl—>lock_sidno
这同时也会影响到GTID的分配阶段,导致TPS在高并发场景下的急剧下降。

解决
实际上对于自动分配GTID的场景,并没有必要维护gtid_owned集合。我们的修改也非常简单,在自动分配一个GTID后,直接加入到gtid_executed集合中,避免维护gtid_owned,这样事务提交时就无需去清理gtid_owned集合了,从而可以完全避免锁竞争。
当然为了保证一致性,如果分配GTID后,写入Binlog文件失败,也需要从gtid_executed集合中删除。不过这种场景非常罕见。
性能数据
使用sysbench,100张表,每张10w行记录,update_non_index.lua,纯内存操作,innodb_flush_log_at_trx_commit = 2,sync_binlog = 1000
并发线程       原生               修改后
32           24500              25000
64           27900              29000
128          30800              31500
256          29700              32000
512          29300              31700
1024         27000              31000
从测试结果可以看到,优化前随着并发上升,性能出现下降,而优化后则能保持TPS稳定。


显式设置
用户通过设置session级别变量gtid_next可以显式指定一个GTID,流程如下:
Step 1:设置变量gtid_next,指定的GTID被加入到gtid_owned集合中。
Step 2:执行任意事务SQL,在将binlog从线程cache刷到binlog文件后,将GTID加入到gtid_executed集合中。
Step 3:在完成事务COMMIT后,从gtid_owned中移除。

备库SQL线程使用的就是第二种方式,因为备库在apply主库的日志时,要保证GTID是一致的,SQL线程读取到GTID事件后,就根据其中记录的GTID来设置其gtid_next变量。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/15480802/viewspace-1432655/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2010-03-18

  • 博文量
    375
  • 访问量
    3095008