NoSQL运动
自从上世纪80年代以降,关系型数据库(即传统的OLTP和OLAP数据库)一直都是后端业务系统的主导,能够满足很多需求。但是,随着数据量的激增、对查询响应要求提升、越来越多非结构化数据泛滥等原因,关系型数据库的领域面临挑战,因此催生了NoSQL(非关系型、not only SQL)运动——这个词在世纪之交才出现,但是NoSQL思想和数据库出现得要早得多。
各式各样的NoSQL数据库在分布式、实时性、可扩展性、Schema-less等方向都取得了突破。下图示出了关系型数据库和NoSQL的区别,以及4种NoSQL存储模式——键值、列族、图和文档。
所谓鱼与熊掌不可兼得,NoSQL数据库几乎都不会提供ACID事务保证,也不将ACID作为指导思想,毕竟在分布式系统上实现ACID太难了。它们的基础是分布式领域的两个著名理论,即CAP和BASE。
CAP理论
CAP理论由Eric Brewer在2000年作为猜想提出,并在2002年由Seth Gilbert和Nancy Lynch证明了其正确性。CAP理论的内容为:
在分布式系统中,不可能同时满足一致性(consistency)、可用性(availability)、分区容错性(partition tolerance)三大特征,最多只能同时满足其中二者。
那么一致性、可用性、分区容错性分别又是什么含义呢?
一致性
Every read receives the most recent write or an error.
字面意思是说,对于客户端向分布式系统的读请求,要么读到最新的数据,要么失败。也就是说,不管数据如何更新,它在多个副本之间都可以保持精准、立即的同步,所有客户端取得的数据永远是“正确”的。由此可见,CAP中的“一致性”与ACID中的“一致性”虽然不是一个概念,但它仍然指的是最强的一致性。
可用性
Every request receives a (non-error) response, without the guarantee that it contains the most recent write.
字面意思是说,对分布式系统的请求一定会得到非错误的响应,但不保证响应一定包含最新写入的数据。也就是说从客户端的角度看,不会出现向分布式系统发出请求但得不到任何有意义的响应的情况,即系统提供的服务对客户端而言不能宕机,一直都是可用的。
分区容错性
The system continues to operate despite an arbitrary number of messages being dropped (or delayed) by the network between nodes.
字面意思是说,就算分布式系统内部节点之间的通信出了问题(如延迟、丢包),系统仍然能正常运作。这里的“分区”是指网络分区(network partition)——网络中有节点挂掉时,原网络分解为两个或多个子网络的过程。也就是说,系统不会因为内部的网络不可靠就停止服务。
CP/AP的抉择与“CAP NoSQL铁三角”
很显然,由于一致性、可用性、分区容错性三者只能得其二,所以必须有所取舍。但是,我们考虑的是分布式系统,分布式系统总是由多个节点组成,网络问题几乎必定会出现。所以分区容错性必须要保证,而在一致性和可用性之间做出权衡,即选择CP还是AP的问题。
考虑下面的极简模型:有两个数据中心,它们通过网络进行Replication,即数据在其中一个数据中心发生更改,就会立即通过网络传输到另外一个数据中心。每个客户端同时只能连接到一个数据中心的Application做读写操作。
假设Replication连接断掉了——即产生了网络分区,那么我们只有两种选择。
- AP:仍然允许所有客户端读写,但是两个数据中心之间不再同步,它们的数据就会逐渐地变得不同,即牺牲一致性,保证可用性。
- CP:只允许连接到一个数据中心上的客户端读写,另外一个停止服务,直到Replication连接恢复,即牺牲可用性,保证一致性。
具体到NoSQL的范畴,我个人把它叫做“CAP NoSQL铁三角”,如下。
CA显然是为传统的(单点)关系型数据库和其他类似架构的非分布式数据库准备的,它们不存在网络分区的问题,只是作为对照。而CP和AP分别都有相当一批NoSQL追随者。
下面不妨解释解释,HBase为什么是CP的?(因为我对HBase最熟悉哈哈)
我们知道,HBase的数据是按照RowKey分布在各个Region(即RegionServer)上的。在稳定的HBase集群中,不管访问同一个RowKey多少次,最终都会指向同一个RegionServer,并且HBase保证对行的更新操作和新行的put操作都是原子的,所以它是强一致的。
但是,如果有RegionServer因为网络分区而不可达,HMaster将其视为失败,并触发它持有的Region的WAL重放(replay)。在整个重放过程中,这些Region都不能提供服务,所以并未保证可用性。很简单,如果正在重放的Region也能被读写,那么就一定会返回不一致的数据了。
当然,还可以再继续“杠”下去:如果开启了HBase 2.0 版本支持的Region Replica特性,那么就不再是CP,而是AP了。看官可以找一找Region Replica的资料,很容易明白。
CP/AP的分类是严谨的么?
我水平不够,给不出答案。这个问题只是为了引出Martin Kleppman(大名鼎鼎的《Designing Data-intensive Applications》一书的作者)在2015年写的一篇博文《Please stop calling databases CP or AP》。从这题目也可以看出来,他认为用CAP原理来界定是很不科学的,观点如下:
- CAP的定义太狭窄,如:一致性实际上指的是最强的线性一致性(linearizability);可用性要求所有节点都能做出响应,与real-world可用性差别甚大;分区容错性的真正含义是容忍异步的不可靠网络,名称过于误导。
- 只考虑了单变量的读写,完全没考虑事务。
- 除了网络问题之外,没考虑其他很多异常情况,如节点出错、磁盘用尽等,也没有将延迟问题算在内(例如,一般不会认为5分钟才能返回查询结果的系统是“可用”的)。
Martin Kleppman进一步指出,CAP理论过于一刀切了,而人们总是强行将分布式系统的设计塞入CP或AP其中之一,丢失了很多细节,并且原来的consistency、availability的含义也改变了。甚至有不少系统的实现都同时不满足严格的C和A,只满足P,虽然有些尴尬,但也不意味着就是糟糕的方案。他另外写了一篇《A Critique of the CAP Theorem》提出了CAP分类的替代方案,就不再赘述了。这两篇都很有意思。
BASE理论
BASE理论由Dan Prichett在2008年藉由论文《BASE:An ACID Alternative》提出。它是三个词组的首字母缩写,即:
- 基本可用(basically available)
- 软状态(soft state)
- 最终一致性(eventually consistent)
文字游戏很有意思,acid(“酸”)与base(“碱”)正好是对立的,当然这不代表RDBMS和NoSQL是水火不容的哈。
BASE是对CAP一致性和可用性权衡的结果,来源于对大规模互联网分布式实践的总结,是基于CAP理论演化来的。下面简单地解释一下。
基本可用
所谓基本可用,是指分布式系统在出现故障时,允许损失部分可用性,以保证系统基础运转仍然正常。这在实际操作中并不少见,如:
- 正常情况下,数据库需要在200ms内返回查询结果,但由于故障,使得响应时间延长到了2s,即响应的延迟增加了;
- 在电商大促高峰(特别是秒杀)时,为了维护整个系统的稳定性,部分订单会不成功,用户会被引导至降级页面,即牺牲部分功能。
软状态
软状态也称为弱状态,是指允许系统中的数据存在中间状态,并且该中间状态不影响系统的可用性。翻译成人话,就是分布式系统在不同节点之间进行数据同步的过程中允许有一定的不同步性。
最终一致性
最终一致性是指系统中的所有副本在经过一段时间的同步之后,最终总能够达到一致的状态。它是BASE的终极目标,也是任何分布式系统在实践中必须达到的目标。
最终一致性是个复杂的话题,本文已经非常之长了,就不再展开了。引用一下Werner Vogels在2008年的文章《Eventually Consistent Revisited》中的观点,他认为最终一致性是特殊的弱一致性:系统保证在没有后续更新的前提下,系统最终返回上一次更新操作的值。在没有故障发生的前提下,不一致窗口的时间主要受通信延迟,系统负载和复制副本的个数影响。
那么有哪些手段保证最终一致性呢?比较重量级的方案就是直接引入分布式事务,如2PC、3PC、TCC等,轻量级的方案都可以在业务中自己实现,如重试 幂等性保证、状态机、重做日志等,不再一一赘述。
由上面的介绍可知,只考虑NoSQL的话,它们全部符合BASE理论的精神,即舍弃强一致性而追求最终一致性。BASE理论的约束也比CAP更加松散也易于实现,所以Martin Kleppman并未对它颇有微词吧。