spanner,mit6.824论文解读

为什么选择这篇论文(Google Spanner, OSDI 2012)?

  • 宽域分布式事务的罕见示例。
    • 非常理想。
    • 但是二阶段提交被视为太慢并且容易阻塞。
  • 宽域同步复制的罕见示例。
  • 巧妙的想法:
    • 通过Paxos进行的两阶段提交。
    • 同步时间用于快速只读事务。
  • 在Google内部广泛使用。

动机用例是什么?

  • Google F1广告数据库(第5.4节)。
  • 之前分片在许多MySQL和BigTable DBs上;笨拙。
  • 需要:
    • 更好的(同步)复制。
    • 更灵活的分片。
    • 跨分片事务。
  • 工作负载主要由只读事务主导(表6)。
  • 需要强一致性。
    • 外部一致性/线性一致性/可串行化。

基本组织结构:

  • 数据中心A:
    • “客户端”是Web服务器,例如用于gmail
    • 数据在多个服务器上分片:
      • a-m
      • n-z
  • 数据中心B:
    • 拥有自己的本地客户端
    • 以及数据分片的自己的副本
      • a-m
      • n-z
  • 数据中心C:
    • 相同的设置
  • 通过Paxos管理复制;每个分片一个Paxos组。

    • 副本位于不同的数据中心。

    • 类似于Raft – 每个Paxos组都有一个领导者。

    • 如实验室中一样,Paxos复制操作日志。

为什么采用这种安排?

  • 分片通过并行性允许巨大的总吞吐量。
  • 数据中心独立失败 – 不同的城市。
  • 客户端可以读取本地副本 – 快速!
  • 可以将副本放置在相关客户附近。
  • Paxos只需要多数 – 容忍慢的/远程的副本。

面临的挑战是什么?

  • 读取本地副本必须获得最新数据。
    • 但是本地副本可能不反映最新的Paxos写入!
  • 一个事务可能涉及多个分片 -> 多个Paxos组。
  • 读取多个记录的事务必须是可串行化的。
    • 但是本地分片可能反映了不同子集的已提交事务!

Spanner对读/写事务和只读事务进行不同的处理。

  • 首先,读/写事务。

    • 读/写事务示例(银行转账):

    • BEGIN
          x = x + 1
          y = y - 1
        END
      
    • 我们不希望任何读取或写入x或y在我们两个操作之间偷偷进行。提交后,所有读取都应该看到我们的更新。

  • 总结:使用Paxos复制参与者的两阶段提交(2pc)。

    • (现在省略时间戳。)(这适用于读/写事务,而不是只读事务。)
    • 客户端选择一个唯一的事务ID(TID)。
    • 客户端将每个读取发送到相关分片的Paxos领导者(2.1)。
      • 每个分片首先在相关记录上获取锁。(可能必须等待)。
      • 每个分片的领导者都有单独的锁表。
      • 通过Paxos不复制读锁,所以领导者失败 -> 中止。
    • 客户端保持写操作私有直到提交。
    • 当客户端提交(4.2.1)时:
      • 选择一个Paxos组充当2pc事务协调器(TC)。
      • 将写操作发送到相关分片领导者。
      • 每个被写入的分片领导者:
        • 在被写入的记录上获取锁。
        • 通过Paxos记录一个“准备”记录,以复制锁和新值。
        • 告诉TC它已准备好。
        • 如果崩溃从而丢失锁表则告诉TC“不”。
    • 事务协调器:
      • 决定提交或中止。
      • 通过Paxos将决定记录到其组。
      • 告诉参与者领导者和客户端结果。
    • 每个参与者领导者:
      • 通过Paxos记录TC的决定。
      • 释放事务的锁。
  • 只读(r/o)事务

    • Spanner为r/o事务消除了两个大成本:

      • 从本地副本读取,避免Paxos和跨数据中心消息。
        • 但请注意,本地副本可能不是最新的!
      • 没有锁,没有两阶段提交,没有事务管理器。

        • 再次避免跨数据中心消息到Paxos领导者。
        • 并避免减慢读/写事务。
      • 表3和表6显示结果是10倍的延迟改善!

      • 如何与正确性相协调?

    • r/o事务的正确性约束:

      • 可串行化:

        • 结果与事务逐一执行相同。
          • 尽管它们实际上可能并发执行。
        • 即r/o事务必须本质上适合在r/w事务之间。
        • 看到之前事务的所有写入,之后事务的没有。
      • 外部一致性:

        • 如果T1在T2开始之前完成,T2必须看到T1的写入。
          • “之前”指的是实际(挂钟)时间。
          • 类似于线性一致性。
          • 排除了读取陈旧数据。

