在TiKV MVCC
并发控制简介
可串行性是经典的并发方案。它确保执行并发事务的调度与按照某种顺序连续执行事务的调度相同。虽然可序列化性是一个很好的概念,但是很难有效地实现。经典解是两相锁定,即2PL.使用2PL,数据库管理系统(DBMS)维护读和写锁,以确保冲突的事务按照定义良好的顺序执行,或按照可序列化的执行计划执行。然而,锁定有几个缺点。首先,读者和作者相互阻碍。其次,大多数事务是只读的,因此从事务排序的角度来看是无害的。在基于锁定的隔离机制下,可能长时间运行的读事务正在读取的数据对象上不允许有更新事务。因此,更新必须等到读取完成。这严重限制了系统中的并发程度。
多版本并发控制(MVCC)是一个优雅的解决方案解决这个问题,其中每个更新都会创建一个新版本的数据对象而不是更新它,使得并发读者仍然可以在更新事务继续时看到旧版本。这样的策略可以防止只读交易等待。实际上,根本不需要锁定。这是一个非常理想的财产,以及许多数据库系统,如postgreSQL,Oracle和Microsoft SQL Server实现MVCC。
在这篇文章中,我们将探讨TIKV中MVCC实施的复杂性。
在TiKV MVCC
让我们深入TiKV
的MVCC实现,它位于src /存储.
时间戳甲骨文(TSO)
自从TiKV
是分布式存储系统,它需要一个全局唯一的时间服务,称为时间戳oracle.
(TSO),以分配一个单调递增的时间戳。类似于谷歌的TrueTime API扳手,这个服务是在TiKV的放置驱动程序(PD)中实现的。每一个TS
表示单调递增的时间戳。
存储
要潜入交易部分TiKV
,src /存储是一个很好的起点。存储
是一个实际接收Get/Scan命令的结构体。
酒吧结构体存储{引擎:盒子<引擎>,Sendch.:SendCh<味精>,处理:弧<互斥锁<StorageHandle.>>,}impl存储{酒吧fn开始(&mut自我,配置:&配置)- >结果<()>{让mut处理=自我.处理.锁().打开();如果处理.处理.is_some(){返回犯错(box_err !(调度程序已经在运行));}让引擎=自我.引擎.克隆();让构建器=线::构建器::新().的名字(thd_name !(“存储调度程序”));让mut埃尔=处理.event_loop.取().打开();让sched_concurrency=配置.sched_concurrency;让sched_worker_pool_size=配置.sched_worker_pool_size;让sched_too_busy_threshold=配置.sched_too_busy_threshold;让ch=自我.Sendch..克隆();让h=尝试!(构建器.产卵(移动||{让mut固定播送时间=调度器::新(引擎,ch,sched_concurrency,sched_worker_pool_size,sched_too_busy_threshold);如果让犯错(e)=埃尔.运行(&mut固定播送时间){恐慌!(“调度程序运行出错:{:?}”,e);}信息!(“调度器停止”);}));处理.处理=一些(h);好的(())}}
这开始
上面示例中的功能介绍了存储结构的运行方式。
引擎
引擎描述存储系统中实际使用的数据库的特征。它是在raftkv和Rocksdb_engine..
StorageHandle.
StorageHandle.
是处理接收到的命令的结构吗Sendch.
.I/O由绪
.
然后函数就像async_get
和async_batch_get
在里面贮存
Struct将向通道发送相应的命令,调度程序可以获得这些命令并异步执行。
MVCC层被调用进来调度器.存储接收来自客户端的命令,并将命令作为消息发送给调度程序。然后调度程序将处理命令或调用对应的异步函数.有两种类型的操作——读和写。Read在MvccReader,这很容易理解,所以我们不作详细说明。让我们关注编写,这是MVCC实现的核心。
MVCC.
专栏家庭
Percolator通过在特定行中添加额外的列来存储像Lock这样的信息,而TiKV使用RocksDB中的一个列族(CF)来处理所有与Lock相关的信息。具体来说,TiKV存储键值、锁和写入信息CF_DEFAULT
,CF_LOCK
,CF_WRITE
.
CF的所有值编码如下:
默认的 | 锁 | 写 | |
---|---|---|---|
关键 | z {encoded_key} {start_ts (desc)} | z {encoded_key} | z {encoded_key} {commit_ts (desc)} |
价值 | {value} | {国旗}{primary_key} {start_ts (varint)} | {国旗}{start_ts (varint)} |
可以找到更多细节这里.
事务模型
下面是TiKV事务模型的核心,它是由两阶段提交驱动的MVCC。一个事务中有两个阶段:
腹稿
- 事务开始。客户端获取当前时间戳(
startTS
从TSO)。 - 选择一行作为主行,其他行作为辅助行。
- 检查这一行上是否有另一个锁或者之后是否有任何提交
startTS
.这两种情况会导致冲突。如果发生任何一种情况,则提交失败并回滚将被调用。 - 锁定主行.
- 在辅助行上重复上述步骤。
- 事务开始。客户端获取当前时间戳(
犯罪
- 获取提交时间戳
commit_ts
从TSO。 - 检查主行上的锁是否仍然存在。如果锁还在,就继续。如果不是,就回滚。
- 写专栏
CF_WRITE
与commit_ts
. - 清除相应的主锁。
- 在辅助行上重复上述步骤。
- 获取提交时间戳
垃圾收集器
如果没有,很容易预测,如果没有,则会有更多和更多的MVCC版本垃圾收集器删除无效版本。但是我们不能在安全点之前简单地删除所有版本,因为密钥可能只有一个版本,必须保留它。在TiKV,如果有的话把
或者删除
记录在安全点之前,之后的所有写操作都可以删除;否则只有删除
,回滚
和锁
将被删除。