# MVCC

__*MVCC*__

```13
- Transaction 1:
  Begin（start_ts = 100）
    < 1,Tom > -> < 1,Jack >
    < 2,Andy > -> < 2, Candy > 
  Commit;（commit_ts = 110）

- Transaction 2:
  Begin（start_ts = 115）
    < 1,Jack > -> < 1,Tim >
    < 4,Tony > -> < 4, Jerry > 
```
-------------------------------------------------------------
从分布式事务的实现中,我们了解到,如果某行数据正在事务中（未提交）,那么对于它的读取会返回错误。也就是说写操作不仅会阻塞写操作,也会阻塞读操作,虽然在理论上没有问题,但是对于高并发的系统来说,如果某行数据正在事务中,那么对于这行数据的操作马上会变为串行,效率非常低。

解决上述问题的方法叫做多版本并发控制（Multi-Version Concurrency Control,以下简称 MVCC）。像 Oracle,MySQL,PostgreSQ 等数据库也是采用这个方法。下面我们一起来看看 TiKV 是如何使用 MVCC 的。

在 MVCC 技术中,每当想要更改或者删除某个数据对象时,数据库不会在原地去删除或者修改这个已有的数据对象本身,而是创建一个该数据对象的新的版本,这样的话同时并发的读取操作仍旧可以读取老版本的数据,而写操作就可以同时进行。这个模式的好处在于,可以让读取操作不再阻塞,事实上根本就不需要锁。 

在分布式事务部分,我们已经看到了,对于数据的修改或者删除,TiKV 实际上只进行追加操作（PUT,DELETE）,而不是去修改或者删除原有的数据。所以,在 TiKV 中我们是有之前版本的数据的,接下来就是如何找到合适的版本数据的问题了。

下面我们假设一个场景:

目前有 2 个事务,事务 1 已经提交,其 start_ts 为 100,commit_ts 为 110；事务 2 已经开始,但是,没有提交,其 start_ts 为 105。

事务一:

```
Begin（start_ts = 100）
 < 1,Tom > -> < 1,Jack >
< 2,Andy > -> < 2, Candy > 
Commit;（commit_ts = 110）
```

事务二:

```
Begin（start_ts = 115）
 < 1,Jack > -> < 1,Tim >
< 4,Tony > -> < 4, Jerry >
```

此时,我们在 TSO 为 120 的时候,启动一个查询,查询 key = 1,key = 2, key = 4 的值。根据常识,如果没有 MVCC 机制,那么 key = 1 和 key = 4 是返回错误或者被阻塞的,因为其正在事务二中,而 key = 2 可以查询出结果,value = Candy,因为事务一已经完成。

但是,如果有了 MVCC 机制,我们知道,在 TSO 为 120 时的读取,不仅可以读取出 < 2, Candy > ,还可以读出  < 1,Jack > 和 < 4,Tony > ,因为事务一没有提交,我们可以读取提交之前的值。