# `MVCC`：我们应该尊重彼此。

`MVCC`：我们应该尊重彼此
```
~!@// 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 > 
```

------------------------------------------------------------------------------
在分布式事务的实施中，我们学习到，如果数据更改未提交，读取它将返回错误。这意味着写操作不仅阻塞其他写操作，还阻塞读操作。虽然从理论上讲这不是问题，但在具有高并发性的系统中可能效率低下。如果在事务中更改了特定行的数据，该行数据的操作将变为串行，这可能会降低性能。

解决上述问题的方案叫做多版本并发控制（简称MVCC）。

MVCC技术中，每次想要更改或删除某个数据对象时，数据库并不会直接在原处删除或修改现有的数据对象，而是创建该数据对象的新版本。这样，同时的读取操作仍然可以读取旧版本的数据，而写入操作也可以同时进行。这种模式的好处是它允许读取操作不再被阻塞；实际上，根本不需要锁定。

在分布式事务部分，我们已经看到，对于数据修改或删除，TiKV实际上只执行附加操作（PUT，DELETE），而不是修改或删除原始数据。因此，在TiKV中，我们有上一个版本的数据，那么下一个问题是如何找到正确版本的数据。

让我们假设下面的场景：

目前有两个事务，事务1已提交，其`start_ts`为100，`commit_ts`为110；事务2已开始但尚未提交，其`start_ts`为105。

事务1工作流程：
```
Begin（start_ts = 100）
< 1,Tom > -> < 1,Jack >
< 2,Andy > -> < 2, Candy > 
Commit;（commit_ts = 110）
```

事务2工作流：
```
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会返回错误或被阻塞，因为它们在事务2中，而key=2可以查询到结果，值为Candy，因为事务1已经完成。

然而，通过MVCC机制，我们知道，一个具有时间戳120的读取不仅可以读取<2，Candy>，还可以读取<1，Jack>和<4，Tony>，因为事务一还没有提交，我们可以在提交之前读取这些值。