1.前言
为了适应大数据应用场景的要求,hadoop以及nosql等与传统企业平台完全不同的新兴架构迅速地崛起。而下层技术基础的革命必将影响上层建筑:数据模型和算法。简单地将传统基于第四范式结构化关系型数据库的模型拷贝到新的引擎上,无异于削足适履,不仅增加了大数据应用开发的难度和复杂度,又无法发释放新框架的潜能。
该如何构建基于nosql的数据模型?现在能供参考的公开知识积累要么是空虚简单的一句“去规范化“或粗暴的宽表化(将query和应用需要访问的所有字段“排排坐“,放在一个有很多列的结构化表中),要么是针对具体工具或具体场景的实现细节,(如《hbase权威指南》中对于如何设计hbase主键的探讨)。没有一个像编程的设计模式一样的,在模型架构层面可以遵循的方法论。
在比较不同的nosql数据库时,通常使用功能以外其他各种指标,如可扩展性、性能和一致性。由于这些指标通常是使用nosql的初衷,所以无论从理论的角度还是实践的角度被深入地研究了,而像cap定理这样的分布式系统基础结论也同样适用于nosql系统。另一方面,在nosql的数据模型领域,却还没有很好地研究过,也缺乏关系数据库中那种系统性的理论。
我在这篇文章中从数据建模的角度对nosql家族系统做了比较简单的比较,并简要介绍几种常见建模技术。
2.nosql数据模型视图
要探索数据建模技术,必须先从系统性的nosql数据模型视图着手,这多多少少能帮助我们揭示其发展趋势以及相互之间的关系。下图描绘了主要nosql家族系统的虚拟“进化”过程,即键值存储,bigtable类型的数据库,文档数据库,全文搜索引擎,数据库和图形数据库:
首先,我们应该注意到,一般意义上讲,sql和关系型模型都是在很久以前就被设计出来,目的是为最终用户交互之用。这种面向用户的性质有极深的影响:
最终用户往往对汇总报表信息感兴趣而不是单独的数据项,因而sql这方面做了大量的工作。
不能指望作为自然人的用户能显式地控制并发性、完整性、一致性或者数据类型有效性。这就是为什么sql竭力关注于事务保证、schema和参照完整性。
另一方面,软件应用程序往往对在数据库内部做聚合没有太大的兴趣,而且至少在许多情况下,程序能够自己控制完整性和有效性。除此之外,剔除这些功能对于性能和可扩展性存储的影响极其重要。
新数据模型的演变开始了:
键-值存储是一个非常简单,但非常强大的模型。下面所描述的许多技术都完全适用于这个模型。
键值模型最致命的缺点之一就是不适合按范围处理主键的场景。有序的键-值模型突破了这一限制,并显著提高了聚合能力。
有序的键-值模型非常强大,但它不提供任何针对值(value)的建模框架。在一般情况下,值的建模可以由应用程序完成,但bigtable风格的数据库想得更加周到,它可以将值按照映射的映射的映射(map-of-maps-of-maps)进行建模,说得明确点,分别是列簇(column family)、列(column)和时间戳化的版本。
文档数据库对bigtable模式提出两个明显的改善。第一,值可以被声明为任意复杂的schema,而不仅仅是一个映射的映射(map-of-maps)。第二,至少有一些产品实现了被数据库管理的索引。就这个意义上来讲,全文搜索引擎也可以同样被认为提供了灵活的schema和自动化的索引。他们之间主要区别在于,文档数据库是根据字段名对索引进行编组,而搜索引擎是使用字段值对索引编组。值得注意的是像oracle coherence这样的键-值存储系统增加了索引和内嵌入口处理器的功能,正逐步向文件数据库演进。
最后,图形数据模型可以被视为有序的键-值模型朝另外一个方向的进化。图形数据库允许对业务实体进行非常透明的建模(这个东西取决于那个东西),而分层建模技术在这方面用的是另外的数据模型,但也可与之媲美。图形数据库和文件数据库息息相关,因为许多实现允许建模的值是映射或者文档。
3.nosql数据建模的一般注意事项
与关系型建模不同,nosql数据建模往往是从特定查询的应用开始:
关系型建模是典型地被手上可用数据的结构所驱动。设计主要围绕着的是“我有什么样的答案?”
nosql数据建模通常由特定应用的访问模式所驱动,比如需要支持的查询类型。设计主要围绕着的是“我有什么问题?”
nosql数据建模往往比关系数据库建模需要更加深入地了解数据结构和算法。在这篇文章中,我介绍了几个著名的数据结构,他们虽然非nosql所特有,但对于实际的nosql建模非常有用。
数据复制和去规范化是一等公民。
关系数据库在对分层或图形数据进行建模和处理时不是很方便。图形数据库显然是这个领域的完美解决方案,但实际上大多数的nosql也都非常善于解决这样的问题。这就是为什么这篇文章为分层数据建模单独写了一个章节。
虽然数据建模技术基本上和具体实现无关,但我还是列出了在写这篇文章时我能想到的产品:
键值存储:oracle coherence,redis,kyoto cabinet
bigtable风格的数据库: apache hbase,apache cassandra
文档数据库: mongodb,couchdb
全文搜索引擎: apache lucene,apache solr
图形数据库:neo4j,flockdb
4.概念技术
本节专门介绍nosql数据建模的基本原则。
1、 去规范化(denormalization)
可以将去规范化定义为把相同的数据复制到多个文档或数据表中,这样可以简化/优化查询处理,或者让用户数据能匹配一个特定的数据模型。在本文的大多数技术用到了这样或那样的去规范化。
一般来说,去规范化用于以下的折衷:
查询的数据量或每次查询io**与总数据量的折衷。去规范化可以将一个查询所需的所有数据组合起来存放到同一个地方。这通常意味着对相同数据的不同的查询会访问不同的数据组合。因此,数据需要被复制多份,也就意味着增加了总数据量。
处理复杂性与总数据量的折衷。建模时的规范化和相应查询的连接(join)明显增加了查询处理器的复杂度,在分布式系统中尤为明显。去规范化允许将数据按照查询友好的方式存储,从而简化查询的处理。
适用性:键值存储,文档数据库, bigtable风格的数据库
2、 聚合(aggregates)
所有主流nosql都提供了这样或那样的松散schema(soft schema)支持:
键值存储和图形数据库通常不对值进行约束,所以值可能是任意格式。另外,也可以通过使用组合键将一个业务实体表示为多条记录。例如,可以将一个用户帐户建模为userid_name,userid_email,userid_messages等组合键表示的一个实体集合。如果用户没有电子邮件或消息,然后相应的实体不会被记录。
bigtable模式也支持松散schema,因为一个列簇是可变的列集合,一个单元格又能存储不定数目的数据版本。
文档数据库天生就没schema,虽然某些文档数据库允许在数据输入时使用用户定义的schema进行验证。
松散schema允许使用复杂的内部结构(嵌套实体)构造实体的类,也允许改变特定实体的结构。这个更能带来了两个重要的便利:
通过嵌套的实体,最小化了一对多的关系,也因此减少了连接(join)。
异构业务实体的模型可以使用一个文档集合或者一个数据表。松散schema掩藏了这种建模和业务实体之间“技术”上的差异。
我们用下面的图来说明这些便利。该图描绘了对电子商务领域中一个产品实体进行的建模。首先我们可以认为所有的产品都有一个id、价格(price)和描述(description)。进一步来看,我们发现不同类型的产品有不同的属性,如图书包含作者信息,而牛仔裤有长度属性。这些属性中间的某些属性天生就有一对多或这多对多的特性,比如音乐唱片中的曲目。
更进一步来看,可能有些实体不可能使用固定的类型进行建模。例如,不同品牌的牛仔裤的属性是不固定的,而每个制造商出产的牛仔裤的属性也是不一致的。在规范化的关系型数据模型中虽然这些问题都可以解决,但方法很猥琐。松散schema软架构允许只使用一个聚合(aggregation)(产品)就能对所有类型的产品及其属性进行建模:
内嵌的去规范化会在性能和一致性上对更新操作造成很大的影响,所以要特别注意更新过程。
适用性:键值存储,文档数据库, bigtable的风格数据库
3、 应用端连接(application side joins)
很少有nosql解决方案支持连接。nosql“问题导向”性质的后果就是,通常在设计时处理join,而关系型模型是在执行查询时处理join。查询时处理join几乎肯定会带来性能上的损失,但在许多情况下,可使用去规范化和聚合,即嵌入嵌套实体来避免join。当然,join在许多情况下是不可避免的,而且应该由应用程序处理。主要的用例:
多对多关系往往是通过链接(link)建模的,这需要join。
聚合操作往往不适合内部实体会被频繁修改的场景。通常更好的办法是将发生的事情作为一条新的记录保留,并在查询的时候将所有记录做join,而不是去更改值。例如,对于一个信息系统而言,可以用嵌套包含了message实体的user实体来建模。但是,如果会经常地添加消息,更好的办法可能是把message提取出来作为独立实体,并在查询时再将其与user进行连接:
适用性:键值存储,文档数据库, bigtable风格数据库,图形数据库
5.一般建模技术
在本节中,我们将讨论适用于各种nosql实现的一般建模技术。
1、 原子聚合(atomic aggregates)
许多nosql解决方案提供了有限的事务支持,虽然有些nosql不支持。在某些情况下,人们还可以使用分布式锁或应用程序管理的mvcc机制实现事务行为,但常见的是使用聚合技术来对数据建模,以保证一些acid特性。
强大的事务处理机制对于关系型数据库而言是不可或缺的,其中原因之一就是规范化的数据通常需要在多个地方进行更新。另一方面,聚合允许一个单个业务实体存储为一个文件,行或键值对,从而可以对其进行原子性的更新:
当然,做为一种数据建模技术,原子聚合并不是一个完善的事务型解决方案,但如果存储能提供原子性、锁或者tas(test-and-set,测试并设置)指令上的一些担保,那原子聚合就是可行的。
(译者注:即将需要事务性操作的业务数据聚合放在一起,存储在一个noqsql提供或者应用能提供原子性操作的数据结构中。使用hbase时,将某个用户某个业务的所有数据,如上图,用一行存储就是这种模式的应用。)
适用性:键值存储,文档数据库, bigtable风格数据库
2、 可枚举主键(enumerable keys)
也许无序键-值数据模型最大的好处就是可以通过将主键哈希的办法把实体数据分别存储在多个服务器上。排序使事情变得更加复杂,但是即使存储不提供这样的功能,有时应用程序也能利用到有序主键的优势。让我们将对电子邮件建模作为一个例子:
某些nosql存储提供原子计数器,能生成一个顺序化的id。在这种情况下,可以使用userid_messageid作为一个复合键来存储消息。如果最新的消息id是已知的,那就可以遍历以前的消息。另外,对于任何一个给定的消息id,也可以向前或向后进行遍历。
也可以将消息分桶(bucket),例如,每天的数据放到一个桶里。这样就允许从任何指定日期或当前日期开始,向前或向后遍历一个邮箱。
适用性:键值存储
(译者注:能利用主键的一些自然或业务维度的特征,将随机读写转换为顺序读写能提高遍历性能,同时能方便应用逻辑编写。但需要注意对分布式部署时并发写的影响以及对于业务的过度耦合。对于无序主键和有序主键的讨论可以参见《hbase权威指南》中schema设计章节。)
3、 降维(dimensionality reduction)
降维这种技术允许将一个多维数据模型映射到一个键-值模型或其他非多维模型。
传统的地理信息系统使用四叉树(quadtree)或r树(r-tree)的某种变形来做索引。这些结构需要就地完成更新操作,因此在数据量很大时,维护开销相当的大。另一种方法是对这个二维结构进行遍历,并将其扁平化为一个普通的条目列表。使用这种技术的一个众所周知的例子是geohash。 geohash使用类似z形状的路线来扫描整个二维空间,每次移动根据行进方向被编码为0或1。交错位的经度和纬度上的变更移动以及移动。编码过程在下图中进行了说明,其中黑色和红色位分别代表经度和纬度:
如图所示,geohash的一个重要特性是能够通过这种逐位编码的近似程度来估计区域之间的距离。geohash编码允许使用简单普通的数据模型来存储地理信息,比如用有序键值保存空间上的联系。[6.1]讲述了bigtable中的降维技术。更多有关geohash及其相关技术的信息可以在[6.2]和[6.3]中找到。
(译者注:通过交织编码方式来能将原本需要多维度标示的数据,如cube,存储到一维的键值存储系统中,这是一种非常重要的建模模式:提供了不同缩放等级下在多维空间中邻接的数据仍然顺序存储,遍历高效;同时不同主键从前向后的相似度和空间距离的远近相一致,能通过键值的简单顺序比较判断其位置“相似度”。
它的应用远远不只地理信息的表示,有多个维度属性不同粒度的数据表示都能用到这个技术,比如线下销售交易数...