如何理解Ture Time

半年前看过一下spanner论文,对于其中True time相关的内容相当的疑惑,虽然True Time本身只有3个接口,Wound wait的方式也非常简单,但是因为基础较为欠缺,依然对很多问题很疑惑。

  1. 外部一致性是什么?
  2. 为什么需要时钟?
  3. True Time解决了什么问题

True time的理论非常简单,具体介绍可以参考spanner论文,或者这篇博客:
True Time

1. 外部一致性是什么?

这个词好像只在spanner的论文中看到过,按照论文中的说法就是,事务x结束之后,事务y再开始执行,那么y一定可以看到x的修改。好像挺好理解的,有点儿听君一席话,如听一席话的感觉;好像又不太好理解,这也没有明确的下一个约束的定义。
其实可以把这个外部一致性拆分成两个约束,Serializable(可串行化)和Linearizable(可线性化)。
spanner是一个分布式数据库,整个数据库的数据被被分片到多个shard上,而为了高可用,每个shard中存放了数据的三副本,使用paxos协议来管理三个数据副本,即每个shard是一个paxos group。
其中可串行化的概念来自于数据库隔离级别,其中最高的级别就是可串行化,意味着多个事务并发执行的结果和这些事务一个一个执行的结果是一致的,这是对spanner中分布式事务隔离级别的约束。
可线性化这个概念和事务无关,现在忘掉事务,只考虑单纯的get和set,没get都能读取到之前成功set的值,就是可线性化,即每次保证能够读取到新数据,因为spanner中数据是多副本存在的,读取可能从多个副本中任意读取,可线性化意味着,每次向一个shard读取数据,不论读的哪个副本,都能读取到最新值。
可线性化+可串行化=外部一致性

并发的顺序系统一般都是不做约束的,事务1发起,事务2发起,事务1结束,事务2结束,那么不保证事务2一定可以看到事务1的修改,这是并发。

2. 为什么需要时钟

时钟是为事务服务的,现在可以忘掉paxos,多副本,可线性化相关的问题。
就单机数据库而言,要实现可串行化的隔离级别,在不考虑性能的前提下,最简单的方案就是SS2PL,读写数据加锁就行,通过锁来互斥有冲突的事务,让它们最终可以有序执行,达成可串行化。
但是这样的实现性能很成问题,所以大多数数据库都进行了优化,引入了数据多版本(Multi Version),以此来优化读事务,即读数据读的是快照数据,不受写的影响,写数据是增加新的版本,不是在原来的数据上做修改,不影响旧版本数据读取,读不阻塞写,写不阻塞读。
既然有了多版本,那么就需要版本号,在单机数据库上很好处理这个事情,每个事务有互不相同ID,一般来说事务id是递增发放的。那么写事务写的新数据版本号就是该事物的id。那么读取数据时又该怎么判断哪些数据是可以读取的呢?这个和隔离级别有关,比如在read commited隔离级别下,可以在每次查询语句执行时取一下当前数据库中最大已提交的事务id,该事务就应该读取所有版本号不大于该id的数据中的最新版本,就能读取到已提交的最新数据,如果是read repeated,那就在读事务开始的时候去一次当前数据库的最大已完成事务id即可,后续的所有查询语句都带上该id,就能保证查询到的是同一个时间节点的快照数据。
说完了多版本,其实时钟就是为多版本服务的,刚才介绍多版本数据的版本号时使用事务id举的例子,但是在分布式数据库中,没法像单机数据库中使用事务id号作为版本号,除非搞个中心发号器,那rpc开销就大了,而且也存在单点问题,发号器崩了集群就完蛋,达不到分布式容错的目的。所以一般在分布式数据库中一般使用时间戳来作为版本号,通过时钟来给数据库中的数据定序,判断可见行。

3. True Time解决了什么问题

