系统架构设计之数据模型的选型难题
这是一个纷杂而无规则的世界,越想忘掉的事情,越难忘记。
正文
数据模型不仅对软件编写方式,还对如何思考待解决的问题都有影响。
大多数应用程序都是通过叠加一层层的数据模型构建而来 。因此每层都面临问题:如何将其用下一层表示?如:
- 作为一名开发,观测现实世界(包括人员、组织 、货物 、行为、资金流动、传感器等) ,通过对象或数据结构及操作这些数据结构的API来对其建模,这些数据结构往往特定于该应用
- 当需要存储这些数据结构时,可采用通用数据模型(例如JSON XML文档、关系数据库中的表或图模型)来表示
- DBA接着决定用何种内存、磁盘或网络的 节格式来表示上述JSON/XML/关系/图形数据。数据表示需要支持多种方式的查询询、搜索、操作和处理数据(系列三时介绍)
- 在更下一层,硬件工程师则需要考虑用电流、光脉冲、磁场等来表示字节
复杂的应用程序可能会有更多的中间层:如基于API来构建上层API ,但基本思想一样:每层都通过提供一个简洁的数据模型来隐藏下层的复杂性。这些抽象机制使得不同团队可高效协作,例如数据厂商的工程师和使用数据库的开发很好的协作。
数据模型有很多类型,每种都有其最佳实践。考虑到数据模型对其上的软件应用有巨大影响( 哪些可以做、不能做),因此需慎重选择适合业务的数据模型。
本本会介绍一些用于数据存储和查询的通用数据模型 (上面提到的第2点)。尤其,将比较关系模型 、文档模型和一些基于图的数据模型。还讨论多种查询语言并比较使用场景。
1 关系模型与文档模型
如今SQL是最著名的数据模型 ,它基于Edgar Codd 1970年提出的关系模型:数据被组织成关系(relations),在SQL中称为表(table),其中每个关系都是元组(tuples)的无序集合(SQL中称为行)。
关系模型曾经只是理论建议, 很多人怀凝它是否能被高效实现。但20世纪80年代中期,关系数据库管理系统( RDBMS )和SQL已成为大多数需要存储、查询具有某种规则结构的数据的首选工具。关系数据库的主导地位持续至今已有三十多年,算得上是计算机史的不朽传奇。
关系数据库核心在于商业数据处理, 20世纪60年代和70年代主要运行在大型计算机。如今来看,用例很常见,主要是:
- 事务处理(包括输入销售和银行交易、订票 、仓库库存 )
- 批处理(例如客户发票、工资单 、报告
当时的其他数据库迫使开发人员考虑数据的内部表示。关系模型的目标就是将实现细节隐藏在更简洁的接口下。20世纪70年代和80代初期,网络模型和层次模型是两个主要选择,但最终关系模型主宰该领域。
对象数据库曾在20世纪80年代后期和90年代初期起起伏伏。XML数据库则出现在21世纪初,但也仅限于利基市场。关系模型的竞争者都昙花一现。
如今关系数据库超出它们最初的商业数据处理范围,推广到各种案例:在线发布、论坛、社
交网络、电子商务 游戏、 SaaS。
2 NoSQL
21世纪,NoSQL成为推翻关系模式主导地位的有力竞争者。NoSQL名字不恰当,因为它其实并不代表具体技术,最初只是作为吸引人眼球的Twitter标签频频出现在2009年的开源、分布式及非关系数据库的见面会。现在很多新兴数据库系统总是会打上NoSQL标签,而其含义也已经被逆向重新解释为“不仅仅是SQL”。
采用NoSQL数据库的驱动因素:
- 比关系数据库更好的扩展性需求包括支持超大数据集或超高写入吞吐量
- 普遍偏爱免费和开源软件而非商业数据库产品
- 关系模型不能很好支持一些特定查询
- 对关系模式某些限制性感到沮丧,渴望更具动态和表达力的数据模型
不同应用程序有不同需求,没有银弹技术,可预见的将来,关系数据库仍将继续与各种非关系数据存储一起使用,这种思路有时也被称为混合持久化。
3 对象-关系不匹配
现在大多数应用开发都采用OOP编程语言 ,由于兼容性问题,普遍对SQL数据模型抱怨:若数据存储在关系表,则应用层代码中的对象与表、行和列的数据库模型之间需要一个笨拙的转换层。模型之间的脱离有时被称为阻抗失谐(电子学术语:每个电路的输入和输出都有 一定的阻抗(交流电阻) 一个电路的输出连接到另一个电路的输入时,若两个电路的输出和输入阻抗匹配,则连接上的功率传输将被最大化,阻抗不匹配会导致信号反射和其他故障)。
像Hibernate对象关系映射(ORM) 框架减少了此转换层的样板代码,但也无法完全隐藏两个模型之间差异。
简历案例
如下展示了关系模式表示LinkedIn简历:
图一
整个简历可通过唯一标识符user id标识。像first_name、last_name字段在每个用户中只出现一次,所以建模为users表中的列。而大多数人在他们的职业中有一个以上工作,且可能有多个教育阶段和任意数量的联系信息。用户与这些项目之间是一对多,可用多种方式表示:
- 在传统SQL模型( SQL1999之前), 最常见的规范化表示是将职位、教育和联系信息放在单独表,并使用外键引用users表
- 之后的SQL标准增加了对结构化数据类型和XML数据的支持。这允许将多值数据存储在单行内,井支持在这些文档中查询和索引。Oracle、DB2、SQLServr、PostgreSQL都不同程度支持这些功能。一些数据库也支持JSON,如DB2、MySQL和PostgreSQL。
- 将工作、教育和联系信息编码为JSON或XML,存储在数据库的文本列,并由应用程序解释其结构和内容。对于此方法,通常不能使用数据库查询该编码列中的值
简历这样的数据结构,主要是个自包含的文档( document ),因此用JSON表示非常合适:
面向文档的数据库(如MongoDB)都支持该数据模型。
有人觉得JSON模型减少了应用程序代码和存储层之间的阻抗失配。然而,JSON作为数据编码格式也存在问题。缺乏模式常常被认为是一个优势~
JSON表示比关系模式表示的多表模式具有更好的局部性。若要在关系模式中读取一份简历,则:
- 要么执行多个查询(通过user_id查询每个表)
- 要么在users表及其从属表之间执行混乱的多路联结
而JSON表示方法,所有的相关信息都在一个地方,一次查询即可。
用户简历到用户的职位、教育历史和联系信息的 对多关系,意味着数据存在树状结构,JSON表示将该树结构显式化,一对多的关系形成树状结构:
4 多对一、多对多
上面案例的region_id、indu0stry_id 定义为ID ,而非纯文本字符串,如"Greater Seattle Area Philanthropy",why?
若用户界面是可以输入地区或行业的自由文本字段,则将其存储为纯文本字符串更有意义。但拥有地理区域和行业的标准化列表,并让用户从下拉列表或自动填充器中进行选择更有优势:
- 所有简历保持样式和输入值一致
- 避免歧义(例如,若存在一些同名城市)
- 易于更新:名字只保存一次,因此,若需要改变(例如,由于政治而更改城市名称),易全面更新。
- 本地化支持:当网站被翻译成其他语,标准化的列表方便本地化,因此地区和行业可以用查看者的母语显示
- 更好的搜索支持:例如,搜索华盛顿州的慈善家可匹配到过个简历,因为地区列表可以将西雅图属于华盛顿的信息编码进来(而从“大西雅图地区”字符串中并不能看出西雅图属于华盛顿)
无论是存储ID or 文本字符串,都涉及内容重复问题:
- 使用ID ,对人类有意义的信息(例如慈善这个词)只存储在一个地方,引用它的所有内容都使用ID(ID只在数据库中有意义
- 直接存储文本时, 则使用它的每条记录中都保存了一份这样的可读信息
使用ID的好处:对人类没有任何直接意义,所以永远不需要直接改变。即使ID标识的信息变化,他也能继续保持不变。任何对人类有意义的东西都可能变更。若这些信息被复制 ,则所有副本都得更新。这会导致更多写开销,且存在数据不一致风险。消除这种重复,也正是数据库规范化的核心思想。
而这种数据规范化需要表达多对一关系(许多人生活在同一地区, 在同一行业工作),这并不是很适合文档模型:
- 关系数据库由于支持联结操作,可方便通过ID引用其他表中的行
- 文档数据库中, 一对多的树状结构不需要联结,支持联结通常也很弱
若数据库本身不支持联结,则必须在应用程序代码中,通过对数据库进行多次查询来模拟联结(对于上述例子,地区和行业的列表很小且短期内不大可能变化, 应用程序可将其缓存在内存。但无论如何,联结工作其实从数据库转移到了应用层。
即使应用程序的初始版本非常适合采用无联结 文档模型 但随着应用支持越来越多功能,数据也变更互联一体化。例如,考虑可能对简历进行的变更:
- 组织和学校作为实体 前面定义中, organization(用户所在公司)、school_name (用户所在学校 )都是字符串。也许他们应该定义为实体的引用?然后每个组织、学校或大学都拥有自己的网页 (logo 、新闻发布源等) 。每个简历都能链接到相关组织和学校,包括他们的logo和其他信息
- 推荐假设添加这样一个新功能:一个用户能推荐其他用户。推荐显示在被推荐者的简历上,并附上推荐人的姓名和照片。若推荐人更新了他们的照片, 他们所写的任何推荐都需显示新照片。因此,推荐需要有一个到作者简历的引用。
如下图展示了这些新功能如何定义多对多。每个虚线矩形框数据可组织为一个文档,但指向组织、学校及其他用户的关系则需表示为引用,并且在查询时,需要联结操作。多对多关系来扩展简历:
文章转载自公众号: JavaEdge