当数据库遇到分布式

概述

NewSQL日渐火热,无论还是开源的TiDB,CockroachDB还是互联网大厂的Spanner,Oceanbase都号称NewSQL,也就是分布式数据库。NewSQL的典型特征就是,支持SQL,支持事务,高性能,低成本,高可靠,强一致,易扩展,运维友好等。从NewSQL的演进来看,所谓NewSQL,可以简单理解为NoSQL+传统的关系型数据库的结合,NoSQL强调分布式(高可用,可扩展),关系型数据库则强调事务,SQL。正因为二者的叠加,所以需要把两个领域的概念整合在一起,本文主要想把分布式数据库中几个基本概念讲清楚。

一致性

数据库和分布式系统中都有一致性概念,由于很多英文单词对应的中文都是“一致性”,导致容易产生误区。数据库中的ACID,C是Consistency,这个C主要强调应用逻辑的一致性,比如应用定义的约束,包括外键等。分布式系统的CAP以及一致性协议,也称为一致性。前者主要强调,读是否能读到最新,以及并发场景下操作执行的时序关系,主要包括线性一致性(linearizability),顺序一致性(sequential consistency),因果一致性(causal consistency)等;后者主要强调“共识”,分布式中的多个节点对某个事情(选主,事务提交)达成一致,常见的共识算法包括paxos协议,raft协议等。

线性一致性(linearizability)

简单来说,线性一致性要求,第一,“写后读”,这里写和读是两个操作,如果写操作在完成之后,读才开始,读要能读到最新的数据,而且保证以后也能读操作也都能读到这个最新的数据。第二,所有操作的时序与真实物理时间一致。相对于“写后读”,第二点要求即使不相关的两个操作,如果执行有先后顺序,线性一致性要求最终执行的结果也需要满足这个先后顺序。比如,操作序列(写A,读A,写B,读B),那么不仅,读A,读B能读到最新A值和B值;而且要保证,如果读B读到最新值时,读A一定也能读到最新值,也就是需要保证执行时序与真实时序相同。第三点,如果两个操作是并发的(比如读A没有结束时,写B开始了),那么这个并发时序不确定,但从最终执行的结果来看,要确保所有线程(进程,节点)看到的执行序列是一致的。

下图对线性一致性有详细的论述,来源于[6]

顺序一致性(sequential consistency)

相比线性一致性,主要区别在于,对于物理上有先后顺序的操作,是否要保证这个时序。具体而言,对于单个线程,操作的顺序仍然要保留,对于多个线程(进程,节点),执行的事件的先后顺序与物理时钟顺序不保证。但是要求,从执行结果来看,所有线程(进程,节点)看到的执行序列是一样的。详细定义来源于[6]

                             

下图的例子很好的区分了线性一致性和顺序一致性。

                             

对于(a),执行序列write(y,2),read(x,0),write(x,4),read(y,2),结果符合要求,但是从客户端的角度来看,write(x,4)先于read(x,0)执行,但是read却没有读到最新值。

对于(b),write(y,2)和read(y,2)有先后顺序,也是符合“写后读”,所以是线性一致性。

对于(c), 有几种可能:

1).write(x,4),read(y,0),write(y,2),read(x,0),x的写后读,不符合要求;

2).write(y,2),read(x,0),write(x,4),read(y,0),y的写后读,不符合要求。

所以既不符合线性一致性,也不符合顺序一致性。

因果一致性(causal consistency)

相对于顺序一致性,弱化了不相关操作是否需要保序

对于b),p1和p2 w(x)是没有先后关系的,因此谁先发生都是可以的。

从p3的视角来看,操作执行的序列是w(x,7),r(x,7),w(x,2),r(x,2),w(x,4);保证了“写后读”

从p4的视角来看,操作执行序列是w(x,2),w(x,4),r(x,4),w(x,7),r(x,7);保证了“写后读”

但是不同进程看到的执行序列不一样,所以不符合顺序一致性。

                              

可串行化