spanner的目标就是提供一个事务型,且保证外部一致性的数据库。写事务通过二阶段事务中加索(2PL)来实现,读事务通过多版本数据来实现快照读。看起来很美好,问题是分布式数据库中没法直接用事务id来作为版本号,所以在spanner中使用了时间戳作为版本号。
先来考虑一下,如果使用普通的时间戳来作为版本号,会存在什么问题?一般服务器会使用NTP协议来同步时钟,根据网络情况的不同,不同机器间的时钟会有一些误差,误差一般在百毫秒级。具体的数字先不管,我们知道服务器的时钟不准就行了,有的快有的慢,这会导致什么问题呢?比如有个服务器A时钟比较快,比绝对时钟快10分钟,服务器B的时钟和绝对时钟一致,那么在0(A:10,B:0)分钟的时候,有个客户端想A发起一个写事务W,然后提交,不考虑网络延迟和执行时间,那么A的提交时间就是10,提交W后返回给客户端,完成了该事物W。一分钟后,另一个客户端发起一个读数据的事务R,先在B上读取一批数据,B给给这个读事务分配了一个时间戳1(当前绝对时间),并把数据和时间戳返回给客户端,客户端该事务的后续读请求都带上该时间戳,以保证可重复读(同一个事物用于都读同一个版本),那么客户端带上1这个时间戳去A上读,并不能在A上读取到W写入的数据,因为W写入的数据版本号是10,不满足外部一致性(R在W结束之后才开始,但是并没有读取到W的写入)
在这里插入图片描述
具体的反例还有很多,简单的说就是存在一些问题:
(1)时钟太快,把数据提交在了未来,时钟慢的机器根本就读不到提交的数据
(2)时钟太慢,某些事物的协调者时钟太慢(数据的版本时间戳由二阶段提交的协调者确定),导致后提交的事务的时间戳比先提交事务的时间戳小,后提交进去的数据变成了旧数据。
对于二阶段提交的事务,可以尝试多种提交时间戳的选择(选协调者的时间,选所有参与者中最大的时间等),但是因为不同服务器的时钟之间存在误差,都会遇到上述的两个问题,就不一一举例子了。

现在来看一下True Time是怎么解决时钟存在误差的问题的。首先spanner中使用了原子钟和GPS时钟,这样spanner的所有数据中心之间的时间误差都能控制在很小的范围内,论文中表明误差不超过7ms。True Time提供了如下三个接口:

MethodReturns
TT.now()TTinterval: [earliest, latest]
TT.after(t)true if t has definitely passed
TT.before(t)true if t has definitely not arrived

这三个接口的返回结果可以保证和语义绝对一致。调用了now(),会返回一个时间段,表明当前绝对时间一定在该范围内,after表明当前绝对时间一定超过了给定时间t,before表明当前绝对时间一定没有超过给定时间t。

spanner中提交时间遵循一下规则:

  1. 提交时间一定大于所有参与者prepare的时间
  2. 提交时间一定大于协调者本地时间,即选择MAX(TT.now().lastest, PT1, PT2…)作为提交时间,其中TT.now().lastest是在获取协调者上当前可能的最大时间,PT为prepare time,分别表示每个参与者prepare事务的时间
  3. 一定要等到TT.after©返回true时才能提交。(C表示协调者选定的提交时间)

来看个简单的例子,有S1,S2,S3共3个服务器,Tabs表示标准时间轴,其中事务T1有两个参与者,S1和S2,事务T2有两个参与者S2和S3,两个事务的协调者都是S2,现在发起T1事务,首先在两个参与者节点上prepare,S1的PT(prepare time)是15,S2的是7,prepare结束后都汇报给协调者S2,S2获取了两个PT分别是15和7,按照第一条规则,选择最大的PT,即15,然后在本地调用一次TT.now(),S2的本地时钟是7,假设TT.now().latest值为14,那么该选择的时间戳就是MAX(15,14)=15,然后按照第三条规则,等待到TT.after(15)返回true时,才能进行提交,最终可能在绝对时间为19时,完成T1提交。T2同理。
在这里插入图片描述
现在来看看上面的三个约束:
第一个条件很好理解,二阶段提交,提交时间肯定应该晚于所有参与者prepare的时间,这是符合认知的
第二个,提交时间一定大于协调者本地时间,这个也很好理解,总不能选择一个时间戳小于本地时间吧,这不就相当于写了一个旧的数据到数据库里面吗?(改变过去不符合逻辑)
第三个,即一定要等到绝对时间已经超过了选定的提交时间戳C之后才能提交。TT给定的语义是绝对准确的,一个服务器调用TT.after©返回True,那么集群中所有节点调用该接口都一定返回True,即表示,当等待到TT.after©返回True时,集群中所有节点的时间都已经超过了C,这就能保证,在该事务提交之后,再发起的新事务时间戳一定大于C,后续的新事务一定可以看到该事务的修改。


论文中的TT保证外部一致性(此处不考虑paxos多副本)的证明如图,s1是事务1选择的提交时间戳,tabs(e1commit)表示事务1提交的绝对时间,这是有第三条约束保证的,s1一定小于事务1提交的绝对时间,事务1的提交时间小于事务2的开始时间,这是外部一致性的假设,事务2的开始绝对时间一定小于提交时的绝对时间(因为运行了一段时间),提交时间一定要大于TT.now().latest,这就保证了事务2的提交时间s2大于当前的绝对时间,最终推理出s1<s2,所以事务2一定可以看到事务1的修改。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值