原文来自“NoSQL Data Modeling Techniques”,由酷壳网陈皓编译《NoSQL数据建模技术》。这篇文章看完以后,你可能会对NoSQL的数据结构会有些感受。个人感受是,关系型数据库想把一致性,完整性,索引,CRUD都干好,NoSQL只干某一种事,可是牺牲了不少别的东西。整体来讲,我以为NoSQL更适合作Cache。html
下面是正文:正则表达式
NoSQL数据库常常被用做不少非功能性的地方,如,扩展性,性能和一致性的地方。这些NoSQL的特性在理论和实践中都正在被大众普遍地研究着,研究的热点正是那些和性能分布式相关的非功能性的东西,咱们都知道CAP 理论被很好地应用于了NoSQL系统中(陈皓注:CAP即,一致性(Consistency),可用性(Availability),分区容忍性(Partition tolerance),在分布式系统中,这三个要素最多只能同时实现两个,而NoSQL通常放弃的是一致性)。但在另外一方面,NoSQL的数据建模技术却由于缺少像关系型数据库那样的基础理论没有被世人很好地研究。这篇文章从数据建模方面对NoSQL家族进行了比较,并讨论几个常见的数据建模技术。算法
要开始讨论数据建模技术,咱们不得不或多或少地先系统地看一下NoSQL数据模型的成长的趋势,以此咱们能够了一些他们内在的联系。下图是NoSQL家族的进化图,咱们能够看到这样的进化:Key-Value时代,BigTable时代,Document时代,全文搜索时代,和Graph数据库时代:(陈皓注:注意图中SQL说的那句话,NoSQL再这样发展下去就是SQL了,哈哈。)sql
NoSQL Data Modelsshell
首先,咱们须要注意的是SQL和关系型数据模型已存在了很长的时间,这种面向用户的天然性意味着:数据库
另外一方面,SQL可让软件应用程序在不少状况下不须要关心数据库的数据聚合,和数据完整性和有效性进行控制。而若是咱们去除了数据一致性,完整性这些东西,会对性能和分布存储有着重的帮助。正由于如此,咱们才有数据模型的进化:数组
本文剩下的章节将向你介绍数据建模的技术实现和相关模式。可是,在介绍这些技术以前,先来一段序言:服务器
下面是NoSQL的分类表,也是我用来写这篇文章时作实践的产品:数据结构
这一节主要介绍NoSQL数据模型的基本原则。并发
反规格化Denormalization能够被认为是把相同的数据拷贝到不一样的文档或是表中,这样就能够简化和优化查询,或是正好适合用户的某中特别的数据模型。这篇文章中所说的绝大多数技术都或多或少地导向了这一技术。
整体来讲,反规格化须要权衡下面这些东西:
适用性:Key-Value Store 键值对数据库,Document Databases文档数据库,BigTable风格的数据库。
全部类型的NoSQL数据库都会提供灵活的Schema(数据结构,对数据格式的限制):
灵活的Schema容许你能够用一种嵌套式的内部数据方式来存储一组有关联的业务实体(陈皓注:相似于JSON这样的数据封装格式)。这样能够为咱们带来两个好处。
下图示意了这两种好处。图中描给了电子商务中的商品模型(陈皓注:我记得我在“挑战无处不在”一文中说到过电商中产品分类数据库设计的挑战)
对于关系型数据库来讲,要设计这样的数据模型并不简单,并且设计出来的绝对离优雅很远很远。而咱们NoSQL中灵活的Schema容许你使用一个聚合Aggregate (product) 能够建出全部不一样种类的商品和他们的不一样的属性:
Entity Aggregation
上图中咱们能够比较关系型数据库和NoSQL的差异。可是咱们能够看到在数据更新上,非规格化的数据存储在性能和一致性上会有很大的影响,这就是咱们须要重点注意和不得不牺牲的地方。
适用性: Key-Value Store键值对数据库,Document Databases文档数据库,BigTable风格的数据库。
表联结基本上不被NoSQL支持。正如咱们前面所说的,NoSQL是“面向问题”而不是“面向答案”的,不支持表联结就是“面向问题”的后果。表的联结是在设计时被构造出来的,而不是在执行时建造出来的。因此,表联结在运行时是有很大开销的(陈皓注:搞过SQL表联结的都知道笛卡尔积是什么东西,大能够在参看之前酷壳的“图解数据库表Joins”),可是在使用了Denormalization和Aggregates技术后,咱们基本不用进行表联结,如:大家使用嵌套式的数据实体。固然,若是你须要联结数据,你须要在应用层完成这个事。下面是几个主要的Use Case:
适用性: Key-Value Store键值对数据库,Document Databases文档数据库,BigTable风格的数据库,Graph Databases图数据库。
在本书中,咱们将讨论NoSQL中各类不一样的通用的数据建模技术。
不少NoSQL的数据库(并非全部)在事务处理上都是短板。在某些状况下,他们能够经过分布式锁技术或是应用层管理的MVCC技术来实现其事务性(陈皓注:可参看本站的“多版本并发控制(MVCC)在分布式系统中的应用”)可是,一般来讲只能使用聚合Aggregates技术来保证一些ACID原则。
这就是为何咱们的关系型数据库须要有强大的事务处理机制——由于关系型数据库的数据是被规格化存放在了不一样的地方。因此,Aggregates聚合容许咱们把一个业务实体存成一个文档、存成一行,存成一个key-value,这样就能够原子式的更新了:
Atomic Aggregates
固然,原子聚合Atomic Aggregates这种数据模型并不能实现彻底意义上的事务处理,可是若是支持原子性,锁,或test-and-set指令,那么,Atomic Aggregates是能够适用的。
适用性: Key-Value Store键值对数据库,Document Databases文档数据库,BigTable风格的数据库。
也许,对于无顺序的Key-Value最大的好处是业务实体能够被容易地hash以分区在多个服务器上。而排序了的key会把事情搞复杂,可是有些时候,一个应用能从排序key中得到不少好处,就算是数据库自己不提供这个功能。让咱们来思考下email消息的数据模型:
适用性: Key-Value Store键值对数据库。
Dimensionality Reduction降维是一种技术能够容许把一个多维的数据映射成一个Key-Value或是其它非多给的数据模型。
传统的地理位置信息系统使用一些如“四分树QuadTree”或“R-Tree”来作地理位置索引。这些数据结构的内容须要被在适当的位置更新,而且,若是数据量很大的话,操做成本会很高。另外一个方法是咱们能够遍历一个二维的数据结构并把其扁平化成一个列表。一个众所周知的例子是Geohash(地理哈希)。一个Geohash使用“之字形”的路线扫描一个2维的空间,并且遍历中的移动能够被简单地用0和1来表示其方向,而后在移动的过程当中产生0/1串。下图展现了这一算法:(陈皓注:先把地图分红四份,经度为第一位,纬度为第二位,因而左边的经度是0,右边的是1,纬度也同样,上面是为1,下面的为0,这样,经纬度就能够组合成01,11,00,10这四个值,其标识了四块区域,咱们能够如此不断的递归地对每一个区域进行四分,而后能够获得一串1和0组成的字串,而后使用0-9,b-z去掉(去掉a, i, l, o)这32个字母进行base32编码获得一个8个长度的编码,这就是Geohash的算法)
Geohash Index
Geohash的最强大的功能是使用简单的位操做就能够知道两个区域间的距离,就像图中所示(陈皓:proximity框着的那两个,这个很像IP地址了)。Geohash把一个二维的坐标生生地变成了一个一维的数据模型,这就是降维技术。BigTable的降维技术参看到文章后面的[6.1]。更多的关于Geohash和其它技术能够参看[6.2] 和 [6.3]。
适用性: Key-Value Store键值对数据库,Document Databases文档数据库,BigTable风格的数据库。
Index Table索引表是一个很是直白的技术,其能够你在不支持索引的数据库中获得索引的好处。BigTable是这类最重要的数据库。这须要咱们维护一个有相应存取模式的特别表。例如,咱们有一个主表存着用户账号,其能够被UserID存取。某查询须要查出某个城市里全部的用户,因而咱们能够加入一张表,这张表用城市作主键,全部和这个城市相关的UserID是其Value,以下所示:
Index Table Example
可见,城市索引表的须要和对主表用户表保持一致性,所以,主表的每个更新可能须要对索引表进行更新,否则就是一个批处理更新。不管哪一个方式,这都会损伤一些性能,由于须要保持一致性。
Index Table索引表能够被认为是关系型数据库中的视图的等价物。
适用性:BigTable数据库。
Composite key键组合是一个很经常使用的技术,对此,当咱们的数据库支持键排序时能获得极大的好处。Composite key组合键的拼接成为第二排序字段可让你构建出一种多维索引,这很像咱们以前说过的 Dimensionality Reduction降维技术。例如,咱们须要存取用户统计。若是咱们须要根据不一样的地区来统计用户的分布状况,咱们能够把Key设计成这样的格式 (State:City:UserID),这样一来,就使得咱们能够经过State到City来按组遍历用户,特别是咱们的NoSQL数据库支持在key上按区查询(如:BigTable类的系统):
Composite Key Index
适用性: BigTable 数据库。
Composite keys键组合技术并不只仅能够用来作索引,一样能够用来区分不用的类型的数据以支持数据分组。考虑一个例子,咱们有一个海量的日志数组,这个日志记录了互联网上的用户的访问来源。咱们须要计算从某一网站过来的独立访客的数量,在关系型数据库中,咱们可能须要下面这样的SQL查询语句:
咱们能够在NoSQL中创建以下的数据模型:
Counting Unique Users using Composite Keys
这样,咱们就能够把数据按UserID来排序,咱们就能够很容易把同一个用户的数据(一个用户并不会产生太多的event)进行处理,去掉那些重复的站点(使用hash table或是别的什么)。另外一个可选的技术是,咱们能够对每个用户创建一个数据实体,而后把其站点来源追加到这个数据实体中,固然,这样一来,数据的更新在性能相比之下会有必定损失。
适用性: Ordered Key-Value Store 排序键值对数据库, BigTable风格的数据库。
这个技术更多的是数据处理技术,而不是数据建模技术。尽管如此,这个技术仍是会影响数据模型。这个技术最主要的想法是使用一个索引来找到知足某条件的数据,可是把数据聚合起须要使用全文搜索。仍是让咱们来讲一个示例。仍是用上面那个例子,咱们有不少的日志,其中包括互联网用户和他们的访问来源。让咱们假定每条记录都有一个UserID,还有用户的种类 (Men,Women,Bloggers,等),以及用户所在的城市,和访问过的站点。咱们要干的事是,为每一个用户种类找到知足某些条件(访问源,所在城市,等)的的独立用户。
很明显,咱们须要搜索那些知足条件的用户,若是咱们使用反转搜索,这会让咱们把这事干得很容易,如: {Category -> [user IDs]} 或 {Site -> [user IDs]}。使用这样的索引,咱们能够取两个或多个UserID要的交集或并集(这个事很容易干,并且能够干得很快,若是这些UserID是排好序的)。可是,咱们要按用户种类来生成报表会变得有点麻烦,由于咱们用语句可能会像下面这样
但这样的SQL很没有效率,由于category数据太多了。为了应对这个问题,咱们能够创建一个直接索引 {UserID -> [Categories]}而后咱们用它来生成报表:
Counting Unique Users using Inverse and Direct Indexes
最后,咱们须要明白,对每一个UserID的随机查询是很没有效率的。咱们能够经过批查询处理来解决这个问题。这意味着,对于一些用户集,咱们能够进行预处理(不一样的查询条件)。
适用性: Key-Value Store键值对数据库,Document Databases文档数据库,BigTable风格的数据库。
树形或是任意的图(需反规格化)能够被直接打成一条记录或文档存放。
Tree Aggregation
适用性:Key-Value键值对数据库,Document Databases文档数据库
Adjacency Lists邻接列表是一种图–每个结点都是一个独立的记录,其包含了全部的父结点或子结点。这样,咱们就能够经过给定的父或子结点来进行搜索。固然,咱们须要经过hop查询遍历图。这个技术在广度和深度查询,以及获得某个结点的子树上没有效率。
适用性:Key-Value键值对数据库,Document Databases文档数据库
Materialized Paths能够帮助避免递归遍历(如:树形结构)。这个技术也能够被认为是反规格化的一种变种。其想法是为每一个结点加上父结点或子结点的标识属性,这样就能够不须要遍历就知道全部的后裔结点和祖先结点了:
Materialized Paths for eShop Category Hierarchy
这个技术对于全文搜索引擎来讲很是有帮助,由于其能够容许把一个层级结构转成一个文档。上面的示图中咱们能够看到全部的商品或Men’s Shoes下的子分类能够被一条很短的查询语句处理——只须要给定个分类名。
Materialized Paths能够存储一个ID的集合,或是一堆ID拼出的字符串。后者容许你经过一个正则表达式来搜索一个特定的分支路径。下图展现了这个技术(分支的路径包括告终点自己):
Query Materialized Paths using RegExp
适用性:Key-Value键值对数据库,Document Databases文档数据,Search Engines搜索引擎
Nested sets嵌套集是树形结构的标准技术。它被普遍地用在了关系性数据库中,它彻底地适用于Key-Value键值对数据库和Document Databases文档数据库。这个技术的想法是把叶子结点存储成一个数组,并经过使用索引的开始和结束来映射每个非叶子结点到一个叶子结点集,就以下图所示同样:
Modeling of eCommerce Catalog using Nested Sets
这样的数据结构对于immutable data不变的数据有很是不错的效率,由于其点内存空间小,而且能够很快地找出全部的叶子结点而不须要树的遍历。尽管如此,在插入和更新上须要很高的性能成本,由于新的叶子结点须要大规模地更新索引。
适用性:Key-Value Stores键值数据库,Document Databases文档数据库
搜索引擎基本上来讲和扁平文档一同工做,如:每个文档是一个扁平的字段和值的例表。这种数据模型的用来把业务实体映射到一个文本文档上,若是你的业务实体有很复杂的内部结构,这可能会变得颇有挑战。一个典型的挑战是把一个有层级的文档映映射出来。例如,文档中嵌套另外一个文档。让咱们看看下面的示例:
Nested Documents Problem
上面的每个业务实体代码一种简历。其包括了人名和一个技能列表。我把这个层级文档映射成一个文本文档,一种方法是建立Skill和Level字段。这个模型能够经过技术或是等级来搜索一我的,而上图标注的那样的组合查询则会失败。(陈皓注:由于分不清Excellent是不是Math仍是Poetry上的)
在引用中的[4.6]给出了一种解决方案。其为每一个字段都标上数字 Skill_i 和 Level_i,这样就能够分开搜索每个对(下图中使用了OR来遍历查找全部可能的字段):
Nested Document Modeling using Numbered Field Names
这样的方式根本没有扩展性,对于一些复杂的问题来讲只会让代码复杂度和维护工做变大。
适用性:Search Engines全文搜索
在附录[4.6]中给出了这个技术用来解决扁平层次文档。它用邻近的查询来限制可被查询的单词的范围。下图中,全部的技能和等级被放在一个字段中,叫 SkillAndLevel,查询中出现的“Excellent”和“Poetry”必需一个紧跟另外一个:
Nested Document Modeling using Proximity Queries
附录[4.3]中讲述了这个技术被用在Solr中的一个成功案例。
适用性:Search Engines全文搜索
Graph databases图数据库,如neo4j是一个出众的图数据库,尤为是使用一个结点来探索邻居结点,或是探索两个或少许结点前的关系。可是处理大量的图数据是很没有效率的,由于图数据库的性能和扩展性并非其目的。分布式的图数据处理能够被MapReduce 和 Message Passing pattern来处理。如:在我前一篇的文章中的那个示例。这个方法可让Key-Value stores, Document databases和BigTable-style databases适合于处理大图。