MySQL复制

MySQL的复制指的就是事务在Master/Slave两个节点上的执行过程。具体的流程:

当一个事物在Master上提交的时候,会将其binlog cache中的binlog events刷入到binlog文件中,binlog文件中就生成相应的binlog events,master的dump thread会定时的将binlog events发送给slave端。那么binlog events中记录的具体是什么内容由三种不同的binlog模式来决定:

① Binlog_format=Statement: SBR模式中binlog记录的是完整的原始sql语句。例如insert tb1 value(1,1),binlog中记录的也是insert tb1 value(1,1)。

② Binlog_format=Row: RBR模式中binlog记录的是具体的更新前后的行数据。有三种行数据类型write_rows、delete_rows、update_rows,如下面三个图所示:

write_rows只记录after image,delete_rows只记录before image,update_rows则会记录before image和after image。通过参数binlog_row_image来控制before image和after image中需要记录哪些数据:当为FULL时,before image和after image记录所有的columns;当为MINIMAL时,before image中只记录主键值或者改变的列(没主键的情况下),after image中只记录sql语句中指定的那些行和自增所产生的行数据;当为NOBLOB时,和FULL一样也要记录所有的行数据,但是对于blob和text列,不需要记录那些没有指定的或者没有改变的列。

③ Binlog_format=Mixed:MBR模式就是statement和row模式的混合。就是当MySQL检测到不安全的sql语句时,将其转换成row格式。不影响主从复制数据一致性的sql记录的是statement格式。

 

Slave端的io_thread首先会和master连接,接收master上dump thread传过来的binlog events,然后将其放到relay log中等待sql_thread去重放,sql_thread将接收过来的binlog events顺序的执行完成之后,slave上就会生成和master上一致的数据,完成主从复制。既然,MySQL的主从复制依赖binlog events,那么我们比较一下RBR和SBR的主要区别:

  • 记录的文件大小:比如更新100行数据,由于SBR模式记录的是原始sql语句,所以只需要记录一条sql即可;但是RBR模式中记录的是更新前后的行数据,则需要记录更新前后的各100条数据。所以RBR模式比SBR模式所需要更大的空间。可以通过设置expire_logs_days来设置binlog的保存时长;
  • 安全性:对于SBR模式来说,由于记录的是原始sql语句,那么对于random()、select…from where limit等,主库记录的是什么到了从库,还是一模一样的语句,那么就会出现主从执行的结果是不一样的,安全性低。而RBR模式,由于记录的是更新后的行数据,所以直接将数据更新到从库上即可。因此,安全性RBR比SBR要高很多;
  • 执行效率:因为SBR模式记录的是原始sql语句,到了从库上,要先将sql进行解析,才能继续下面的更新数据操作。对于RBR模式来说,因为binlog中记录的是更新后的数据,所以到了从库上直接通过引擎接口,找到相应的数据页进行更新就可以了。效率上RBR模式要比SBR模式快得多。
  • Temporary table:例如:客户端存在一个临时表(create tempoparary、order by、join等),现在我们执行 insert into …select 语句,select用到了这个临时表。由于SBR模式记录的是原始sql语句,所以从库执行时,首先会执行select,产生一个临时表,然后再将结果insert into到目标表里。相反,RBR模式binlog中记录的是最后的行数据,所以从库执行时,直接应用行数据,不会产生临时表。综上,目前主流的binlog format为row格式。

 

传统复制和GTID复制:

传统复制就是基于位置点的复制(master_log_file以及master_log_pos)。

GTID(GLOBAL TRANSACTION IDENTIED):全局事务标识符。它由source_id和sequence_id来组成。source_id通常使用源服务器的server_uuid,在服务器创建的时候随机分配的一个uuid;sequence_id是在事务提交时由系统顺序分配的一个序列号。具体的由gtid_executed来维护,如下图所下:

当MySQL启用GTID时,MySQL会定期的对这个表进行压缩,根据参数gtid_executed_compression_period来进行压缩默认是1000.但是在binlog文件切换时也会自动进行压缩。当master上一个事务提交时,会根据gtid_executed的值顺序的为当前事务分配一个GTID,然后该GTID会写入binlog文件里的GTID event中,同时每个binlog文件开始的时候,都会先将之前的GTID SET记录在privious-gtids里,如下图所示:

slave并不会产生GTID,而是接收master上传过来的GTID EVENT,然后将gtid_next设置为relay log里的GTID值来告知slave将要执行的下一个事务。slave通过gtid_retrived和gtid_executed来分别表示从master接收了多少事务和执行了多少事务。所以,相比于传统的根据位置点的复制,比如针对一些多线程环境的主从切换,事务的执行顺序也许不一样,所以这个位置点也是相当的难找;而基于GTID复制的切换,master会根据slave上gtid_retrived和gtid_executed的值来决定从哪个事务开始进行复制。如果要增加一个slave节点,可以设置gtid_purged来表示已经执行过的gtid set。

 

多线程复制

对于传统异步复制来说,由于MySQL是单进程多线程模式,主库可以并发很多连接进行写操作,而对于slave来说,sql_thread是单线程进行顺序的重放relay log。所以在主库写并发高的环境下,sql_thread经常会成为整个复制环境的瓶颈。所以为了解决这个问题,MySQL5.6引入了基于库的并发复制;5.7引入了基于逻辑时钟的并发复制。

slave_parallel_type=database(基于库的并发复制):对于master上不是同一个库上的事务,那么slave在重放的时候就可以将这些事务分给不同的worker去执行,而对于同一个库上的事务就分给同一个worker顺序去执行。slave_parallel_type=database(基于逻辑时钟的并发复制):一个事务开始的时候,首先会对其操作的B+树上的行记录加锁,一旦加锁成功,直到事务结束(commit或者rollback)才会将其所持有的锁释放。这就是二阶段锁协议。此外,MySQL的事务提交分为二阶段提交:1,通知Innodb引擎做prepare,Innodb会更改事务的状态为prepare阶段,然后将redo刷盘;2,写完其对应的binlog通知Innodb引擎将事务commit。由于事务提交之前,每个事物务持有的锁不会释放,所以如果此时同时处于prepare阶段的事务是不会有冲突的,那么他们就可以做为一个队列同时的提交。通过binlog_group_commit_sync_delay和binlog_group_commit_sync_no_delay_count来控制这个队列等待多长时间或者这个队列里最多有多少个事务可以一起提交。如下图所示:

(引用于知数堂MySQL实战班)

last_committed表示的是上一组事务提交的最大sequence_number,而sequence_number表示的是事务提交的时刻。这两个值在写入binlog时,会同时写入到GTID event中。

如下图所示:

(摘自MySQL运维内参)

slave的sql线程拿到一个新的事务时,会取出它的last_committed和当前系统sequence_number的checkpoint值(这个值表示此sequence_number之前的事务已经全部提交)。然后将这两个值做比较,如果last_committed大,那么说明这个事务是下一组的事务,要进行等待;如果小于或者等于最小值,说明是同一组事务,然后就分给空闲的worker线程去执行。具体的执行顺序通slave_preserve_commit_order来控制。开启这个参数,那么slave事务的提交顺序就应该和relay log中事务的提交顺序完全一致,所以last_committed对应的值之前的事务必须全部提交,并且其sequence_number之前的事务也必须得全部执行完,才能轮到当前的事务执行。如上图所示,虽然处于不同worker线程的事务可以并发的进行,但是每一行事务执行之前它前面那行的事务必须已经执行完成,才能轮到它执行。

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值