新浪博客

常见关系库MVCC浅析2

2022-04-16 01:22阅读:
本文继续论述数据库的MVCC实现,主要包括PolarDB-X、TIDB、OceanBase、TDSQL。
PolarDB-X
常见关系库MVCC浅析2
PolarDB-x采用的是TSOTimeStamp Oracle)的分布式事务系统,TSO分配递增的逻辑时钟事务SCN号,数据节点的MVCC数据行可见性判定也是基于该SCN号。如下图所示,PolarDB-x的数据节点没有使用原生的InnoDB MVCC实现,而是使用一个叫Lizard SCN
的事务MVCC实现。具体而言,在原InnoDB数据行系统隐藏列的基础上,新增2个隐藏列SCNUBASCN列存储改行的事务提交SCN号,UBA列存储该行相关事务对应的系统事务表对应的slot。系统事务表是一个事务提交与否最准确的判定标准,数据行中的SCN字段通常不在事务提交的时候进行填充,大部分的行记录 SCN 回填,都是 Delayed Record Cleanout,即在读取行记录上 SCN 时,发现是 NULL 值,就根据 UBA 地址查询 transaction slot 找到需要回填的 SCN,并做压力和负载的自动化优化。这块的设计类似OracleMVCC实现,不同的是OracleMVCC是块级别的,PolarDB-x是行级别的。
PolarDB-x的事务快照就是当前事务获取的读SCN号,可见性判定就是比对行SCN号与事务读SCN号,如果行SCN<=SCN,那么可见,否则基于innodb的回滚段找历史版本。
PolarDB-x引入Lizard SCN事务的主要原因是2个:
1. 在单机多核的场景下,MySQL InnoDB 的事务系统作为一个内存结构,存在严重的 Write Transaction Read Query 的干扰,无法高效使用多核能力,Lizard SCN 单机事务系统,解绑读写之间对事务系统的争抢,能够在OLTP Read-Write 的真实业务场景满载多核能力。
2. 在多节点集群的场景下,由于数据分片,一个业务场景会有多个节点参与,单机事务无法保证多个节点参与的事务 Atomicity 以及查询的一致性,Lizard SCN 分布式事务系统,提供了一整套的解决方案来满足。
TIDB
不是类似mysql那样基于回滚段,而是在tikv层把所有历史版本数据一起存储,key是原本key+mvcc版本号。版本高的在前面。如下是官网的描述。
常见关系库MVCC浅析2
这里的Version存储的是什么呢?是事务的编号或时间戳,由TIDBPD模块分配。TIDB的事务模型是基于googlepercolaterPD模块作为TSO为事务分配时间戳。TIDB事务的start_ts是事务begin开始的时候申请的,而commit_ts是在事务提交阶段申请的。和percolater一样,数据行中存在2个系统列:lockwrite
事务操作的第一行数据为primary key数据行。事务其他行的Lock列存储事务的start_ts和指向primary key数据行的信息。当事务提交的时候,会清理lock列信息,并将commit_ts信息存储到write列中。primary key行的lock清理是事务提交时做完的,而其他非primary key列的lock信息则是异步后台清理的。

Percolater的说明可以参考https://tikv.org/deep-dive/distributed-transaction/percolator/。如下是主要实现的一个说明:


常见关系库MVCC浅析2

如下是TIDB中数据行存储信息的keyvalue的示例,使用了3rocksdb column family(CF)
常见关系库MVCC浅析2


所以从write CF是可以查到数据行对应事务提交版本号commit_ts。对于MVCC读而言,一个事务的读快照(事务的启动版本号start_ts)版本号>=对应行的事务提交版本号commit_ts,即这个行可读。否则看它的历史版本。由于TIDB的数据组织是高版本放前面,低版本放后面,所以可以方便地查找历史版本。
所以MVCC查询的一个基本流程就是:
1. 事务begin,PD获取start_ts 记为read_view_ts
2. 读取key基于keylock CF中查找是否有锁信息并结合primay key还有write cf信息,判断当前行是否可读。因为lock CF是异步清理的,所以会需要通过primary keywrite cf来确认是否事务已经提交。

除了最新行外,其他的行就可以通过遍历write CFkeykey中包含了commit_ts),来确认一个可读的版本,即第一个write CF commit_ts<</font>事务read_view_ts的行。Write CF信息为${key}_${commit_ts}-->${start_ts} 其中key包含commit_ts的,而tidb的数据组织是高版本放前面低版本放后面,所以找到一个合适的commit_ts其实就是对write CF对应key的一个顺序遍历。基于找到的commit_ts,就可以获取对应可见数据行事务的start_ts,而后基于default CF存储的信息${key}_${start_ts} --> ${value} 就可以通过keystart_ts找到当前事务可以读到的数据行版本。


OceanBase
OceanBase采用基线数据+内存增量数据的分层 LSMTree 结构。数据分为两部分:基线数据和增量数据。基线数据是持久到磁盘上的数据,一旦生成就不会再修改,称之为 SSTable。增量数据存在于内存,用户写入都是先写到增量数据,称之为 MemTable,通过 Redo Log 来保证事务性。

我的更多文章

下载客户端阅读体验更佳

APP专享