剖析复制线程
2017-12-16 本文已影响0人
显卡hg
传统复制线程
复制线程图- master
- Dump_thread
- slave
- IO_Thread
- SQL_Thread
- 纠结的问题
- MySQL Replication是主库把日志推给Slave,还是Slave从主库把日志拉过来的?
- 可以这么理解,延迟时间过长,比如用备份恢复时就是拉取,正常主从没有延迟或延迟比较低时是推
- 主从结构的瓶颈是什么?
- sql_thread是单线程重放,master是多线程写入,容易造成瓶颈
GTID原理
- GTID
- 事务唯一编号,全剧唯一(一个复制Group里)
- 同时和事物记录到binlog中,用来标识事务
- binlog中多一个:Gtid_log_event
- 构成
- UUID:sequence_number
- sequence_number是MySQL内部的一个事务编号,一个MySQL不会重复的顺序号(保证服务器内唯一)
- 每个MySQL有一个全局唯一的UUID:$datadir/auto.cnf中存储
- GTID复制中出现断点,估计是使用:master_auto_position=0
- master_auto_position=1
- MySQL可以记录执行过的事务ID,可以通过show master status -> gtid_executed查看
- MySQL5.6依赖于binlog和gtid_purge;MySQL5.7依赖于mysql.gtid_executed表
- slave上记录了接收和执行的gtid,可以通过show slave status:
- Retrieve_gtid_set
- Execute_gtid_set
- slave连接Master时,会把gtid_executed中的gtid发给Master,Master会Skip过executed_gtid_set,把没有执行过的GTID事务发送给slave
- BUG:slave_net_timeout 参数设置过小,造成MySQL Master Dump thread增多,主库会认为自己多了从库,其实是频繁的从库连接又断开造成的
- MySQL5.6从非GTID到GTID切换,需要重启
- MySQL5.7支持在线启用GTID
- 不用重启mysqld
- 在启用GTID过程,可以对外提供服务
- 不需要改变复制结构
- 如果需要GTID,又不想太多停机时间:升级到MySQL5.7在线启用就OK
- MySQL5.7GTID启动过程
- gtid_mode
gitd_mode | 解释 |
---|---|
OFF | 不产生GTID,Slave只接收不带GTID的事务 |
OFF_PERMISSIVE | 不产生GTID,Slave接收不带GTID的事务也接收带GTID的事务 |
ON_PERMISSIVE | 产生GTID,Slave接收不带GTID的事务也接收带GTID的事务 |
ON | 产生GTID,Slave只接收带GTID的事务 |
- 5.7GTID的切换流程
gitd_mode | 执行对象 |
---|---|
set global gtid_mode='OFF_PERMISSIVE'; | 在Group中每个MySQL上执行 |
set global gtid_mode='ON_PERMISSIVE'; | 在Group中每个MySQL上执行,为了安全尽量现在从库上执行 |
确认每个Group中binlog非GTID的执行完毕 | |
set global gtid_mode='ON'; | 在Group中每个MySQL上执行 |
- MySQL5.7GTID会存储到表里
- 支持slave不用开启binlog(log_slave_status),建议还是开启
CREATE TABLE `gtid_executed` (
`source_uuid` char(36) NOT NULL COMMENT 'uuid of the source where the transaction was originally executed.',
`interval_start` bigint(20) NOT NULL COMMENT 'First number of interval.',
`interval_end` bigint(20) NOT NULL COMMENT 'Last number of interval.',
PRIMARY KEY (`source_uuid`,`interval_start`)
-
如何记录gtid
- 如果开启binlog,在binlog日志文件切换时,将当前的gtid插入到gtid_executed表中
- insert into mysql.gtid_executed(UUID,1000,2000);
- 如果没有开启binlog,每个事务提交前,会执行一个insert操作
- Begin
- 事务操作
- insert into mysql.gtid_executed(UUID,1000,2000); #隐式MySQL内部添加
- commit
- 如果开启binlog,在binlog日志文件切换时,将当前的gtid插入到gtid_executed表中
-
gtid_executed表压缩
- 控制压缩频率
-
mysql>set global gtid_executed_compression_period=N;(N事务个数,默认是1000)
压缩前后对比
-
- 控制压缩频率
-
enforce-gtid-consistency
- OFF 不检测是否有GTID不知的语句/事务
- WARN 当发现不支持语句/事务,返回警告,并在日志中记录警告信息
- ON 当发现语句/事务不支持GTID时,返回错误
-
TIPS
- 在线上从非GTID到GTID切换过程中,可以先设置成:WARN
-
GTID Limit
- 不能使用 create table ... select ...
- 事务中更新非事务表
- begin:update no_trx_tabe set col1=xxx where xxx;update trx_tb set col1=xxx where ....;commit;
- 事务中创建/删除临时表
- begin:update trx_tb set xxx;create temporary table ...;commit;
- sql_slave_skip_counter 不支持
- 如果在传统复制到GTID复制切换时,主从卡住了,可以用start slave sql_thread试一下看能不能过去,要是过不去只能使用enforce-gtid-consistency=off先让主从能跑通
-
GTID跳过错误
- stop slave sql_thread;
- set gtid_next='uuid:N';
- begin;commit;
- set gtid_next='automatic';
- start slave sql_thread;
半同步复制
MySQL5.7无数据丢失的半同步复制
半同步复制- 半同步复制里面主从会不会存在延迟?
- 会存在延迟,半同步只能保证写入到relay log,也就是IO_thread同步的部分,sql_thread重放有可能会出现延迟
- 在Master接收到slave的ACK应答后才Commit事务(5.6上是Master在Commit后,才等待Slave应答)
- 因此在事务复制到slave之前,并发的事务看不到当前的事务的数据(5.6这有点问题)
- 当Master故障时,所有已提交的事务都会复制到slave上
- 缺省采用无数据丢失的应答等待机制,用户可以选择使用5.6的应答待机制
- mysql>set rpl_semi_sync_master_wait_point={AFTER_SYNC|AFTER_COMMIT}
增强半同步
- mysql>set rpl_semi_sync_master_wait_point=AFTER_SYNC;
[图片上传失败...(image-e3c6a6-1513426634210)]
半同步
-
mysql>set rpl_semi_sync_master_wait_point=AFTER_COMMIT;
半同步
更快的半同步复制
-
MySQL5.6
mysql5.6半同步复制- mysql5.6半同步复制两个事务中间有三个流程要走,所以两个事务之间存在时间间隔
- MySQL5.7
- 创建单独的应答接收线程
-
变成双工模式:发送和接收互不影响
mysql5.7半同步复制
5.7半同步
- Master接收到N个Slave应答后,才Commit事务
- 用户可以设置应答的Slave数量
mysql>set global rpl_semi_sync_master_wait_for_slave_count=2;
- 特别提示:
- 低于5.7版本,在从库关闭一段时间后,刚启动时,注意先用异步复制,复制追上后,在用半同步复制
- master:
- set global rpl_semi_sync_master_enabled=ON|OFF;
- slave:
- set global rpl_semi_sync_slave_enabled=ON|OFF;
- 增强半同步mysqld creash recovery
- 扫描最后一个binlog提取其中Xid
- 扫描InnoDB维持状态在Prepare的事务链表,和Binlog中的Xid比较,如果Binlog中存在,则提交,否则回滚事务
- 提交到InnoDB中处于Prepare,并且写入Binlog的,就可以从崩溃中恢复事务
- 三种场景
- redo中有Xid,filename,position 执行Commit
- redo中有prepare_mutex_lock,Xid
- 在last binlog中找到对应的事务 执行Commit
- 反之rollback
- redo只有事务本身,没有Xid,prepare_mutex_lock 执行rollback
并行复制
并发复制
- 在MySQL5.6的并发复制是基于库级别
- 实质上需要:
- 让在Master上能并发执行的事务,在Slave上也并发执行
- 在Binlog中记录事务并发执行的相关信息
- Slave上根据以上这些信息,让这些事务在Slave上多个线程中并发执行
- MySQL5.7的并发复制是基于事务级别(Logical Clock)
- 在Master上并发COMMIT事务越多,slave上并发性能越好
- 微调参数
- binlog_group_commit_sync_delay #group_commit等待时间,建议20微妙
- binlog_group_commit_sync_no_delay_count #group_commit等待时间内到达多少个事务,比如这个参数是50个,当等待时间内已经有50个事务在等待,就直接提交,不在等待了,建议设置20
- slave_preserve_commit_order=ON|OFF
- 开启binlog
- Logical_clock有作用
基于锁的并行复制
- 事务能不能并发是锁来决定的.如果有锁冲突,则一个事务要等待另一个事务执行完毕
- 如何判断并行事务没有锁冲突
- 如果两个事务(操作)可以并行执行,这两个事务没有锁的冲突
- 当事务开始执行Commit语句时,它已经获取了所有的锁
- 当事务进入到prepare阶段时,它已经获取了所有的锁
- 并发信息以逻辑的时间方式写入gtid_log_event中,共记录两个逻辑时间
- sequence_number
- 当事务写入binlog时,事务自己的逻辑时间,按照事务记录binlog的顺序递增
- commit_parent
- 当事务进行prepare阶段时,已经提交的事务的最大逻辑时间
- sequence_number
- 事务在Slave上执行
动作 | 事务 | 依赖关系 |
---|---|---|
insert commit | T1 | (1,0) |
update commit | T2 | (2,0) |
delete commit | T3 | (3,1) |
- commit_parent之前执行的事务全部提交以后,才开始执行
- T1和T2同事开始执行,T1 Commit后,T3开始执行
启用并行复制
- 启用GTID!!!
mysql>stop slave sql_thread;
mysql>set global slave_parallel_workers=4|8|max_cpu_core/2; #最多可以设成cpu核数,不过不建议,一般4个或8个就够了
mysql>set global slave_parallel_type='LOGICAL_CLOCK'; #OR DATABASE
mysql>start slave sql_thread;
- MySQL 5.7并行复制的思想简单易懂,一言以蔽之: 一个组提交的事务都是可以并行回放 ,因为这些事务都已进入到事务的prepare阶段,则说明事务之间没有任何冲突(否则就不可能提交)。
- DATABASE:默认值,基于库的并行复制方式
- LOGICAL_CLOCK:基于组提交的并行复制方式
- 最好配置binlog group commit
- binlog_group_commit_sync_delay
- binlog_group_commit_sync_no_delay_count
延迟复制
- 让从库和主库保持固定时间的延迟
- 使用场景
- 利用延迟复制做误操作恢复
- 利用延迟复制做统计分析环境处理
mysql>stop slave sql_thread;
mysql>change master to master_delay=N; #N单位秒
mysql>start slave sql_thread;
- 从库在某一个复制位置停住
- start slave中有个参数为until可以实现这个功能
- start slave sql_thread until master_log_file='xxx',master_log_pos=xxx;
- 或者可以用GTID模式,{SQL_BEFORE_GTIDS|SQL_AFTER_GTIDS}=gtid_set
多源复制
- Slave 可以同时从多个Master复制
- 每个DB中的名字不能一样,否则有可能复制出错
[图片上传失败...(image-6f90e6-1513426634210)] - 场景
- 集中备份
- 数据分析聚合
-
分片数据聚合
- 多个channels(channels包含:recevier thread ,relay logs ,applier threads)每个channel能独立的运行和停止
- 通过P_S表进行状态监控:
- 下列表中添加了channel_name字段,不同的Channel的信息在不同的行中显示
- replication_applier_status_by_coordinator
- replication_applier_status_by_worker
- replication_connection_status
group replication
group replication- Group Replication的实质是:
- 二进制日志
- 基于Row格式+GTID
- 一个通信框架+事务排序控制(atomic message delivery & total ordering of message)
- 增强半同步 & 并行复制
- Group Replication是一个趋势
- 备份不好搞定
- mysqldump不支持一致性备份(5.7.18后支持)
- xtrabackup备份,造成性能损失比较严重
- 新的节点加入过程对原有集群性能有影响
5.7复制其他方面的增强
- 动态变更filter
- 不需要重启MySQL
- 支持所有的Filter选项
- 支持各种字符集
mysql>stop slave sql_thread;
mysql>CHANGE REPLICATION FILTER REPLICATE_DO_DB= (db1,db2);
mysql>start slave sql_thread;
- 切换主时不用停sql_tread
- 切换master时,只需停止io_thread,不需要停止sql_thread
mysql>stop slave io_thread;
mysql>change master to master_host='master-2',...;
mysql>start slave io_thread;
- Replication的信息添加到了Performance Schema中
- 通过SQL进行监控
- 逻辑上无关的信息被放在不同的表中
- 配置信息表
- replication_connection_configuration
- replication_applier_configuration
- 状态信息表
- replication_connection_status
- replication_applier_status
- replication_applier_status_by_coordinator
- replication_applier_status_by_worker