《HBase权威指南》一1.3 非关系型数据库系统Not-Only-SQL(简称NoSQL)

本节书摘来异步社区《HBase权威指南》一书中的第1章,第1.3节,作者: 【美】Lars George 译者: 代志远 , 刘佳 , 蒋杰 责编: 杨海玲,更多章节内容可以访问云栖社区“异步社区”公众号查看。

1.3 非关系型数据库系统Not-Only-SQL(简称NoSQL)

过去的四五年时间里,为了解决问题,创新的前进步伐由缓慢变得出奇得快,好像每周都会发布新的框架和项目来满足需求。我们看到了所谓的NoSQL解决方案问世了,NoSQL是Eric Evans针对Johan Oskarsson提出的“为新兴的新数据存储空间⑫命名”问题而创造的一个名词。

正是因为这类新产品还没有合适的名称,NoSQL一举成名。在激烈的讨论中,它被认为是“SQL”的克星_,或者说,它给仍旧考虑使用传统RDBMS的人带来了瘟疫……只是开个玩笑!

文字实际上,为特定的问题制定差异化的专用解决方案的想法并不新鲜。像Berkeley DB、Coherence、GT.M这样的系统,以及面向对象的数据库系统都已经出现了好多年,有些甚至都可以追溯到20世纪80年代初,从定义上来看,它们都属于NoSQL。

标示符号化(tagword)实际上是一个不错的选择:最新的存储系统不提供通过SQL查询数据的手段,只提供一些比较简单的、类似于API接口的方式来存取数据。

但是,也有一些工具为NoSQL数据存储提供了SQL语言的入口,用于执行一些关系数据库中常用的复杂条件查询。因此,从查询方式上的限制来说,关系型数据库和非关系型数据库并没有严格的区分。

实际上两者在底层上是有区别的,尤其涉及到模式或者ACID事务特性时,因为这与实际的存储架构是相关的。很多这一类的新系统首先做的事情是:抛弃一些限制因素以提升扩展性(这一点会在1.3.1节讨论)。例如,它们通常不支持事务或辅助索引。更重要的是,这一类系统是没有固定模式的,可以随着应用的改变而灵活变化。

一致性模型

在这本书里,我们经常会提到一致性问题,所以有必要在这里对它稍加介绍。一开始的一致性是保证数据库客户端操作的正确性,数据库必须保证每一步操作都是从一个一致的状态到下一个一致的状态。系统没有明确地指定如何实现这个功能,以便系统可以有多种选择。最终,系统要选择是进入下一个一致的状态,还是回退到上一个一致的状态,从而保证一致性。

一致性可以按照严格程度由强到弱分类,或者是按照对客户端的保证程度分类,下面是一个非正式的分类列表。

严格一致性:数据的变化是原子的,一经改变即时生效,这是一致性的最高形式。

顺序一致性:每个客户端看到的数据依照它们操作执行的顺序而变化。

因果一致性:客户端以因果关系顺序观察到数据的改变。

最终一致性:在没有更新数据的一段时间里,系统将通过广播保证副本之间的数据一致性。

弱一致性:没有做出保证的情况下,所有的更新会通过广播的形式传递,展现给不同客户端的数据顺序可能不一样。

采用最终一致性策略的系统还可以细分为几个子类,并且这些子策略还可以共存。亚马逊的首席技术官Werner Vogels在一篇名为“Eventually Consistent”的文章中列举了这几个子类。这篇文章还谈到了CAP定理(CAP theorem)⑬,其中指出,一个分布式系统只能同时实现一致性、可用性和分区容忍性(或分区容错性)中的两个。CAP定理是热点话题,不过它不是区分分布式系统的唯一方法,但CAP定理指出了,开发一套同时满足以上需求的分布式系统是比较困难的。例如,Vogels提到:

