分布式数据库中的事务时序

概述

在单机数据库领域,咱们为每一个事务都分配一个序列号,好比Oracle的SCN(SystemChangeNumber),MySQL的LSN(LogSequenceNumber),这个序列号能够是逻辑的,也能够是物理的。咱们依赖这个序列号对系统中发生的事务进行排序,确保全部事务都有严格的前后关系。数据库中全部的事务都按分配的序列号排序,对于任什么时候间点发生的读,保证能读到这个时间点以前的提交的事务,而且读不到以后发生的事务。因此,通常来讲,不管系统中序列号是逻辑仍是物理的,都与真实的物理时间有一个对应的单独递增关系。在单机数据库时代,这个相对容易作到,系统中有一个惟一的序列号分配器,保证有序。算法

来到分布式数据时代,一个数据库系统再也不只有一个节点能够处理事务,多个节点上发生的事务如何保证有序是本文想要讨论的问题。最简单的想法是,咱们在数据库系统中专门添加一个组件,这个组件做用就是分配时间戳,付出的代价是,任何一个事务提交,都须要有一个网络RTT的消耗,而且分配时间戳的组件是整个系统的单点,可能成为系统的瓶颈。实际上,目前主流分布式数据库系统还提供了两个可选方案,一种是Spanner数据库的TrueTime机制,另一种CockroachDB数据库的HLC(HybridLogicClock)机制。数据库

HLC机制

先说说HLC的由来,咱们前面提到分布式数据库中,要为每一个事务都分配一个合理的序列号比较麻烦,实际上这不仅仅是数据库的问题,仍是全部分布式系统中的共性问题,如何为系统中发生的事件排序。既然各个节点的物理时钟不一致,不如都采用逻辑时钟(LogicClock),逻辑时钟只保证因果一致性,即不保证全局有序,只保证有前后顺序的事件有序。哪些事件有前后关系,主要包括两类,1.单节点内部前后发生的事件;2.节点间有通讯的事件,发送消息的节点必定早于接收消息的节点。因为分配的序列号与物理时钟彻底无关,真实时间没法与序列号对应,致使没法用于实际的生产环境。HLC源于LC,是对LC的改进,一样是保证因果序,但引入了物理时间戳做为序列号的一部分,这样能与物理时钟对应起来,采用物理时钟+逻辑时钟混合的方式做为序列号,提供一种事务序列号的分配方法。网络