上一篇文章讲了数据库中异常和隔离级别,实际上隔离级别是纯粹数据库领域的概念与分布式系统并没有交集,比如读未提交,读提交,可重复读以及可串行化。对于数据库而言,我们说Serializable,是说并发场景下,多个并发事务最终执行的序列与某个串行执行的序列相同(无事务并发,事务的执行没有重叠)。那么如何实现并发控制来达到可串行化调度。数据库中对于同一对象的操作可能存在几类冲突,包括读写冲突,写写冲突,写读冲突等,如果解决了这些冲突,也就实现了可串行化调度。实际上冲突可串行化调度是可串行调度的充分条件,并非必要条件,详细展开可以看这篇blog,而我们实际的数据库系统中实现可串行化调度也是解决冲突串行化问题。主要有两种,一种是基于S2PL(Strict 2 Phrase Locking),事务操作过程中,对读加读锁,对写加写锁,事务提交时,才将锁释放,为了避免幻读,还需要实现间歇锁等;另外一种,是基于Snapshot的SSI隔离级别,这种实现与S2PL的主要区别在于,读仍然采用快照读,不加锁,读写不互斥,为了实现可串行化调度,需要收集事务的读写操作信息,并判断是否事务有相互依赖的情况(冲突成环),如有,则将冲突的事务回滚,实际上是first-commit-win原则,最后导致成环的事务会被回滚。具体可以参考论文:Serializable Isolation for Snapshot Databases,目前商业数据库PG和CockroachDB都是实现了SSI隔离级别。而MySQL的InnoDB存储引擎则是采用了S2PL实现了可串行化隔离级别。

线性一致性VS可串行化

前面分别介绍了分布式系统中的一致性以及数据库中的可串行化隔离级别,分布式数据库显然是分布式系统,也是数据库系统,那么是否能做到线性一致性+可串行化,就是所谓的“Strong Consistency”。这个定义来源于Jepsen的一篇blog,具体可以看下图。

                             

我们要注意到,讨论线性一致性时,我们讨论的粒度是一个操作,操作是否满足先后关系;而讨论隔离级别时,粒度是一个事务,事务是否与某个串行执行的结果相同。所以,这里就比较特殊了,事务中包含了若干读写操作,我们要保证读到最新,是说后开启的事务的读能读到之前的提交;还是事务中的每个读都能读到读开始之前的提交(读提交)。至少从DDIA(Designing Data-Intensive Application)[2]这本书介绍来看,应该是后者,所以它认为基于S2PL协议实现的可串行化,可以做到线性一致性兼得;而SSI由于是快照读,导致读不能读到最新,所以不满足线性一致性的。

                       

数据库要实现“线性一致性”,需要保证事务操作按全局时钟的先后顺序。对于写而言,通过一个统一的地方分配时间戳,显然先后执行的事务分配的时间戳也满足先后关系。这里实际上需要一个统一“全局时间源”,也就是业内常用的TSO(TimeStampOracle)方案,TSO能保证所有事务全局有序。对于读而言,读采用加锁当前读,也能保证读到最新,所以结合S2PL可以兼得可串行化+线性一致性,也就是实现Strict Serializable。

External Consistency(外部一致性)

google Spanner论文还提到一个外部一致性的概念,

                             

Spanner通过GPS+原子钟保证了所有事务的写有序,实现了类似TSO的功能,但是避免了TSO的单点可用性和性能问题。它提到External Consistency相比linearizability,主要是约束了非相关并发事务的提交顺序与物理时钟要保持一致,因为linearizability并不约束并发执行的操作。论文中没有提到Spanner如何实现Serializable隔离级别,我猜测是类似SSI的实现,那么仍然做不到Strict Serializable。

总结

分布式数据库中的一致性概念有很多,但含义都不太一样,ACID中的一致性主要强调应用逻辑的一致性,需要应用参与保证一致性,CAP中的一致性则主要强调多个副本的一致性,写后读是否能读到最新,这里面就衍生了几种一致性,包括线性一致性,顺序一致性,因果一致性等。数据库有隔离级别的概念,对于可串行化隔离级别也要求顺序,实际上与分布式系统的一致性没有什么关系,它更强调隔离,不强调事务执行的顺序是否与真实执行先后顺序保持一致。因此,数据库可能实现了可串行化隔离级别,但是并不一定实现了线性一致性,比如基于SSI实现的可串行化就是这类系统。

参考文档

[1].spanner论文

[2].https://jepsen.io/consistency

[3].http://www.bailis.org/blog/linearizability-versus-serializability/

[4].Spanner存储层实现

[5].Serializable Isolation for Snapshot Databases

[6].《Distributed Computing,Principles, Algorithms, and Systems》

[7].《Designing Data-Intensive Applications》

 

posted @ 2019-04-14 09:24 天士梦 阅读(...) 评论(...) 编辑 收藏