“在一系列的研究结果里发现,在较大型的分布式系统中,由于网络分隔,一致性与可用性不能同时满足。这意味着三个要素最多只能同时实现两个,不可能三者兼顾;放宽一致性的要求会提升系统的可用性……提升一致性意味着系统需要牺牲一定的可用性。”

放宽一致性来提高系统可用性是一个非常有效的提议。不过这种方案会强制让应用层去解决一致性的问题,因此也会增加系统的复杂度。
各种非关系型数据库有许多共同的特性,同时这其中的许多特性与传统的存储方案也有很多共同点。因此新系统并不是革命性的产品,从工程的角度来看更像是产品的进化。

假使连Memcached这样的项目都划入到NoSQL范畴的话,那就成了只要不是RDBMS就可以认为是NoSQL。这个说法导致了错误的二分法,二分法掩盖了这些系统提供的令人振奋的技术可行性。在NoSQL范畴内,还有很多的维度可以区分系统的特定优势所在。

1.3.1 维度

让我们来挑几种维度简单介绍一下。需要注意的是,列举的这些维度并不全面,并且这也不是唯一的区分方式。

数据模型

数据有多种存储的方式,包括键/值对(类似于HashMap)、半结构化的列式存储和文档结构存储。用户的应用如何存取数据?同时数据模式是否随着时间而变化?

存储模型

内存还是持久化?坦率来说做出这个决定并不难,其主要原因是,我们可以将其与RDBMS进行对比,它们通常持久化存储数据到磁盘中。即使需要的是纯粹的内存模式,也仍旧有其他方案。一旦考虑持久化存储,就需要考虑选择的方案是否会影响到访问模式?

一致性模型

严格一致性还是最终一致性?问题是存储系统如何实现它的目标:必须降低一致性要求吗?虽然这种问题很粗浅,但是在特定的场景中会产生巨大影响。因为一致性可能会影响操作延时,即系统响应读写请求的速度。这需要权衡投入和产出后得到一个折中结果。⑭

物理模型

分布式模式还是单机模式?这种架构看起来像什么?是仅仅运行在单个机器上,还是分布在多台机器上,但分布及扩展规则由客户端管理,换句话说,由用户自己的代码管理?也许分布式模式仅仅是个事后的工作,并且只会在用户需要扩展系统时产生问题。如果系统提供了一定的扩展性,那么需要用户采取特定的操作吗?最简单的解决方案就是一次增加一台机器,并且设置好分区(这点对于不支持虚拟分区的系统非常重要),设置时需要考虑同时提高每个分区的处理能力,因为系统的每个部分都需要提供均衡的性能。

读/写性能

用户必须了解自己的应用程序的访问模式。是读多写少?还是读写相当?或者是写多读少?是用范围扫描数据好,还是用随机读请求数据更好?有些系统仅仅对这些情况中的一种支持得非常好,有些系统则对各种情况都提供了很好的支持。

辅助索引

辅助索引支持用户按不同的字段和排序方式来访问表。这个维度覆盖了某些完全没有辅助索引支持且不保证数据排序的系统(类似于HashMap,即用户需要知道数据对应键的值),到某些可能通过外部手段简单支持这些功能的系统。如果存储系统不提供这项功能,用户的应用可以应对或自已模拟辅助索引吗?

故障处理

机器会崩溃是一个客观存在的问题,需要有一套数据迁移方案来应对这种情况(关于这一点可以参考在“一致性模型”中讨论的CAP定理)。每个数据存储如何进行服务器故障处理?故障处理完毕之后是否可以正常工作?这与之前讨论的“一致性模型”维度有关系,因为失去一台服务器可能会造成数据存储的空洞(hole),甚至使整个数据存储不可用。如果替换掉故障服务器,那么恢复100%服务的难度有多大?从一个正在提供服务的集群中卸载一台服务器时,也会遇到类似的问题。

压缩