为什么不让r/o事务只读取最新提交的值?

  • 假设我们有两次银行转账,和一个读取两者的事务。

    • T1:  Wx  Wy  C
      T2:                 Wx  Wy  C
      T3:             Rx             Ry
      
    • 结果将不符合任何串行顺序!

      • 不是T1, T2, T3。
      • 也不是T1, T3, T2。
    • 我们希望T3看到T2的所有写入,或者一个也不看。

    • 我们希望T3的读取全部在与T1/T2相同的点发生。

  • 想法:快照隔离(SI):

    • 同步所有计算机的时钟(到实际挂钟时间)。

      • 给每个事务分配一个时间戳。

        • 读/写:提交时间。
        • 读/只:开始时间。
      • 执行时,仿佛按时间戳顺序逐一进行。

        • 即使实际读取顺序不同。
      • 每个副本存储每条记录的多个时间戳版本。

        • 一个读/写事务的所有写入获得相同的时间戳。
    • 一个读/只事务的读取看到的是事务时间戳时的版本。

      • 记录版本的时间戳小于事务的,且是最高的。
  • 我们的例子使用快照隔离:

    •     x@10=9         x@20=8
          y@10=11        y@20=12
      T1 @ 10:  Wx  Wy  C
      T2 @ 20:                 Wx  Wy  C
      T3 @ 15:             Rx             Ry
      
    • “@ 10”表示时间戳。

      • 现在T3的读取将都来自@10的版本。
      • T3不会看到T2的写入,即使T3的读取发生在T2之后。
    • 现在结果是可串行化的:T1 T2 T3
      • 串行顺序与时间戳顺序相同。
    • 为什么T3读取y的旧值是可以的,即使有一个更新的值?
      • T2和T3是并发的,因此外部一致性允许任何顺序。
      • 记住:只读事务需要读取它们时间戳时的值,不看到之后的写入。
  • 问题:如果T3从一个还没看到T1写入的副本读取x会怎样?

    • 因为该副本不在Paxos多数派中?
    • 解决方案:副本“安全时间”。

      • Paxos领导者按时间戳顺序发送写入。

        • 在时间20提供读取服务之前,副本必须看到时间>20的Paxos写入。
          • 所以它知道它已经看到了所有<20的写入。
      • 如果有准备好但未提交的事务也必须延迟(第4.1.3节)。

      • 因此:只读事务可以从本地副本读取 – 通常很快。

  • 问题:如果时钟不是完美同步会怎样?

    • 如果时钟没有完全同步会出现什么问题?

      • 对于读/写事务,使用锁,没有问题。
        • 如果只读事务的TS太大:
          • 它的TS将高于副本安全时间,读取将被阻塞。
          • 正确但慢 – 由于时钟误差增加了延迟。
        • 如果只读事务的TS太小:
          • 它会错过在只读事务开始之前提交的写入。
            • 因为它的低TS将导致它使用记录的旧版本。
            • 这违反了外部一致性。
  • 如果只读事务的TS太小的问题示例:

    • /T0 @  0: Wx1 C/T1 @ 10:         Wx2 C
        只读 T2 @  5:                   Rx?C表示提交)
      
    • 这将导致T2读取时间为0的x版本,即1。但T2在T1提交(实时)之后开始,因此外部一致性要求T2看到x=2。所以必须有解决不正确时钟可能性的方案!

