澳门皇冠金沙网站-澳门皇冠844网站

热门关键词: 澳门皇冠金沙网站,澳门皇冠844网站

MySQL的并行复制多线程复制MTS,MySQL并行复制的一

早上巡检数据库,发现一个延迟从库的sql_thread中断了。

MySQL的并行复制多线程复制MTS(Multi-Threaded Slaves)

传统复制线程

图片 1

复制线程图

  • master
    • Dump_thread
  • slave
    • IO_Thread
    • SQL_Thread
  • 纠结的问题
    • MySQL Replication是主库把日志推给Slave,还是Slave从主库把日志拉过来的?
    • 可以这么理解,延迟时间过长,比如用备份恢复时就是拉取,正常主从没有延迟或延迟比较低时是推
    • 主从结构的瓶颈是什么?
    • sql_thread是单线程重放,master是多线程写入,容易造成瓶颈

Last_SQL_Errno: 1755
Last_SQL_Error: Cannot execute the current event group in the parallel mode. Encountered event Gtid, relay-log name ./oracle-relay-bin.000093, position 152912092 which prevents execution of this event group in parallel mode. Reason: The master event is logically timestamped incorrectly..

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
  • gtid_executed表压缩

    • 控制压缩频率
      • mysql>set global gtid_executed_compression_period=N;(N事务个数,默认是1000)
        ![](https://upload-images.jianshu.io/upload_images/9520647-e8d8de8fe3db46c7.png)

        压缩前后对比
  • 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;

检查performance_schema下的replication_applier_status_by_worker表,除了GTID之外也没有更具体的信息:

 

半同步复制

"root@localhost:mysql3308.sock  [(none)]>select * from performance_schema.replication_applier_status_by_worker;
 -------------- ----------- ----------- --------------- ------------------------------------------------ ------------------- -------------------- ---------------------- 
| CHANNEL_NAME | WORKER_ID | THREAD_ID | SERVICE_STATE | LAST_SEEN_TRANSACTION                          | LAST_ERROR_NUMBER | LAST_ERROR_MESSAGE | LAST_ERROR_TIMESTAMP |
 -------------- ----------- ----------- --------------- ------------------------------------------------ ------------------- -------------------- ---------------------- 
|              |         1 |      NULL | OFF           | 0b961fcc-41c2-11e7-84fd-286ed488c7da:156369774 |                 0 |                    | 0000-00-00 00:00:00  |
|              |         2 |      NULL | OFF           |                                                |                 0 |                    | 0000-00-00 00:00:00  |
|              |         3 |      NULL | OFF           |                                                |                 0 |                    | 0000-00-00 00:00:00  |
|              |         4 |      NULL | OFF           |                                                |                 0 |                    | 0000-00-00 00:00:00  |
|              |         5 |      NULL | OFF           |                                                |                 0 |                    | 0000-00-00 00:00:00  |
|              |         6 |      NULL | OFF           |                                                |                 0 |                    | 0000-00-00 00:00:00  |
|              |         7 |      NULL | OFF           |                                                |                 0 |                    | 0000-00-00 00:00:00  |
|              |         8 |      NULL | OFF           |                                                |                 0 |                    | 0000-00-00 00:00:00  |
 -------------- ----------- ----------- --------------- ------------------------------------------------ ------------------- -------------------- ---------------------- 

姜承饶

MySQL5.7无数据丢失的半同步复制

图片 2

半同步复制

  • 半同步复制里面主从会不会存在延迟?
    • 会存在延迟,半同步只能保证写入到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}

既然relay_log的位置信息都有了,那就去日志里看看吧:

 

增强半同步

  • mysql>set rpl_semi_sync_master_wait_point=AFTER_SYNC;
    [图片上传失败...(image-e3c6a6-1513426634210)]

解析Binlog文件:

简称MTS:基于binlog组提交,mysql5.7默认开启binlog组提交

半同步

  • mysql>set rpl_semi_sync_master_wait_point=AFTER_COMMIT;
