# 分布式事务（C）

![left](./student-guide/diagram/03-distributed-02-01.png)

+ write 列：当用户写入了一行数据时，如果该行数据长度小于 255 字节，那么会被存储 write 列中，否则的话该行数据会被存入到 default 列中。
+ default 列：用于存储超过 255 字节长度的数据。
-------------------------------------------------------------
假设我们要完成的事务如下：
```
Begin
 < 1，Tom > -> < 1，Jack >
< 2，Andy > -> < 2, Candy > 
Commit;
```
注：假设 key = 1，key = 2 分别在不同 TiKV 的 region 上。
首先，事务开始的时候，我们从 PD 中拿到了一个 startTS，假设为 100，我们进入 Percolator 的第一个阶段 PreWrite，这个时候，由于没有 commit，所以需要写入 Lock 和 Default 两个列族（CF）即可，从图中可以看出，在写入 Default 时候，我们将 key 加上了时间戳（startTS）, 两个修改的 Lock 列族（CF）的 value 也不太一样，主要包括 lock type 和 startTS 等。
如下图：
```
                                         Key = 1
        Default CF                   Lock CF                       Write CF
     key        value            key        value             key        value
     1_100       Jack            1       W, primary key,100...


                                        Key = 2
        Default CF                    Lock CF                       Write CF
     key       value             key         value            key             value
    2_100      Candy              2    W, @ key = 1,100...
```
（W：表示写入，D：表示删除）
这里有个点很重要，也就是事务中修改 key = 1 和 key = 2，在 Lock 列族 （CF） 中，我们只将第一个修改也就是 key = 1 作为 primary key，而 key = 2 的锁是指向 key = 1 的。（即， Lock 列族 （CF）中的 value）
同时，我们也注意到 Default 列族（CF），无论是修改还是删除数据，都是新插入键值对，而不是去修改或者删除已经存储的数据，这点在后面介绍 MVCC 技术的时候，还会介绍到。
其次，接下来我们进入 Percolator 的第二个阶段 Commit，这个时候会从 PD 中拿到一个 commitTS，假设为 110，之后会在 Write 列族（CF）中写入，key 加上时间戳（commit_ts）, value 就是这个事务的 startTS, 在 Lock 列族（CF）记录 primary key 被删除。
如下图：
```
                                      Key = 1
           Default CF                 Lock CF                    Write CF
     key           value        key          value           key          value
    1_100           Jack         1    W, primary key,100...
                                 1    D, primary key,100... 1_110       start_ts=100

                                       Key = 2
            Default CF                Lock CF                     Write CF
     key           value        key          value           key           value
    2_100          Candy         2     W, @ key = 1,100...
                                 2     D, @ key = 1,100...  2_110        start_ts=100
```
（W：表示写入，D：表示删除）
Commit 阶段完成后，当我们要读取数据时，会发现无论是 key = 1 还是 key = 2，我们只要查找对应的 Lock CF 中是否有锁即可，如果没有锁或者锁被删除（标志为 D），则可以读取，否则读取就会失败。

最后，我们发现，Percolator 的 commit 阶段我们只必须删除一个  key = 1 的 primary key 就可以了，因为 key = 2 的 Lock CF 中存储的是指向 key = 1 的 primary key 的指针。那么假设，如果在 commit 阶段，由于错误，只删除了 key = 1，没有来得及删除 key = 2，在恢复以后，当读取到 key = 2 时，发现 Lock CF 上有锁（由于错误没有被删除）指向 key = 1，而 key = 1 对应的锁已经被删除，我们就知道在错误前没有来得及更新（删除） key = 2 的锁，但实际上事务已经提交成功了。于是，就解决了之前提到的分布式事务的问题。

我们这里只是简单介绍了 Percolator 模型，实际的实现还会比较复杂，我们也忽略了一些细节，如果要详细了解，可以参考 分布式事务课程 中的内容。