当用户需要存储TB级的数据时,尤其当这些数据差异性很小或由可读性文本组成时,压缩会带来非常好的效果,即能节省大量的原始数据存储。有些压缩算法可以将此类的数据压缩到原始文件大小的十分之一。有可选择的压缩组件吗?又有哪些压缩算法可用?

负载均衡

假如用户有高读写吞吐率的需求,就要考虑配置一套能够随着负载变化自动均衡处理能力的系统。虽然这样不能完全解决该问题,但是也可以帮助用户设计高读写吞吐量的程序。

原子操作的读-修改-写

RDBMS提供了很多这类的操作(因为它是一个集中式的面向单服务器的系统),但这些操作在分布式系统中较难实现,这些操作可以帮助用户避免多线程造成的资源竞争,也可以帮助用户完成无共享应用服务器的设计。有了这些比较并交换(compare and swap,CAS)操作,或者说检查并设置(check and set)操作,在设计系统的时候可以有效地降低客户端的复杂度。

加锁、等待和死锁

众所周知,复杂的事务处理,如两阶段提交,会增加多个客户端竞争同一个资源的可能性。最糟糕的情况就是死锁,这种情况也很难解决。用户需要支持的系统采用哪种锁模型?这种锁模型能否避免等待和死锁?

图像说明文字 稍后我们会回顾这些维度,看看HBase适合用在哪里,其优势何在。现在需要指出的是,一定要根据实际的需求来仔细选择最适合的维度。按照实际情况来设计解决方案,要知道没有硬性规定说:RDBMS不能很好地解决的问题,NoSQL就能完美解决。重要的是正确地评估需求,然后再做出明智的选择,有需要的话甚至可以采用混合使用的方案。

可以用一个有趣的词来形容这个情况——阻抗匹配(impedance match),意思就是要为一个给定问题找到一个理想的解决方案,除了使用通用的解决方案,还应该知道有什么可用的解决方案,从而找到最适合于解决该问题的系统。

1.3.2 可扩展性

RDBMS非常适合事务性操作,但不见长于超大规模的数据分析处理,因为超大规模的查询需要进行大范围的数据记录扫描或全表扫描。分析型数据库可以存储数百或数千TB的数据,在一台服务器上做查询工作的响应时间,会远远超过用户可接受的合理响应时间。垂直扩展服务器性能,即增加CPU核数和磁盘数目,也并不能很好地解决该问题。

更糟糕的是,RDBMS的等待和死锁的出现频率,与事务和并发的增加并不是线性关系,准确地说,与并发数目的平方以及事务规模的3次方甚至5次方相关⑮。分区通常是一个不切合实际的解决方案,因为它需要客户端采用非常复杂的方式和较高的代价来维护分区信息。

一些商业RDBMS也解决过类似的问题,但它们往往只是特定地解决了问题的某几个方面,更重要的是,它们非常非常的昂贵。而一些开源的RDBMS解决方案中,往往放弃了其中的一些甚至全部的关系型特性,如辅助索引,来换取更高的性能拓展能力。

问题是,为了性能而一直放弃以上关系型特性是否值得?用户可以反范式化(见1.3.3节)数据模型来避免等待,并且可以通过降低锁粒度的方式来尽量避免死锁。数据增长时,无需重新分区迁移数据并内嵌水平扩展性的方法。最后,用户还要面对容错和数据可用性问题,采用提高扩展性的机制,用户最终会得到一个NoSQL的解决方案,更确切地说,HBase可以满足以上多种需求。

1.3.3 数据库的范式化和反范式化

不同的规模,经常需要设计不同的系统结构,对这种原则的最佳描述是:反范式化、复制和智能的主键(Denormalization, Duplication, and Intelligent Keys,简称DDI ⑯)。这就需要重新思考在类似BigTable的存储系统中如何才能高效合理地存储数据。

部分原则是采用反范式化模式,例如将数据复制到多张表中,这样在读取的时候就不需从多张表中聚合数据了。或者预先物化所需的视图,一次优化从而避免进一步的处理来提高读取性能。

