本月初,macOS 平台的开源包管理系统 Homebrew 宣布 Homebrew-core 将移除 MongoDB 的支持 (详见:https://github.com/Homebrew/homebrew-core/pull/43770)。
虽然事件的起因是MongoDB修改了开源协议,MongoDB 去年 10 月份宣布将开源 License 从 GNU AGPLv3 切换到 Server Side Public License(SSPL),SSPL协议要求,使用该协议提供的软件服务的厂商,必须同样开源使用该服务的程序,这样做的目的在于希望各厂商使用MongoDB 的商业云产品。
但后续的发展似乎并未按照预期:Linux 发行版 Debian和 Fedora 决定不使用 SSPL 协议下的软件。2019 年 1 月中旬,红帽宣布弃用MongoDB。再加上最近的 Homebrew,MongoDB似乎被推上了风口浪尖。
但作为技术人员,这些并不是我们需要过度关注的, 但可以从中引发一些思考。
回顾数据模型发展历史
近年来,随着NoSQL概念的兴起,再次引发了一轮如何最佳表示数据关系的争论。
然而当我们追溯到最早的数据库系统,回顾数据模型的发展历史,可能会发现,事物的发展像是一个环。
层次模型
数据库最初的设计模型是层次模型,比较典型的是IBM的信息管理系统(IMS),与目前的文档模型的JSON结构有很多相似的地方,可以很好的支持一对多的关系,但如果存在多对多的关系,解决方式就只能手动解析记录间的引用。
网络模型
为了解决这种局限性,后续提出了两种解决方案,一种演化成了目前的关系模型,还有一种叫做网络模型。网络模型将层次模型中每个记录只能有一个父节点的限制取消,一条记录可以有多个父节点,连接各种网络模型的,可以类比于指针,访问方式就像链表遍历一样,找到对应的路径,这种方式无论对于查询还是查找,都存在着极大的困难。
关系模型
在这种情况下,关系模型显得更胜一筹,相比之下,不再有复杂的嵌套查询逻辑和复杂的访问路径,用外键来对不同的关系(表)进行连接,方便的支持任意条件的查询,还可以添加索引以支持查询优化,除了索引,关系型数据库还可以通过查询优化器,来选择最合适的索引以提高查询速度。
文档模型
随着业务复杂性的提升,程序员们又发现在某些业务场景下,并不需要将所有的数据关系化,比如下面这个在《数据密集型应用系统设计》中提到的例子,如何用文档型数据库和关系型数据库分别表示简历。一个人可以有多个职位信息和教育信息,所以是一个典型的一对多的关系,如果是关系模型,就是下图的样子,教育,职位等信息都放在单独的表中,并通过外键关联。
如果是通过JSON字符串的形式,则表现为下面的格式,看起来有着非常清晰关系:
但在软件开发领域,最大的不变就是变化,如果此时需求有如下的变动,文档型数据库就会显得力不从心:
- 将positions中的organization(组织)和education中的school_name(学校)作为实体而不是字符串
- 实现用户的推荐,一个用户可以推荐其他用户的建立
面对如下两种需求,文档型数据库目前的做法就只能是在应用层面做处理。又或者,如果建立联结,可能会变成下面的样子,是不是感觉又回到了上面提到的网络模型,但目前文档数据库还没有引入这样的操作,只是我做的假设。
我将上述的发展历程做成图,会发现一切都很有趣:
总结关系模型和文档模型
接下来将从以下几个方面对关系模型和文档模型进行总结:
应用代码
- 业务数据是类似文档的结构,通常一次加载,不需要对其具体内容进行额外处理,关系模型使代码复杂度增加。
- 需要引用文档中的嵌套内容,或者需要连接操作,文档模型就只能通过业务代码完成,通用增加了复杂度。
模式灵活性
显然,文档模型的模式更加灵活。模式灵活对于修改数据格式更加友好。
但如果大部分数据具有相同数据结构,模式是一种很好的约束形式。
查询
如果查询遵循局部性原理,那么文档模型有一定的优势,因为不需要去跨多个表进行连接查询,节省了多次的IO操作。
但需要考虑的是,如果实际场景只需要文档中的小部分内容,那么加载整个文档,也会造成资源浪费。
最后想说的是,MySQL 5.7开始,也开始引入JSON这种非结构化的数据类型,有着逐步融合的趋势。虽然看起来,文档结构与最初的层次模型很相似,但也并不代表我们回到了原地,在这个发展历程中总结到的经验,不会让我们盲目选择某种模型,数据模型间可以相互补充,在合适的业务场景下选择合适的数据模型,而不是争论哪种更好,才是我们应该做的事情。
参考:
《数据密集型应用系统设计》