## 并行复制相关线程 在MySQL 5.6并行复制中,当设置`set global slave_parallel_workers=2`时,共有4个复制相关的线程,如下: ~~~ +----+-------------+------+-------+---------+------+------------------------------------------------------------------+------+ | ID | USER | HOST | DB | COMMAND | TIME | STATE | INFO | +----+-------------+------+-------+---------+------+------------------------------------------------------------------+------+ | 23 | system user | | NULL | Connect | 3 | Waiting for master to send event | NULL | | 24 | system user | | mysql | Connect | 3 | Slave has read all relay log; waiting for the slave I/O thread t | NULL | | 25 | system user | | NULL | Connect | 3 | Waiting for an event from Coordinator | NULL | | 26 | system user | | NULL | Connect | 3 | Waiting for an event from Coordinator | NULL | +----+-------------+------+-------+---------+------+------------------------------------------------------------------+------+ ~~~ 其中第一个为IO线程,负责从主库拉取binlog到备库并存为relay log; 第二个为分发线程,负责解析relay log,并将解析后的事件分发给worker线程处理; 其余两个为worker线程负责处理分发后的事件,类型非并行复制时的sql线程。 ## 并行复制并发策略 目前RDS MySQL并行复制是以表级别进行分发的,即同一时刻,同一个表相关的操作只能在同一个worker线程中进行。 考虑两个事务,分别对表进行insert ~~~ trx1: begin; insert into t1 values(1); comit; trx2: begin; insert into t2 values(1); comit; ~~~ 那么这两个事务可能分别在两个worker中并行执行。 有些特殊情会影响worker的并发执行: * 并行退化为串行的情况 * DDL语句串行处理(RDS MySQL正在优化DDL并行处理) * binlog切换事件需串行处理 * 有外键关系的表需串行处理 * 并行等待的情况 当一个事务中包含多个表时,如下: ~~~ trx3: begin; insert into t1 values(3); insert into t2 values(3); comit; ~~~ 如果当前worker1正在执行t1相关的事务,worker2正在执行t2相关的事务,那么分发线程在分发trx3的t2的事件时必须等待worker2的t2相关的事务执行完成。 ## 并行复制worker分配 假设,worker线程正在执行的情况如下: ~~~ worker 1: trx1: insert t1 worker 2: trx2: insert t2; trx3: insert t3; ~~~ 然后分发线程再来一个事务trx4: ~~~ trx4: insert t4; ~~~ 分发线程该分配给哪个worker呢? 分配线程分配会分配给最空闲的worker(`get_least_occupied_worker`); 这里最空闲是指worker当前正在执行的事务涉及的表数越少越空闲。 worker1正在执行的事务涉及的表数为1,而worker2正在执行的事务涉及的表数为2,因此trx4会分配为worker1; 表和worker存在一一对应关系,为了快速找到表对应的worker,哈希表(`mapping_db_to_worker`)存储了这种映射关系。同时为了控制哈希表过大导致占用较多内存或哈希冲突,系统严格控制了哈希表的大小,不能超过16(硬编码`mts_partition_hash_soft_max`),当超过16时,会将当前没有执行的表从哈希表驱逐出去。 在表较多情况下,哈希表大小设为16显得过小,可能会频繁驱逐表,频繁新建哈希键值对的情况。 ## 优化 1. 表级并行复制下,哈希表的大小16(硬编码`mts_partition_hash_soft_max`)过小,应设为动态可调; 2. 分配线程分配会分配给最空闲的`worker(get_least_occupied_worker)`。 考虑如下情况: ~~~ worker 1: t1 1000个事务 worker 2: t2 10个事务 t3 10 个事务 ~~~ 当前worker1比worker2要繁忙,而按照当前正在执行的事务涉及的表数越少越空闲的原则,新来的事务(比如insert t4)会分配给worker1,这是不合理的。 因此,空闲的标准应改为当前正在执行的事务数越少越空闲,这样新来的事务会分配给worker2。