接下里咱们看看HLC是怎么实现的?HLC分配算法很简单,源于[论文](https://cse.buffalo.edu/tech-reports/2014-04.pdf)。并发

              

l.j表示本地HLC,pt.j表示物理时间戳,c.j表示逻辑时间戳,对于发送事件或是本地事件,递增逻辑时间戳部分,确保本地事件有序;对于接收事件节点,取本地时间戳和发送节点时间戳的最大值,若是物理时间戳部分相同,则递增逻辑时间戳部分。整个逻辑是简单清晰的,回归到数据库系统,则是事务提交时,若是是本地事务,则取本地HLC和本地物理时间的最大值,避免时钟跳变;对于分布式事务,则选择多个参与者节点中最大的HLC,而且推高各个节点的本地HLC,从而保证事务的序列号HLC一直都是单调递增的。分布式

在分布式数据库领域引入HLC算法能为每一个事务都分配一个合理序列号,而且保证有因果关系的事务经过序列号就能肯定。在数据库领域,怎么定义因果关系,对于单节点事务而言,若是事务严格时间前后发生顺序(事务A提交后,事务B才开始),或者存在依赖关系(好比更新同一行),则认为存在因果关系;若是事务能并发执行提交,但不存在依赖关系,则不存在因果关系,事务序列号分配没有约束。对于跨节点分布式事务而言,若是涉及到节点更新与其它事务有依赖关系,则存在因果序,不然没有。数据库系统引入HLC机制后,可以保证有依赖关系的事务,分配的序列号也必定有前后顺序。HLC由物理时钟+逻辑时钟两部分组成,论文中建议48位做为物理时钟位,16位做为逻辑时钟位,那么提供的时钟精度大约是15微妙,每一个微妙的逻辑时钟LC能够跳变65536次。性能

TrueTime机制

前面提到了TrueTime机制,在对比两种分配序列号机制以前,先简单介绍下TrueTime。咱们之因此使用LC,HLC主要缘由是咱们各个节点的物理时钟不一致,咱们没法按单机思惟来解决分布式系统问题。TrueTIme机制是经过在集群中引入原子钟和GPS等硬件设备,来确保集群中各个节点的物理时钟偏差在必定范围内,配合特殊的事务commit-wait逻辑,保证全局的事务有序。spa

咱们看一个问题,假设集群最大物理时钟偏差是100,用户前后执行了两个事务A和B,分别在节点Node1和Node2上提交,Node1的物理时钟比Node2物理时钟快70,事务A的提交时间物理时间戳为120,记为t1;事务B的提交物理时间戳为55,记为t2,显然从时间戳来看t1>t2,可是事务A却先于事务B发生,这显然与实际状况不符。因为事务A和B并无任何交集,按咱们的定义,它们之间没有因果关系,因此HLC机制不保证最终分配的时间戳HLC(A) < HLC(B)。3d

如今看看TrueTime机制如何给两个事务排序。假设事务A提交时,经过TrueTime-API得到的是一个时间戳是一个范围,好比[t1-ε1,t1+ε1],事务B开始时,获取的时间戳范围是[t2-ε2,t2+ε2],因为事务A先于事务B发生,所以须要能保证t1+ε1 < t2-ε2,t2-t1 > ε1+ε2,那么显然只要事务提交时,等待(ε1+ε2)时间,那么必定能知足t1+ε1 < t2-ε2,也就是事务A提交时间戳<事务B提交的时间戳。实际上TrueTime机制保证的时间戳偏差最大在7ms,那么事务提交时,则必须等待大概14ms,才能保证事务有序。对于分布式事务,事务会跨多个节点,事务的时间戳会选取几个节点中最大的时间戳。所以,实际上对于集群中任何一个节点不管是本地事务仍是分布式事务,发生的前后顺序都与时间戳顺序一致,也就能保证所谓的外部一致性。TrueTime机制经过commit-wait的模式经过牺牲延迟,保证了全局顺序。blog

TrueTime机制引入了特殊的硬件设备,外加commit-wait机制,经过牺牲必定的latency,来保证全局事务的顺序。HLC机制对于写请求则相对轻量,尤为是本地事务,没有任何网络交互,也不须要额外的时间等待,不一样节点上有前后顺序事务,实际上分配的序列号没有严格的前后顺序,只能保证因果序。排序

全局序VS因果序

若是采用专门的事务序列号分配组件服务,好比TSO(TimeStamp Oracle),显然全部事务都是有序的,相似于单机数据库;若是是TrueTime机制,对于任何一个时间戳,读取的快照偏差都在指定的范围(7ms之内),结合commit-wait,也能保证全局有序;而对于HLC机制,因为并无解决物理时钟问题,要控制偏差可能须要借助NTP等服务,固然这个偏差可能在150ms或更多,不如原子钟和GPS精确,若是采用commit-wait机制,事务延迟过大,就没法用于实际生产环境了,因此没法提供全局序。那么全局序和因果序对于读有什么影响?对于快照读,HLC机制若是只根据本地HLC时间戳生成,那么由于时钟偏差,可能会漏掉部分已提交的事务;若是与全部节点通讯并获取HLC,那么至少当前的这个快照是能读到全部已提交的事务的,但读性能开销就比较大了(与全部节点都有网络交互)。固然,这个就看场景了,好比要建全局索引,经过与全部节点通讯获取HLC,实际上就至关于与全部节点都有因果关系,间接获得了一个全局一致性快照。这个快照点之前的事务均可以读取到,这个点之后的事务都不读到。显然,全局序更贴合实际应用场景,但咱们也能够具体问题具体分析,不能说只提供因果序的系统没有价值。

总结

数据库系统中,给事务分配合理的序列号,须要与实际发生的事务前后顺序保证单调递增关系。这个需求在单机数据库时代很容易知足,由于只有一个时钟源。进入分布式数据库时代,因为一个数据库系统中有多个节点,每一个节点都须要承担分配事务序列号的任务,但自然的各个节点时钟存在偏差,致使须要引入特殊的事务序列号分配机制,好比HLC机制,或者TrueTime机制。TrueTime机制本质就是硬件方案,将集群中节点间的物理时钟偏差控制在很小的范围内,结合commit-wait模式,保证全部事务全局有序,理论上要保证全局有序,TrueTime并不是是充分条件,只须要commit-wait便可,就看wait的时间长短了。HLC机制则是软件方案,只保证事务因果序,而且解决了本地时钟跳变问题。HLC机制配合NTP服务,也能提供必定偏差范围内的快照读。

参考文档

https://cse.buffalo.edu/tech-reports/2014-04.pdf

相关文章
相关标签/搜索