一、NoSQL数据库概述
NoSQL 是一种不同于关系数据库的数据库管理系统设计方式,是对非关系型数据库的统称,它所采用的数据模型并非传统关系数据库的关系模型,而是类似键/值、列族、文档等非关系模型。NoSQL 数据库没有固定的表结构,通常也不存在连接操作,也没有严格遵守 ACID 约束。因此,与关系数据库相比,NoSQL 具有灵活的水平可扩展性,可以支持海量数据存储。此外,NoSQL数据库支持 MapReduce 风格的编程,可以较好地应用于大数据时代的各种数据管理。NoSQL 数据库的出现,一方面弥补了关系数据库在当前商业应用中存在的各种缺陷,另一方面也撼动了关系数据库的传统垄断地位。
二、NoSQL数据库特点
当应用场合需要简单的数据模型、灵活性的 IT 系统、较高的数据库性能和较低的数据库一致性时,NoSQL 数据库是一个很好的选择。通常 NoSQL 数据库具有以下 3 个特点。
1.灵活的可扩展性
传统的关系型数据库由于自身设计机理的原因,通常很难实现"横向扩展",在面对数据库负载大规模增加时,往往需要通过升级硬件来实现"纵向扩展"。但是,当前的计算机硬件制造工艺已经达到一个限度,性能提升的速度开始趋缓,已经远远赶不上数据库系统负载的增加速度,而且配置高端的高性能服务器价格不菲,因此寄希望于通过"纵向扩展"满足实际业务需求,已经变得越来越不现实。相反,"横向扩展"仅需要非常普通廉价的标准化刀片服务器,不仅具有较高的性价比,也提供了理论上近乎无限的扩展空间。NoSQL 数据库在设计之初就是为了满足"横向扩展"的需求,因此天生具备良好的水平扩展能力。
2.灵活的数据模型
关系模型是关系数据库的基石,它以完备的关系代数理论为基础,具有规范的定义,遵守各种严格的约束条件。这种做法虽然保证了业务系统对数据一致性的需求,但是过于死板的数据模型,也意味着无法满足各种新兴的业务需求。相反,NoSQL 数据库天生就旨在摆脱关系数据库的各种束缚条件,摈弃了流行多年的关系数据模型,转而采用键/值、列族等非关系模型,允许在一个数据元素里存储不同类型的数据。
3.与云计算紧密融合
云计算具有很好的水平扩展能力,可以根据资源使用情况进行自由伸缩,各种资源可以动态加入或退出,NoSQL 数据库可以凭借自身良好的横向扩展能力,充分自由利用云计算基础设施,很好地融入到云计算环境中,构建基于 NoSQL 的云数据库服务。
三、NoSQL与关系数据库的比较
下表给出了 NoSQL 和关系数据库(Relational DataBase Management System,RDBMS)的简单比较,对比指标包括数据库原理、数据规模、数据库模式、查询效率、一致性、数据完整性、扩展性、可用性、标准化、技术支持和可维护性等方面。从表中可以看出,关系数据库的突出优势在于,以完善的关系代数理论作为基础,有严格的标准,支持事务 ACID 四性,借助索引机制可以实现高效的查询,技术成熟,有专业公司的技术支持;其劣势在于,可扩展性较差,无法较好地支持海量数据存储,数据模型过于死板,无法较好地支持 Web 2.0 应用,事务机制影响了系统的整体性能等。NoSQL 数据库的明显优势在于,可以支持超大规模数据存储,灵活的数据模型可以很好地支持 Web 2.0 应用,具有强大的横向扩展能力等;其劣势在于,缺乏数学理论基础,复杂查询性能不高,一般都不能实现事务强一致性,很难实现数据完整性,技术尚不成熟,缺乏专业团队的技术支持,维护较困难等。
通过上述对 NoSQL 数据库和关系数据库的一系列比较可以看出,二者各有优势,也都存在不同层面的缺陷。因此,在实际应用中,二者都可以有各自的目标用户群体和市场空间,不存在一个完全取代另一个的问题。对于关系数据库而言,在一些特定应用领域,其地位和作用仍然无法被取代,银行、超市等领域的业务系统仍然需要高度依赖于关系数据库来保证数据的一致性。此外,对于一些复杂查询分析型应用而言,基于关系数据库的数据仓库产品,仍然可以比 NoSQL数据库获得更好的性能。
在实际应用中,一些公司也会采用混合的方式构建数据库应用,比如亚马逊公司就使用不同类型的数据库来支撑它的电子商务应用。对于"购物篮"这种临时性数据,采用键值存储会更加高效,而当前的产品和订单信息则适合存放在关系数据库中,大量的历史订单信息则适合保存在类似 MongoDB 的文档数据库中。
四、NoSQL 的四大类型
近些年,NoSQL 数据库发展势头非常迅猛。在短短四五年时间内,NoSQL 领域就爆炸性地产生了 50~150 个新的数据库(http://nosql-database.org/)。据一项网络调查显示,行业中最需要的开发人员技能前十名依次是 HTML5、MongoDB、iOS、Android、Mobile Apps、Puppet、Hadoop、jQuery、PaaS 和 Social Media。其中,MongoDB(一种文档数据库,属于 NoSQL)的热度甚至位于 iOS 之前,足以看出 NoSQL 的受欢迎程度。感兴趣的读者可以参考《七天七数据库》一书,学习 Riak、Apache HBase、MongoDB、Apache CouchDB、Neo4j 和 Redis 等 NoSQL 数据库的使用方法。
NoSQL 数据库虽然数量众多,但是归结起来,典型的 NoSQL 数据库通常包括键值数据库、
列族数据库、文档数据库和图数据库,如图所示。
4.1、键值数据库
键值数据库(Key-Value Database)会使用一个哈希表,这个表中有一个特定的 Key 和一个指针指向特定的 Value。Key 可以用来定位 Value,即存储和检索具体的 Value。Value 对数据库而言是透明不可见的,不能对 Value 进行索引和查询,只能通过 Key 进行查询。Value 可以用来存储任意类型的数据,包括整型、字符型、数组、对象等。在存在大量写操作的情况下,键值数据库可以比关系数据库取得明显更好的性能。因为,关系数据库需要建立索引来加速查询,当存在大量写操作时,索引会发生频繁更新,由此会产生高昂的索引维护代价。关系数据库通常很难水平扩展,但是键值数据库天生具有良好的伸缩性,理论上几乎可以实现数据量的无限扩容。键值数据库可以进一步划分为内存键值数据库和持久化(Persistent)键值数据库。内存键值数据库把数据保存在内存,如 Memcached 和 Redis;持久化键值数据库把数据保存在磁盘,如 BerkeleyDB、Voldmort 和 Riak。
当然,键值数据库也有自身的局限性,条件查询就是键值数据库的弱项。因此,如果只对部分值进行查询或更新,效率就会比较低下。在使用键值数据库时,应该尽量避免多表关联查询,可以采用双向冗余存储关系来代替表关联,把操作分解成单表操作。此外,键值数据库在发生故障时不支持回滚操作,因此无法支持事务。键值数据库的相关产品、数据模型、典型应用、优缺点和使用者见下表:
4.2、列族数据库
列族数据库一般采用列族数据模型,数据库由多个行构成,每行数据包含多个列族,不同的行可以具有不同数量的列族,属于同一列族的数据会被存放在一起。每行数据通过行键进行定位,与这个行键对应的是一个列族,从这个角度来说,列族数据库也可以被视为一个键值数据库。列族可以被配置成支持不同类型的访问模式,一个列族也可以被设置成放入内存当中,以消耗内存为代价来换取更好的响应性能。列族数据库的相关产品、数据模型、典型应用、优缺点和使用者见下表:
4.3、文档数据库
在文档数据库中,文档是数据库的最小单位。虽然每一种文档数据库的部署都有所不同,但是大都假定文档以某种标准化格式封装并对数据进行加密,同时用多种格式进行解码,包括 XML、YAML、JSON 和 BSON 等,或者也可以使用二进制格式(如 PDF、微软 Office 文档等)。文档数据库通过键来定位一个文档,因此可以看成是键值数据库的一个衍生品,而且前者比后者具有更高的查询效率。对于那些可以把输入数据表示成文档的应用而言,文档数据库是非常合适的。一个文档可以包含非常复杂的数据结构,如嵌套对象,并且不需要采用特定的数据模式,每个文档可能具有完全不同的结构。文档数据库既可以根据键(Key)来构建索引,也可以基于文档内容来构建索引。尤其是基于文档内容的索引和查询这种能力,是文档数据库不同于键值数据库的地方,因为在键值数据库中,值(Value)对数据库是透明不可见的,不能根据值来构建索引。文档数据库主要用于存储并检索文档数据,当需要考虑很多关系和标准化约束以及需要事务支持时,传统的关系数据库是更好的选择。文档数据库的相关产品、数据模型、典型应用、优缺点和使用者见下表:
4.4、图数据库
图数据库以图论为基础,一个图是一个数学概念,用来表示一个对象集合,包括顶点以及连接顶点的边。图数据库使用图作为数据模型来存储数据,完全不同于键值、列族和文档数据模型,可以高效地存储不同顶点之间的关系。图数据库专门用于处理具有高度相互关联关系的数据,可以高效地处理实体之间的关系,比较适合于社交网络、模式识别、依赖分析、推荐系统以及路径寻找等问题。有些图数据库(如 Neo4J),完全兼容 ACID。但是,除了在处理图和关系这些应用领域具有很好的性能以外,在其他领域,图数据库的性能不如其他 NoSQL 数据库。图数据库的相关产品、数据模型、典型应用、优缺点和使用者见下表:
五、NoSQL 的三大基石
NoSQL 的三大基石包括 CAP、BASE 和最终一致性。
5.1、CAP
2000 年,美国著名科学家、伯克利大学的 Eric Brewer 教授指出了著名的 CAP 理论,后来美国麻省理工学院(MIT)的两位科学家 Seth Gilbert 和 Nancy lynch 证明了 CAP 理论的正确性。所谓 CAP 指的是:
- C(Consistency):一致性。它是指任何一个读操作总是能够读到之前完成的写操作的结果,也就是在分布式环境中,多点的数据是一致的。
- A(Availability):可用性。它是指快速获取数据,可以在确定的时间内返回操作结果。
- P(Tolerance of Network Partition):分区容忍性。它是指当出现网络分区的情况时(即系统中的一部分节点无法和其他节点进行通信),分离的系统也能够正常运行。
CAP 理论(见下图)告诉我们,一个分布式系统不可能同时满足一致性、可用性和分区容忍性这 3 个需求,最多只能同时满足其中 2 个,正所谓"鱼和熊掌不可兼得"。如果追求一致性,那么就要牺牲可用性,需要处理因为系统不可用而导致的写操作失败的情况;如果要追求可用性,那么就要预估到可能发生数据不一致的情况,比如,系统的读操作可能不能精确地读取到写操作写入的最新值。
下面给出一个牺牲一致性来换取可用性的实例。假设分布式环境下存在两个节点 M1 和 M2,一个数据 V 的两个副本 V1 和 V2 分别保存在 M1 和 M2 上,两个副本的值都是 val0,现在假设有两个进程 P1 和 P2 分别对两个副本进行操作,进程 P1向节点 M1中的副本 V1写入新值 val1,进程 P2从节点 M2 中读取 V 的副本 V2 的值。
当整个过程完全正常执行时,会按照以下过程进行(见下图)。
(1)进程 P1 向节点 M1 的副本 V1 写入新值 val1。
(2)节点 M1 向节点 M2 发送消息 MSG 以更新副本 V2 值,把副本 V2 值更新为 val1。
(3)进程 P2 在节点 M2 中读取副本 V2 的新值 val1。
但是当网络发生故障时,可能导致节点 M1 中的消息 MSG 无法发送到节点 M2,这时,进程P2 在节点 M2 中读取到的副本 V2 的值仍然是旧值 val0。由此产生了不一致性问题。
从这个实例可以看出,当我们希望两个进程 P1 和 P2 都实现高可用性,也就是能够快速访问到需要的数据时,就会牺牲数据一致性。
当处理 CAP 的问题时,可以有以下几个明显的选择(见图 5-4)。
(1)CA。也就是强调一致性(C)和可用性(A),放弃分区容忍性(P),最简单的做法是把所有与事务相关的内容都放到同一台机器上。很显然,这种做法会严重影响系统的可扩展性。传统的关系数据库(MySQL、SQL Server 和 PostgreSQL)都采用了这种设计原则,因此扩展性都比较差。
(2)CP。也就是强调一致性(C)和分区容忍性(P),放弃可用性(A),当出现网络分区的情况时,受影响的服务需要等待数据一致,因此在等待期间就无法对外提供服务。Neo4J、BigTable和 HBase 等 NoSQL 数据库都采用了 CP 设计原则。
(3)AP。也就是强调可用性(A)和分区容忍性(P),放弃一致性(C),允许系统返回不一致的数据。这对于许多 Web 2.0 网站而言是可行的,这些网站的用户首先关注的是网站服务是否可用,当用户需要发布一条微博时,必须能够立即发布,否则,用户就会放弃使用,但是这条微博发布后什么时候能够被其他用户读取到,则不是非常重要的问题,不会影响到用户体验。因此,对于 Web 2.0 网站而言,可用性与分区容忍性优先级要高于数据一致性,网站一般会尽量朝着 AP的方向设计。当然,在采用 AP 设计时,也可以不完全放弃一致性,转而采用最终一致性。Dynamo、Riak、CouchDB、Cassandra 等 NoSQL 数据库就采用了 AP 设计原则。
5.2、BASE
说起 BASE(Basically Availble,Soft-state,Eventual consistency),不得不谈到 ACID。一个数据库事务具有 ACID 四性。
- A(Atomicity):原子性。它是指事务必须是原子工作单元,对于其数据修改,要么全都执行,要么全都不执行。
- C(Consistency):一致性。它是指事务在完成时,必须使所有的数据都保持一致状态。
- I(Isolation):隔离性。它是指由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。
- D(Durability):持久性。它是指事务完成之后,它对于系统的影响是永久性的,该修改即使出现致命的系统故障也将一直保持。
关系数据库系统中设计了复杂的事务管理机制来保证事务在执行过程中严格满足 ACID 四性要求。关系数据库的事务机制较好地满足了银行等领域对数据一致性的要求,因此得到了广泛的商业应用。但是,NoSQL 数据库通常应用于 Web 2.0 网站等场景中,对数据一致性的要求并不是很高,而是强调系统的高可用性,因此为了获得系统的高可用性,可以考虑适当牺牲一致性或分区容忍性。BASE 的基本思想就是在这个基础上发展起来的,它完全不同于 ACID 模型,BASE牺牲了高一致性,从而获得可用性或可靠性,Cassandra 系统就是一个很好的实例。有意思的是,单从名字上就可以看出二者有点"水火不容",BASE 的英文意义是碱,而 ACID 的英文含义是酸。
BASE 的基本含义是基本可用(Basically Availble)、软状态(Soft-state)和最终一致(Eventual consistency)。
5.2.1.基本可用
基本可用是指一个分布式系统的一部分发生问题变得不可用时,其他部分仍然可以正常使用,也就是允许分区失败的情形出现。比如,一个分布式数据存储系统由 10 个节点组成,当其中 1个节点损坏不可用时,其他 9 个节点仍然可以正常提供数据访问,那么,就只有 10%的数据是不可用的,其余 90%的数据都是可用的,这时就可以认为这个分布式数据存储系统"基本可用"。
5.2.2.软状态
"软状态(Soft-state)"是与"硬状态(Hard-state)"相对应的一种提法。数据库保存的数据是"硬状态"时,可以保证数据一致性,即保证数据一直是正确的。"软状态"是指状态可以有一段时间不同步,具有一定的滞后性。假设某个银行中的一个用户 A 转移资金给另外一个用户 B,假设这个操作通过消息队列来实现解耦,即用户 A 向发送队列中放入资金,资金到达接收队列后通知用户 B 取走资金。由于消息传输的延迟,这个过程可能会存在一个短时的不一致性,即用户 A已经在队列中放入资金,但是资金还没有到达接收队列,用户 B 还没拿到资金,这就会出现数据不一致状态,即用户 A 的钱已经减少了,但是用户 B 的钱并没有相应增加,也就是说,在转账的开始和结束状态之间存在一个滞后时间,在这个滞后时间内,两个用户的资金似乎都消失了,出现了短时的不一致状态。虽然这对用户来说有一个滞后,但是这种滞后是用户可以容忍的,甚至用户根本感知不到,因为两边用户实际上都不知道资金何时到达。当经过短暂延迟后,资金到达接收队列时,就可以通知用户 B 取走资金,状态最终一致。
5.2.3.最终一致性
一致性的类型包括强一致性和弱一致性,二者的主要区别在于高并发的数据访问操作下,后续操作是否能够获取最新的数据。对于强一致性而言,当执行完一次更新操作后,后续的其他读操作就可以保证读到更新后的最新数据;反之,如果不能保证后续访问读到的都是更新后的最新数据,那么就是弱一致性。而最终一致性只不过是弱一致性的一种特例,允许后续的访问操作可以暂时读不到更新后的数据,但是经过一段时间之后,必须最终读到更新后的数据。最终一致性也是 ACID 的最终目的,只要最终数据是一致的就可以了,而不是每时每刻都保持实时一致。
5.3、最终一致性
讨论一致性的时候,需要从客户端和服务端两个角度来考虑。从服务端来看,一致性是指更新如何复制分布到整个系统,以保证数据最终一致。从客户端来看,一致性主要指的是高并发的数据访问操作下,后续操作是否能够获取最新的数据。关系数据库通常实现强一致性,也就是一旦一个更新完成,后续的访问操作都可以立即读取到更新过的数据。而对于弱一致性而言,则无法保证后续访问都能够读到更新后的数据。
最终一致性的要求更低,只要经过一段时间后能够访问到更新后的数据即可。也就是说,如果一个操作 OP 往分布式存储系统中写入了一个值,遵循最终一致性的系统可以保证,如果后续访问发生之前没有其他写操作去更新这个值的话,那么,最终所有后续的访问都可以读取到操作OP 写入的最新值。从 OP 操作完成到后续访问可以最终读取到 OP 写入的最新值,这之间的时间间隔称为"不一致性窗口",如果没有发生系统失败的话,这个窗口的大小依赖于交互延迟、系统负载和副本个数等因素。
最终一致性根据更新数据后各进程访问到数据的时间和方式的不同,又可以进行如下区分。
- 因果一致性。如果进程 A 通知进程 B 它已更新了一个数据项,那么进程 B 的后续访问将获得进程 A 写入的最新值。而与进程 A 无因果关系的进程 C 的访问,仍然遵守一般的最终一致性规则。
- "读己之所写"一致性。可以视为因果一致性的一个特例。当进程 A 自己执行一个更新操作之后,它自己总是可以访问到更新过的值,绝不会看到旧值。
- 会话一致性。它把访问存储系统的进程放到会话(Session)的上下文中,只要会话还存在,系统就保证"读己之所写"一致性。如果由于某些失败情形令会话终止,就要建立新的会话,而且系统保证不会延续到新的会话。
- 单调读一致性。如果进程已经看到过数据对象的某个值,那么任何后续访问都不会返回在那个值之前的值。
- 单调写一致性。系统保证来自同一个进程的写操作顺序执行。系统必须保证这种程度的一致性,否则就非常难以编程了。
六、从 NoSQL 到 NewSQL 数据库
NoSQL 数据库可以提供良好的扩展性和灵活性,很好地弥补了传统关系数据库的缺陷,较好地满足了 Web 2.0 应用的需求。但是,NoSQL 数据库也存在自己的天生不足之处。由于采用非关系数据模型,因此它不具备高度结构化查询等特性,查询效率尤其是复杂查询方面不如关系数据库,而且不支持事务 ACID 四性。
在这个背景下,近几年,NewSQL 数据库开始逐渐升温。NewSQL 是对各种新的可扩展、高性能数据库的简称,这类数据库不仅具有 NoSQL 对海量数据的存储管理能力,还保持了传统数据库支持 ACID 和 SQL 等特性。不同的 NewSQL 数据库的内部结构差异很大,但是它们有两个显著的共同特点:都支持关系数据模型;都使用 SQL 作为其主要的接口。
目前,具有代表性的 NewSQL 数据库主要包括 Spanner、Clustrix、GenieDB、ScalArc、Schooner、VoltDB、RethinkDB、ScaleDB、Akiban、CodeFutures、ScaleBase、Translattice、NimbusDB、Drizzle、Tokutek、JustOne DB 等。此外,还有一些在云端提供的 NewSQL 数据库(也就是云数据库),包括 Amazon RDS、Microsoft SQL Azure、Database.com、Xeround 和 FathomDB 等。在众多 NewSQL 数据库中,Spanner 备受瞩目,它是一个可扩展、多版本、全球分布式并且支持同步复制的数据库,是 Google 的第一个可以全球扩展并且支持外部一致性的数据库。Spanner 能做到这些,离不开一个用 GPS 和原子钟实现的时间 API。这个 API 能将数据中心之间的时间同步精确到 10 ms 以内。因此,Spanner 有几个良好的特性:无锁读事务、原子模式修改、读历史数据无阻塞。
一些 NewSQL 数据库比传统的关系数据库具有明显的性能优势。比如,VoltDB 系统使用了NewSQL 创新的体系架构,释放了主内存运行的数据库中消耗系统资源的缓冲池,在执行交易时可比传统关系数据库快 45 倍。VoltDB 可扩展服务器数量为 39 个,并可以每秒处理 160 万个交易(300 个 CPU 核心),而具备同样处理能力的 Hadoop 则需要更多的服务器。
综合来看,大数据时代的到来,引发了数据处理架构的变革。以前,业界和学术界追求的方向是一种架构支持多类应用(One Size Fits All),如图 5-5 所示,包括事务型应用(OLTP 系统)、分析型应用(OLAP、数据仓库)和互联网应用(Web 2.0)。但是,实践证明,这种理想愿景是不可能实现的,不同应用场景的数据管理需求截然不同,一种数据库架构根本无法满足所有场景。因此,到了大数据时代,数据库架构开始向着多元化方向发展,并形成了传统关系数据库(OldSQL)、NoSQL 数据库和 NewSQL 数据库 3 个阵营,三者各有自己的应用场景和发展空间。
尤其是传统关系数据库,并没有就此被其他两者完全取代,在基本架构不变的基础上,许多关系数据库产品开始引入内存计算和一体机技术以提升处理性能。在未来一段时期内,3 个阵营共存共荣的局面还将持续,不过有一点是肯定的,那就是传统关系数据库的辉煌时期已经过去了。
为了更加清晰地认识传统关系数据库、NoSQL 和 NewSQL 数据库的相关产品,图 5-6 给出了3 种数据库相关产品的分类情况。