xiaohanliang
Database
Database
  • review
  • MySQL
    • 0. 索引就是树吗
    • 1. 索引是怎么发挥作用的
    • 2. 聊聊怎么估时间
    • 3. 主键玄学
    • 4. 事务的执行过程@ACD
    • 5. MVCC就是用undolog回退
    • 6. 事务如何彼此隔离@I
    • 7. 如何人为的制造死锁
    • 8. 引擎是干什么的
    • 9. 主从复制怎么做到的
    • 10. 寻找同步起始点GTID
    • 11. 预解析Statement
  • Redis
    • 0. Hash@数据结构
    • 1. ZSet@数据结构
    • 2. 场景与玩法@数据结构
    • 3. 可能的风险点
    • 4. Redis的落盘
    • 5. 关于效率的讨论
    • 6. 主从模式@集群化
    • 7. [WIP]Proxy实现@集群化
    • 8. 标准玩法@集群化
  • KV/Distributed
    • 0. 面临的问题
    • 1.Raft@原理
    • 2. LSM@原理
    • 3. [WIP]分布式事务@原理
Powered by GitBook
On this page
  • 如何论证: 事务的先后关系?
  • 两个原则怎么就解决了这个问题?
  • 一些思考
  • Reference

Was this helpful?

  1. KV/Distributed

3. [WIP]分布式事务@原理

Previous2. LSM@原理

Last updated 3 years ago

Was this helpful?

描述一下我们的基本矛盾 , 因为数据越来越大, 所以一定会将数据切成好多分片, 存放在不同的实力上. 而同样的一个分片, 也会被存好几份, 其中的某一份被称作是主分片, 剩下来的几份就是副本, 一方面这些副本用来容灾, 另一方面副本也\可以作为只读, 用来提升读的效率.

如何论证: 事务的先后关系?

假设我们有三个服务器 [Server1(比客观时间快5ms) / Server2(比客观时间慢4ms) / Server3(比客观时间慢2ms)], 在上面发生了两个事务 $T_1$ (先发生) / $T_2$ (后发生), 那么在数据库记录下这两个事务的时间戳的时候, ${S}_2$应该大于${S}_1$. 因此, 我们只需要证明: 已知 $T_1$的提交时间早于$T_2$的开始时间, 求证 $S_1 < S_2$. 为了能满足这个性质, 要求:

  • $S_i$应当大于[所有参与者的] 本地开始时间, 并且需要大于协调者的 TT.Now().Latest: Start原则

  • 真正提交的时间必须在 $S_i$ 之后, 即真正提交时间大于记录下的时间戳: Cond Wait原则

如果使用这个规则:

  • $T_1$ 开始的时候:

    • Server1的本地时间是15ms, Server2(协调者)的本地时间是7ms, 按照Start原则$S_1$会被选择为 max(7+7, 15) = 15

    • $T_1$的真正执行时间要在15之后我们假设是17ms

  • $T_2$开始的时候:

    • Server2(协调者)的本地时间是12ms, Server3的本地时间是13ms, 按照Start原则$S_2$会被选择为 max(12+7, 13) = 19

    • $T_2$的真正执行时间要在19之后我们假设是21ms

  • 结论:

    • 即使很快的就把事务做完了, 但为了协调时间, 我们故意把真正的提交时间滞后了

    • 但这样能做到事务记录时间上的有序化

如果不使用这个规则:

  • Server1的本地时间是15ms, Server2(协调者)的本地时间是7ms, 直接选择15ms作为T1的执行时间S1

  • Server2(协调者)的本地时间是12ms, Server3的本地时间是13ms, 直接选择13ms作为T2的执行时间S2

  • 即使我们都知道Server2开始的更晚, 但记录下的时间, 却是Server2更早, mess

两个原则怎么就解决了这个问题?

  1. 已知客观时间上有: $t{abs}(T_1^{commit}) < t{abs}(T_2^{start})$

  2. 由于我们进行了CondWait, 所以记录下的时间戳 $S1 < t{abs}(T_1^{commit}) $

  3. 而时间戳 $S2$ 一定大于$t{abs}(T_2^{start})$, 因为很显然只有$T_2$开始了之后, 才能提交产生$S_2$

  4. 因此 $ S1 < t{abs}(T1^{commit}) < t{abs}(T_2^{start}) < S_2 $

第三点的解释:

  • 时间戳$S2$的选择: TrueTime区间的最大值 TT.Now().Latest, 因此我们能做到S2大于 $t{abs}(T_2^{start})$

  • 事实上我们不仅选择了协调者本身的 TT.Now().Latest,甚至考虑到所有参与者开始时间, 请注意S2的选择虽然是提交时间, 但我们为了保证大于T2的开始时间, 因此Start原则里max的是开始时间. 所以我们一定能得到 $ t_{abs}(T_2^{commit}) \le S_2$ .

  • 此时根据传递性, $ t{abs}(T_2^{start}) \lt t{abs}(T_2^{commit}) \le S_2$

一些思考

我们在最上面的图中可以看到, 事实上我们已经把所有事务, 在协调者身上串行化(Linearized) 处理了, 这意味着如果两个事务涉及修改到同一条数据, 那么这两个事务一定得是串行的, 相对的, 如果两个事务并不涉及同一条事务, 那么他们可以并行, 因为Merge的时候并不会出问题. 另外因为我们要等两个 ε , 一个是选择的提交时间TT.Now().Latest, 另一个是TT.After($S_i$)的, 因此等待成本还是挺大的. 根据论文里的数据, 正常来说一个 ε 有90%概率会在1ms之内. 串行的吞吐量是每秒125个.

但这事实上给我们一种可能, 就是集群中的所有节点都可以参与写操作. 这是一个很不一样的设计.

Reference

Spanner的TrueTime与分布式事务
关于Spanner中的TrueTime和Linearizability
计算机的时钟(四):TrueTime