这一主题在第9章里有更详细的介绍,主要阐述了如何充分利用HBase的特性去解决实际问题。让我们来看一个例子,理解传统的关系数据库模型转到列式存储的HBase的几点基本原则。

再来看看HBase短网址,即Hush,Hush允许用户将长网址映射为短网址(short URL),见图1-2表示的实体关系图(entity relationship diagram,ERD,简称ER图)。在附录E⑰ 中可以查看完整的SQL模式。


《HBase权威指南》一1.3 非关系型数据库系统Not-Only-SQL(简称NoSQL)

短网址存储在shorturl表中,用户可以点击短网址来链接到完整网址。系统会跟踪每次点击,记录该网址的使用次数,还会记录一些其他信息,例如,点击该链接的用户所在的国家。这些信息会记录在click表中,这个表的功能类似于一个计数器,以天为周期统计每天的访问量。

用户信息存储在user表中,用户可以在Hush网站上注册并创建个人短网址列表,同时也可以在此网站上增加描述。user表与shorturl表之间维护了一个外键关系。

系统还会在后台下载链接到的页面,并提取一些TITLE之类的HTML标签。将整个页面保存下来的目的是,供后续的异步任务进行处理和分析。这些内容都由url表存储。

每个链接页面只存储一份,不过,由于许多用户可能会链接同一个长网址,并且还想保存他们自己的详细信息,例如,使用统计信息,因此会在短链接表中创建多个项来加以区分。通过这段逻辑,将url表、shorturl表和click表关联在了一起。

这使得通过统计原始的短网址标识refShortId,就可以统计任意一个短网址映射到同一个长网址的使用率。shortId和refShortId利用散列ID的方式被唯一地分配给了短网址。例如:

http://hush.li/a23eg

在上述地址中,散列ID是a23eg。

图1-3展现了Hush应用在HBase中的对应模式。每个短网址都存储在独立的表shorturl中,表中还包含了使用统计信息,按统计时间范围不同,存放于不同的列族中,同时每个值都有其生存期(time-to-live)。列名是日期和一个可选维度后缀的组合,例如国家代码,列值则是对应计数器的值。

下载下来的页面和提取的详细信息存储在url表中,并且要通过压缩最大限度地减少存储量,因为存储的页面主要是HTML,这种格式本身冗余量大,并且包含了大量文本。


《HBase权威指南》一1.3 非关系型数据库系统Not-Only-SQL(简称NoSQL)

系统通过user-shorturl表可以快速查到指定用户的所有短网址标识。这个功能被用在用户主页中,只要用户一登录就会被记录下来。user表存储着实际用户的详细信息。

虽然表的数量相同,都是4个,但表的含义发生了变化:clicks表被合并到了shorturl表中,统计列使用日期为列键,格式为YYYYMMDD,例如,20110502,这样用户可以顺序访问数据。新增的user-shorturl表代替了外键,使查询用户相关信息变得更为快捷。

有非常多的方法来转换一对一、一对多、多对多的关系,以适应HBase的底层架构。这种简单的例子有多种实现方式,用户需要充分理解HBase存储设计的潜在能力,然后深思熟虑地决定用哪一种实现方式。

对稀疏矩阵、宽表、列式存储的支持使得数据在存储的时候无需范式化,同时也可以避免查询时采用开销很大的JOIN操作聚合数据。使用智能的主键可以控制数据怎样去存储以及存储在什么位置。由于可以使用行键的部分内容进行范围检索,行键作为组合键设计时,与字典序左部分为头的索引效果相似。因此,正确的设计能够使性能不会因为数据增长而下降,例如当数据条目从10条增加到1000万条时,系统仍旧可以保持相同的读写性能。

上一篇:redis 与关系型数据库的对比


下一篇:tomcat + apache +jkmod 配置php,jsp共存