WRTIESET机制源码分析
WRTIESET是什么
WRTIESET是MySQL8.0+中提供的一种并行复制的机制,旨在主库事务并行度不高的情况下提高从库回放的并行度。
WRTIESET如何实现
WRTIESET的基本思想是,不同事务的不同的记录不重叠,如果不同事务修改的记录不重叠,则这些事务都可在备节点上并行回放。并行方式的粒度从事务组内并行到记录级别。
为了实现这个过程,需要做到两件事
1.记录事务修改的记录了什么给后面判断是否重叠。
2.标识事务修改的记录是否重叠,如果重叠,我最小依赖的事务是什么。
这里面第一个对应的就是Writeset的生成。第二个对应的是last_committed的生成,下面将对这两方面展开分析。
Writeset生成
Writeset是什么里面有什么?

Writeset在MySQL当中其实对应了C++当中的一个vector,对于主库上的每一个事务,对应一个vector。Writeset通过PKE唯一地标识了事务修改的列和数据。
所以PKE是什么,PKE是一个被hash后的字符串,被hash前的PKE标识了被修改的行,主键、唯一键以及相关的外键信息。他的组成长这样。

生成Writeset的关键步骤是为被修改的行创建PKE,函数 sql/rpl_write_set_handler.cc:add_pke负责的就是为事务影响的每一行生成这些PKE的哈希值,从而构建出事务的写集合。导致主键、唯一键的值发生变化(增、删、改),都会生成一个PKE,除了NULL值的变化。

add_pke除了进行hash的生成之外还会在事务的writeset上下文里面添加一些tag,给后面来判断是不是要使用WRITESET算法。
这就是整个Writeset的生成过程,针对不同的列,例如外键列存在一些特殊的PKE生成规则,不在这里展开。
Writeset的历史Map
针对前面事务生成的PKE,我们也需要一个全局的地方进行存储,方面我们后续事务的使用。这整个结构就存储在一个map当中。它包含WRITESET的hash值和最新一次本行数据修改事务的seq number的两个元素。

last_committed⽣成
last_committed是什么怎么依赖它实现并⾏重放?
last_committed 简单的说指向了前⼀个依赖的事务,⼀个事务只有在last_committed之后重放才不会
产⽣冲突。所以只要last_comiited指向的事务执⾏完了,last_commiited之后的事务,都可以并⾏的进⾏重放。
⽣成过程
在WRITE_SET并⾏重放机制下,整个last_committed的⽣成的过程都在
Transaction_dependency_tracker::get_dependency当中。

可以看到即使是使⽤了WRITESET也还是会调⽤commit_order⽅式对于last_committed和seq number
进⾏赋值,整个WRITESET的过程可以看作⼀个降低last_committed的过程。

- 事务先使⽤committed_order进⾏赋值last_committed , seq_number = 110。
将last_com先赋值为writeset_history_strat ,这时事务last_committed = 70 , seq_number = 110。 - 查看 row1的pke冲突信息, 发现存在冲突 ,将 last_committed 替 换 为 冲 突 的 seq_number ,
- last_committed = 80, seq_number = 110,同时修改历史write_set⾥⾯row1 pke 的seq_number为
110。 - 查 看 row2 的 pke 冲 突 信 息 , 发 现 存 在 冲 突 , 将 last_committed 替 换 为 冲 突 的 seq_number ,last_committed = 82, seq_number = 110,修改历史write_set同上。
- row3同理,赋值之后为last_committed = 90, seq_number = 110。
通过这个过程,成功的降低了事务的last_committed,⽽不是依赖主库的组提交来实现。
当然除了在这个最后会⽐较commit_order和write_set⽣成的last_committed最⼩值作为事务的
last_committed,来避免在组提交边界⼜产⽣了冲突的情况。比如这种情况
假设有⼀系列事务 T1, T2, T3, T4, T5, T6
- 在源库上,事务 T1, T2 属于提交组 G1
- 事务 T3, T4, T5 属于提交组 G2
现在我们分析事务 T4: - 对于 LC_co(T4):T4 属于 G2 组。根据 COMMIT_ORDER 逻辑,它的 last_committed 应该指向 G1 组的最后⼀个事务,即 LC_co(T4) = sequence_number(T2)。
- 对于 LC_ws(T4):现在,假设事务 T4 的写集(修改的⾏)与它同组的前⼀个事务 T3 的写集发⽣了冲突。根据 WRITESET 逻辑,T4 依赖于 T3。因此,LC_ws(T4) = sequence_number(T3)。
- 我们知道 sequence_number(T3) 是在 sequence_number(T2) 之后的,因为 T3 是在 T2 之后提交的(属于不同的提交组,或者即使是同⼀组,序号也是递增的)。所以,在这种情况下:LC_ws(T4) =sequence_number(T3) ⼤于 LC_co(T4) = sequence_number(T2)。