• MySQL5.7 并行复制的学习


    MySQL 5.6 基于库级别的并行复制

    MySQL5.6的并行复制是库(schema)级别的,从库为每个库(schema)分配一个线程以此来提高复制效率

    在MySQL 5.6版本之前,Slave服务器上有两个线程I/O线程和SQL线程。I/O线程负责接收二进制日志(更准确的说是二进制日志的event),SQL线程进行回放二进制日志。

    MySQL5.6开启并行复制时,从库SQL线程就变为了coordinator线程,coordinator线程主要负责以下两部分的内容:

    • 判断可以并行执行,那么选择worker线程执行事务的二进制日志
    • 判断不可以并行执行,如该操作是DDL,亦或者是事务跨schema操作,则等待所有的worker线程执行完成之后,再执行当前的日志。

    这意味着coordinator线程并不是仅将日志发送给worker线程,自己也可以回放日志,但是所有可以并行的操作交付由worker线程完成。coordinator线程与worker是典型的生产者与消费者模型。

    对于有多个数据库的实例,开启并行的执行SQL,对从库能有较大的提升。但对单个库,开启多线程复制,性能可能比单线程还差。

    MySQL 5.7 基于组提交(LOGICAL_CLOCK)的并行复制

    MySQL5.7的并行复制是基于组提交(LOGICAL_CLOCK),主要思想是一个组提交的事务都是可以并行回放到从,原理是基于锁的冲突检测,因为这些事务都已进入到事务的prepare阶段,则说明事务之间没有任何冲突(否则就不可能提交)。

    MySQL5.7并行复制引入了两个值last_committedsequence_number。last_committed表示事务提交的时候,上次事务提交的编号,在主库上同时提交的事务设置成相同的last_committed。如果事务具有相同的last_committed,表示这些事务都在一组内,可以进行并行的回放。

    总的来说就是:并发线程执行不同的事务只要在同一时刻能够commit(说明线程之间没有锁冲突),那么master节点就可以将这一组的事务标记并在slave机器上安全的进行并发重放主库提交的事务。所以尽可能的使所有线程能在同一时刻提交可以,可以极大的提高slave机器并发执行事务的数量使主备数据同步。

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

    • DATABASE:默认值,基于库的并行复制方式

    • LOGICAL_CLOCK:基于组提交的并行复制方式

    主端相关参数:

    • binlog_group_commit_sync_delay:表示binlog提交后等待延迟多少时间再同步到磁盘,单位是微秒,默认0,不延迟。设置延迟可以让多个事务在用一时刻提交,提高binlog组提交的并发数和效率,从而提高slave的吞吐量。

    • binlog_group_commit_sync_no_delay_count:表示在等待上面参数超时之前,如果有足够多的事务,则停止等待直接提交。单位是事务数,默认0。

    从端相关参数:

    • slave_parallel_workers:设置为0,则MySQL 5.7退化为原单线程复制;设置为1,则SQL线程功能转化为coordinator线程,但是只有1个worker线程进行回放,也是单线程复制,性能反而比0还要差

    那么如何知道事务是否在一组中?在MySQL 5.7是将组提交的信息存放在GTID中。那么如果用户没有开启GTID功能,即将参数gtid_mode设置为OFF呢?故MySQL 5.7又引入了称之为Anonymous_Gtid的二进制日志event类型,如:

    mysqlbinlog --base64-output=decode-rows -vv mysql-bin.000005 | grep last_committed
    
    #181228 11:26:19 server id 1  end_log_pos 219 CRC32 0x5f456ce6         Anonymous_GTID  last_committed=0        sequence_number=1       rbr_only=yes
    #181228 11:26:25 server id 1  end_log_pos 489 CRC32 0x70efcdd6         Anonymous_GTID  last_committed=1        sequence_number=2       rbr_only=yes
    #181228 11:29:25 server id 1  end_log_pos 758 CRC32 0x988d75b0         Anonymous_GTID  last_committed=1        sequence_number=3       rbr_only=yes
    #181228 11:29:30 server id 1  end_log_pos 1027 CRC32 0x0441d881         Anonymous_GTID  last_committed=1        sequence_number=4       rbr_only=yes
    #181228 11:30:23 server id 1  end_log_pos 1299 CRC32 0xacef32af         Anonymous_GTID  last_committed=2        sequence_number=5       rbr_only=yes
    #181228 11:41:24 server id 1  end_log_pos 1578 CRC32 0xe19286ec         Anonymous_GTID  last_committed=3        sequence_number=6       rbr_only=yes
    #181228 11:43:14 server id 1  end_log_pos 1857 CRC32 0x29a01493         Anonymous_GTID  last_committed=4        sequence_number=7       rbr_only=yes
    #181228 11:43:30 server id 1  end_log_pos 2139 CRC32 0x9acc9bff         Anonymous_GTID  last_committed=1        sequence_number=8       rbr_only=yes
    #181228 11:43:31 server id 1  end_log_pos 2436 CRC32 0x1c43ff17         Anonymous_GTID  last_committed=1        sequence_number=9       rbr_only=yes
    #181228 11:43:31 server id 1  end_log_pos 2778 CRC32 0x93632d39         Anonymous_GTID  last_committed=1        sequence_number=10      rbr_only=yes
    #181228 11:43:31 server id 1  end_log_pos 3210 CRC32 0xf2d0d2b3         Anonymous_GTID  last_committed=1        sequence_number=11      rbr_only=yes
    #181228 11:43:32 server id 1  end_log_pos 3822 CRC32 0xc1481a21         Anonymous_GTID  last_committed=1        sequence_number=12      rbr_only=yes
    #181228 11:43:32 server id 1  end_log_pos 4794 CRC32 0x58c1beae         Anonymous_GTID  last_committed=1        sequence_number=13      rbr_only=yes
    #181228 11:43:33 server id 1  end_log_pos 6486 CRC32 0x4364eaf9         Anonymous_GTID  last_committed=1        sequence_number=14      rbr_only=yes
    #181228 11:43:33 server id 1  end_log_pos 9618 CRC32 0x2c5edb8c         Anonymous_GTID  last_committed=1        sequence_number=15      rbr_only=yes
    #181228 11:43:34 server id 1  end_log_pos 15630 CRC32 0xa75cc5f4        Anonymous_GTID  last_committed=1        sequence_number=16      rbr_only=yes
    #181228 11:43:34 server id 1  end_log_pos 27437 CRC32 0xff0942d4        Anonymous_GTID  last_committed=1        sequence_number=17      rbr_only=yes
    #181228 11:43:34 server id 1  end_log_pos 50799 CRC32 0x1cb6f41f        Anonymous_GTID  last_committed=1        sequence_number=18      rbr_only=yes
    #181228 11:43:35 server id 1  end_log_pos 97306 CRC32 0x5d03c9b8        Anonymous_GTID  last_committed=1        sequence_number=19      rbr_only=yes
    #181228 11:43:40 server id 1  end_log_pos 190103 CRC32 0x90e95ad1       Anonymous_GTID  last_committed=19       sequence_number=20      rbr_only=yes
    #181228 11:43:41 server id 1  end_log_pos 375445 CRC32 0x2515833b       Anonymous_GTID  last_committed=20       sequence_number=21      rbr_only=yes
    #181228 11:43:41 server id 1  end_log_pos 745912 CRC32 0x81a72e5c       Anonymous_GTID  last_committed=21       sequence_number=22      rbr_only=yes
    

    这意味着在MySQL 5.7版本中即使不开启GTID,每个事务开始前也是会存在一个Anonymous_Gtid,而这GTID中就存在着组提交的信息。

    上面展示的日志内容可以看出,sequence_number 1~19 事物的last_committed都是1,意味着sequence_number 1~19 事物属于同一组可以并行提交。

    配置基于组提交(LOGICAL_CLOCK)的并行复制

    开启并行复制,只需在slave端加如下配置:

    
    [mysqld]
    #slave
    slave_parallel_workers    = 4               ###并行复制的线程数
    slave_parallel_type       = LOGICAL_CLOCK   ###并行复制的类型,默认database
    master_info_repository    = table
    relay_log_info_repository = table
    relay_log_recovery           = 1
    

    slave_preserve_commit_order

    slave端仅仅设置为LOGICAL_CLOCK也会存在问题,因为此时在slave上应用事务的顺序是无序的,和relay log中记录的事务顺序不一样,这样数据一致性是无法保证的,为了保证事务是按照relay log中记录的顺序来回放,就需要开启参数slave_preserve_commit_order

    开启slave_preserve_commit_order参数后,slave_parallel_type只能是LOGICAL_CLOCK

    slave_preserve_commit_order=1
    

    开启该参数后,worker线程将一直等待, 直到提交之前所有的事务。当从线程正在等待其他工作人员提交其事务时, 它报告其状态为等待前面的事务提交。所以虽然slave可以并行应用relay log,但commit部分仍然是顺序提交,其中可能会有等待的情况。

    当开启slave_preserve_commit_order参数后,slave_parallel_type只能是LOGICAL_CLOCK,如果你有使用级联复制,那LOGICAL_CLOCK可能会使离master越远的slave并行性越差。

    PS: 经测试这个参数只有在MySQL 5.7.19设置才有效

    基于写集合的并行复制

    writeset的思想是:不同事物修改了不同行的数据,那么可以视为同一组。MySQL 会对这个提交的事务中的一行记录做一个 HASH值,这些 HASH 值称为 writeset。writeset会存入一张 HASH 表。其他事务提交时会检查这张 HASH 表中是否有相同的记录,如果不相同,则视为同组,如果有相同,则视为不同组。怎么判断是否同组,依然采用了last_committed。

    实现基于写集的并行复制,需要在master端添加如下配置:

    [mysqld]
    transaction_write_set_extraction=XXHASH64
    binlog_transaction_dependency_tracking=WRITESET
    

    这时候,我们分别执行两次insert操作,并解析出binlog:

    BEGIN
    /*!*/;
    # at 291
    #181228 17:04:29 server id 1  end_log_pos 340 CRC32 0xa581c6c2  Table_map: `mydb`.`ttt` mapped to number 108
    # at 340
    #181228 17:04:29 server id 1  end_log_pos 390 CRC32 0x72728e77  Write_rows: table id 108 flags: STMT_END_F
    ### INSERT INTO `mydb`.`ttt`
    ### SET
    ###   @1=4652970 /* INT meta=0 nullable=0 is_null=0 */
    ###   @2='ooooopppp' /* VARSTRING(60) meta=60 nullable=1 is_null=0 */
    # at 390
    #181228 17:04:29 server id 1  end_log_pos 421 CRC32 0xa7e2fcb6  Xid = 819
    COMMIT/*!*/;
    # at 421
    #181228 17:04:36 server id 1  end_log_pos 486 CRC32 0xc7670134  Anonymous_GTID  last_committed=1        sequence_number=2       rbr_only=yes
    /*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
    SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
    # at 486
    #181228 17:04:36 server id 1  end_log_pos 558 CRC32 0xcf925205  Query   thread_id=348   exec_time=0     error_code=0
    SET TIMESTAMP=1545987876/*!*/;
    BEGIN
    /*!*/;
    # at 558
    #181228 17:04:36 server id 1  end_log_pos 607 CRC32 0x69875f63  Table_map: `mydb`.`ttt` mapped to number 108
    # at 607
    #181228 17:04:36 server id 1  end_log_pos 660 CRC32 0x03f67767  Write_rows: table id 108 flags: STMT_END_F
    ### INSERT INTO `mydb`.`ttt`
    ### SET
    ###   @1=4652971 /* INT meta=0 nullable=0 is_null=0 */
    ###   @2='xxxyyyyyyyyy' /* VARSTRING(60) meta=60 nullable=1 is_null=0 */
    # at 660
    #181228 17:04:36 server id 1  end_log_pos 691 CRC32 0xc00ede8f  Xid = 820
    COMMIT/*!*/;
    # at 691
    #181228 17:05:18 server id 1  end_log_pos 756 CRC32 0x154b4941  Anonymous_GTID  last_committed=1        sequence_number=3       rbr_only=yes
    /*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
    SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
    

    可以看到两次执行的事物sequence_number 2 3 对应的 last_committed都是1

    那么 writeset 最多可以并行执行多少个事务呢?近于 binlog_transaction_dependency_history_size 的一半

    测试

    待续

    参考

    https://www.cnblogs.com/zhoujinyi/p/5704567.html
    http://blog.itpub.net/28218939/viewspace-1975809/
    http://blog.itpub.net/28218939/viewspace-1975822/
    http://blog.itpub.net/28218939/viewspace-1975856/

  • 相关阅读:
    用php做了下冒泡排序
    安装xampp无法设置默认时间的坑
    PHP的静态变量和引用函数
    jquery.cookie.js 用法
    PhpStorm的open in browser怎么修改端口和相对路径
    springmvc上传图片并显示图片--支持多图片上传
    Spring MVC中处理静态资源的多种方法
    超强、超详细Redis数据库入门教程(转载)
    推荐60个jQuery插件(转)
    [Spring MVC]
  • 原文地址:https://www.cnblogs.com/wshenjin/p/10191786.html
Copyright © 2020-2023  润新知