Franz`s blog

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的过程。

  1. 事务先使⽤committed_order进⾏赋值last_committed , seq_number = 110。
    将last_com先赋值为writeset_history_strat ,这时事务last_committed = 70 , seq_number = 110。
  2. 查看 row1的pke冲突信息, 发现存在冲突 ,将 last_committed 替 换 为 冲 突 的 seq_number ,
  3. last_committed = 80, seq_number = 110,同时修改历史write_set⾥⾯row1 pke 的seq_number为
    110。
  4. 查 看 row2 的 pke 冲 突 信 息 , 发 现 存 在 冲 突 , 将 last_committed 替 换 为 冲 突 的 seq_number ,last_committed = 82, seq_number = 110,修改历史write_set同上。
  5. row3同理,赋值之后为last_committed = 90, seq_number = 110。

通过这个过程,成功的降低了事务的last_committed,⽽不是依赖主库的组提交来实现。

当然除了在这个最后会⽐较commit_order和write_set⽣成的last_committed最⼩值作为事务的
last_committed,来避免在组提交边界⼜产⽣了冲突的情况。比如这种情况
假设有⼀系列事务 T1, T2, T3, T4, T5, T6