按行分发
要解决热点表并行复制问题,就需要个按行并行复制的方案。
- 思路
若俩事务没有更新同一行,它们在备库上可以并行执行。所以该模式要求binlog是row格式。
这时判断一个事务T和worker是否冲突,用的就规则就不是“修改同一个表”,而是“修改同一行”。
按行复制和按表复制的数据结构差不多,都是为每个worker,分配一个hash。
只是按行分发的key是库名+表名+唯一键的值。
但该 唯一键 只有主键id还不够,考虑如下场景,t1除了主键,还有唯一索引a:
CREATE TABLE `t1` ( `id` int(11) NOT NULL, `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `a` (`a`) ) ENGINE=InnoDB; insert into t1 values(1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5);
要在主库执行俩事务:
session_1 | session_2 |
update t1 set a=6 where id=1; | |
update t1 set a=1 where id=2; |
这俩事务要更新的行的主键不同,但若它们被分到不同worker,可能session2先执行。这时id=1的行的a的值还是1,就会报唯一键冲突。
所以基于行策略,事务hash表中还需考虑唯一键,即key应该是 库名+表名+索引a的名字+a的值
。
在t1执行
update t1 set a=1 where id=2
在binlog里记录整行的数据修改前各字段值和修改后各字段值。因此,coordinator在解析该语句的binlog时,该事务的hash表就有三项:
key=hash_func(db1+t1+“PRIMARY”+2), value=2
value=2是因为修改前后的行id值不变,出现了两次
key=hash_func(db1+t1+“a”+2), value=1
会影响到这个表a=2的行
key=hash_func(db1+t1+“a”+1), value=1
会影响到这个表a=1的行
相比按表并行分发策略,按行并行策略在决定线程分发时,要消耗更多计算。
这俩方案都有一些约束条件:
要能够从binlog里面解析出表名、主键值和唯一索引值。即主库binlog必须是row
表必须有主键
不能有外键
表若有外键,级联更新的行不会记录在binlog,冲突检测就不准确
还好这些本就是DB生产使用规范。
按行分发策略的并行度更高。不过,若是大事务,按行分发策略有如下问题:
耗费内存
比如一个语句要删除100万行数据,这时候hash表就要记录100万个项
耗费CPU
解析binlog,然后计算hash值,对于大事务,该成本很高
所以,按行分发策略要设置一个阈值,单个事务若超过设置的行数阈值(比如,如果单个事务更新的行数超过10w行),就暂时退化为单线程模式,退化过程的逻辑大概是这样的:
coordinator暂时先hold住这个事务
等待所有worker都执行完成,变成空队列
coordinator直接执行这个事务
恢复并行模式