![](https://upload-images.jianshu.io/upload_images/9520647-d094f02d43c86bb7.jpg)

半同步
mysqlbinlog -v --base64-output=decode-rows oracle-relay-bin.000093 >1.sql

更快的半同步复制

  • MySQL5.6
![](https://upload-images.jianshu.io/upload_images/9520647-3a3164607962b8c1.png)

mysql5.6半同步复制

-   mysql5.6半同步复制两个事务中间有三个流程要走,所以两个事务之间存在时间间隔
  • MySQL5.7
    • 创建单独的应答接收线程
    • 变成双工模式:发送和接收互不影响
    ![](https://upload-images.jianshu.io/upload_images/9520647-f1c47ffd00a38f79.png)

    mysql5.7半同步复制

找到152912092位置点附近的日志:

 组提交(group commit)是MYSQL处理日志的一种优化方式,主要为了解决写日志时频繁刷磁盘的问题。组提交伴随着MYSQL的发展不断优化,从最初只支持redo log 组提交,到目前5.6官方版本同时支持redo log 和binlog组提交。组提交的实现大大提高了mysql的事务处理性能

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

图片 3

 

并行复制

检查了一下数据库中这个表ID为14816035的数据确实是不存在的。

支持多线程复制(Multi-Threaded Slaves, 简称MTS:基于binlog组提交 不是redolog组提交,一个组提交的事务都是可以并行回放 ,因为这些事务都已进入到事务的prepare阶段,则说明事务之间没有任何冲突(否则就不可能提交)。
SQL线程就分裂为coordinator线程和worker线程,worker线程对组提交的事务进行并行回放

并发复制

  • 在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有作用

另外除了这条日志,其它日志的last_committed和sequence_number都为0,last_committed表示事务提交的时候,上次事务提交的编号。last_committed和sequence_number代表的就是所谓的LOGICAL_CLOCK。

为了兼容MySQL 5.6基于库的并行复制,5.7引入了新的变量slave-parallel-type,其可以配置的值有:

基于锁的并行复制

  • 事务能不能并发是锁来决定的.如果有锁冲突,则一个事务要等待另一个事务执行完毕
  • 如何判断并行事务没有锁冲突
    • 如果两个事务(操作)可以并行执行,这两个事务没有锁的冲突
    • 当事务开始执行Commit语句时,它已经获取了所有的锁
    • 当事务进入到prepare阶段时,它已经获取了所有的锁
  • 并发信息以逻辑的时间方式写入gtid_log_event中,共记录两个逻辑时间
    • sequence_number
      • 当事务写入binlog时,事务自己的逻辑时间,按照事务记录binlog的顺序递增
    • commit_parent
      • 当事务进行prepare阶段时,已经提交的事务的最大逻辑时间
  • 事务在Slave上执行
动作 事务 依赖关系
insert commit T1 (1,0)
update commit T2 (2,0)
delete commit T3 (3,1)
  • commit_parent之前执行的事务全部提交以后,才开始执行
  • T1和T2同事开始执行,T1 Commit后,T3开始执行

猜测如果手动把这条数据插入延迟从库,并且使用注入一个空事务跳过这个GTID的方法重启sql_thread,相信这个错误也能被解决。

DATABASE:默认值,基于库的并行复制方式
LOGICAL_CLOCK:基于组提交的并行复制方式

启用并行复制

  • 启用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

但既然带了LOGICAL_CLOCK的事务就会出错,跳过事务的方法很难保证以后不会出错。

支持并行复制的GTID
如何知道事务是否在一组中,又是一个问题,因为原版的MySQL并没有提供这样的信息。在MySQL 5.7版本中,其设计方式是将组提交的信息存放在GTID中。那么如果用户没有开启GTID功能,即将参数gtid_mode设置为OFF呢?故MySQL 5.7又引入了称之为Anonymous_Gtid的二进制日志event类型,如:

延迟复制

  • 让从库和主库保持固定时间的延迟
  • 使用场景
    • 利用延迟复制做误操作恢复
    • 利用延迟复制做统计分析环境处理
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

注意到这条日志的last_committed是一个异常大的值,且错误信息中有提到The master event is logically timestamped incorrectly。我怀疑是不是并行配置的问题。

mysql> SHOW BINLOG EVENTS in 'mysql-bin.000006';
------------------ ----- ---------------- ----------- ------------- -----------------------------------------------
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
------------------ ----- ---------------- ----------- ------------- -----------------------------------------------
| mysql-bin.000006 | 4 | Format_desc | 88 | 123 | Server ver: 5.7.7-rc-debug-log, Binlog ver: 4 |
| mysql-bin.000006 | 123 | Previous_gtids | 88 | 194 | f11232f7-ff07-11e4-8fbb-00ff55e152c6:1-2 |
| mysql-bin.000006 | 194 | Anonymous_Gtid | 88 | 259 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| mysql-bin.000006 | 259 | Query | 88 | 330 | BEGIN |
| mysql-bin.000006 | 330 | Table_map | 88 | 373 | table_id: 108 (aaa.t) |
| mysql-bin.000006 | 373 | Write_rows | 88 | 413 | table_id: 108 flags: STMT_END_F

多源复制

  • Slave 可以同时从多个Master复制
  • 每个DB中的名字不能一样,否则有可能复制出错
    [图片上传失败...(image-6f90e6-1513426634210)]
  • 场景
    • 集中备份
    • 数据分析聚合
    • 分片数据聚合
    ![](https://upload-images.jianshu.io/upload_images/9520647-7b244ef4563f90fa.png)
  • 多个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

本文由澳门皇冠金沙网站发布于数据库研究,转载请注明出处:MySQL的并行复制多线程复制MTS,MySQL并行复制的一