Google的时间参考系统(第5.3节)

  • [UTC, GPS卫星, 主服务器, 服务器, TTinterval]
  • 每个数据中心有几个时间主服务器。
  • 每个时间主服务器要么有一个GPS接收器,要么有一个“原子钟”。
  • GPS接收器通常精确到微秒级别。
  • 论文没有说明它所说的原子钟是什么意思。
    • 可能与GPS同步,但在没有GPS的情况下也能准确一段时间。
    • 如果是自由运行,误差会累积,可能是每周几微秒。
  • 其他服务器与几个附近的时间主服务器通信。
    • 由于网络延迟、检查之间的漂移而产生不确定性。

TrueTime

  • 时间服务产生一个TTinterval = [最早时间, 最晚时间]。
  • 正确的时间保证在这个区间内。
  • 区间宽度根据测量的网络延迟、时钟硬件规格计算而来。
  • 图6:间隔通常是微秒级,但有时超过10毫秒。
  • 因此:服务器时钟并非完全同步,但TrueTime提供了服务器时钟可能有多错的保证界限。

如何确保如果读/写事务T1在只读事务T2开始之前结束,那么TS1 < TS2。

  • 即,确保只读事务的时间戳不会太小。
  • 两条规则(4.1.2):
    • 开始规则:
      • 事务TS = TT.now().latest (获取最新时间)
        • 对于只读事务,是在开始时
        • 对于读/写事务,是当提交开始时
    • 提交等待,对于读/写事务:
      • 在提交之前,延迟直到TS < TS.now().earliest
      • 保证TS已经过去。

使用间隔和提交等待更新的示例:

  • 情景是T1提交,然后T2开始,T2必须看到T1的写入。

  • 即,我们需要TS1 < TS2。

    • /T0 @  0: Wx1 C
            			|1-----------10| |11--------------20|/T1 @ 10:         Wx2 P           C
            							|10--------12|
      只读 T2 @ 12:                           Rx?
      (P表示准备)
      
    • C保证在其TS(10)之后发生,由于提交等待。

    • Rx在C之后发生,因此在时间10之后。

    • T2选择TT.now().latest,这在当前时间之后,即在10之后。

    • 所以TS2 > TS1。

为什么这提供了外部一致性:

  • 提交等待意味着读/写TS保证在过去。
  • 只读TS = TT.now().latest保证>=正确时间
    • 因此>=任何之前提交事务的TS(由于其提交等待)

更一般地:

  • 快照隔离为您提供可串行化的只读事务。
    • 时间戳设定一个顺序。
    • 快照版本(和安全时间)实现了在时间戳处一致的读取。
    • 事务看到来自低TS(时间戳)事务的所有写入,来自高TS事务的则没有。
    • 如果你不关心外部一致性,任何数字对于TS来说都可以。
  • 同步的时间戳产生外部一致性。
    • 即使在不同数据中心的事务之间也是如此。
    • 即使从可能滞后的本地副本读取也是如此。

这一切为什么有用?

  • 快速的只读事务:
    • 从客户端数据中心的副本读取。
    • 无锁定,无两阶段提交。
    • 因此,在表3和表6中实现了10倍的延迟改善。
  • 尽管如此:
    • 由于安全时间,只读事务读取可能会阻塞,以追赶。
    • 读/写事务的提交可能会在提交等待中阻塞。
    • 精确(小间隔)时间最小化这些延迟。

总结:

  • 很少看到部署的系统提供跨地理分布的数据的分布式事务。
  • Spanner是一个令人惊讶的证明,它可以是实用的。
  • 时间戳方案是最有趣的方面。
  • 在Google内部广泛使用;一个商业Google服务;有影响力。
  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

poison_Program

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值