MongoDB 等 NoSQL 与关系型数据库相比,有什么优缺点及适用场景?

MongoDB 的项目的时候,我们绝对没有想到它今天的样子。我们只有一个信念:让开发者更高效。MongoDB 诞生于在庞大复杂的业务部署中使用关系型数据库给我们带来的沮丧。我们着手建造一个我们自己想用的数据库。这样,每当开发者想写一个应用时,他们可以专注于应用本身,而不用围着数据库转。

Eliot Horowitz, MongoDB 的十年,一个创始人的回顾

2017年10月19日,MongoDB 纳斯达克上市当天,CTO Eliot 在公司博客写下这些话。从 MongoDB 纽约总部,穿过时代广场,到纳斯达克咫尺之遥。而这一天,距离 MongoDB 代码第一次提交,恰巧整整十年。

「让开发者更高效」概括了 MongoDB 相较于关系型数据库带来的全部价值。这要放在最近十年整个行业的大背景下理解。

软件行业的工资水平是出了名得高。这意味着软件开发的成本中,人力成本已经超越了硬件成本,成为成本里的大头。曾经长周期、瀑布式的开发流程被强调快速迭代的开发流程所替代。所有这些,加上你经常听说的热词,比如敏捷开发,微服务,DevOps,都指向「让开发者更高效」。

这一背景下,基于关系表的关系型的数据模式也被灵活的非关系型数据模式替代。

文档模型更自然

文档模型与面向对象的数据表达方式更相似更自然。与关系型数据库中表结构不同,文档中可以嵌入数组和子文档,就像程序中的数组和成员变量一样。这是关系型数据库三范式不允许的。但实际中我们经常看到一对多和一对一的数据。比如,一篇博客文章的 Tag 列表作为文章的一部分非常直观,而把 Tag 与文章的从属关系单独放一张表里就不那么自然。再比如,一个订单下面的收货地址,包括省、市、区、街道和门牌,作为一个子文档,与订单的信用卡地址很容易区分开。更方便的是,嵌入的数组和子文档之上可以直接建立索引,比如我可以很快找到所有 Tag 包含 MongoDB 的文章。

其实,我挺喜欢关系型数据库那些理论的,SQL 语言可以很精确地形式化,赏心悦目。然而,三范式强调的「数据没有任何冗余」并不是今天程序员们最关心的问题。他们用着方便不方便才是更重要的问题。

性能

三范式所带来的性能损失也是一个问题。为了满足一个查询,多个表的数据都要参与 Join,每一个表都对应着磁盘的一次读取。这和数据放在一个地方,一次读完,当然完全不同。更简单的数据访问模式也让开发者更容易理解数据库的性能表现,尤其是当涉及到索引时。例如,关系型数据库中一个查询太慢了,它 Join 了 6 张表,到底我应该给哪个表的哪一列加什么样的索引呢?

灵活

关系型数据库最常见的运维问题是给已有数据加一个新的可选的属性,从数据表到应用数据层都要改。尽管有些工具可以把它自动化,这仍然是一个复杂的工作,尤其是更新产品线上数据库的时候。MongoDB 没有 Schema(模式、数据模型),就不需要改动数据库,只需要在应用层做必要的改动。

自定义属性也是一个问题,电子商务网站的产品除了价格,ID等,每类产品有许多自定义属性,比如 LCD 和 LED 显示器的产品特性就不一样。数据这种特性被称为「多态」(polymorphism)。关系型数据库里,产品这个表应该怎么设计?最简单的方案,是把每个可能的属性都变成单独一列,当然这个方案不能扩展。更极端一点,一个通讯录允许用户随意添加新的联系方式,你有 Facebook,他有 Twitter。一种方案是在程序里把所有自定义属性序列化,比如用 JSON 放到一列里。另一种方案,叫做 Entity-attribute-value Model,把自定义属性和他们的数据类型,进一步抽象,用关系表来表现。在我看来,这两种方案都是把数据库应该做的工作丢给程序员。这时,相比这些复杂方案,MongoDB 没有 Schema 的优点就显现出来了。

MongoDB 的灵活还体现在非结构化和半结构化的数据上。MongoDB 提供全文索引,也支持地理位置查询和索引。例如一位用户想知道方圆五公里哪里有公共卫生间,这是「地理范围查询」。然后他搜索最近的单车。摩拜单车正是使用 MongoDB 完成这样的「距离排序查询」。强烈建议使用地理位置索引加速查询,因为我能理解这位用户的心情。

可扩展性

数据一台机器放不下了,就需要 sharding(分片)把它放到好几台机器上去。分片是 MongoDB 多年以来的原生功能,与 MongoDB 其他功能高效整合。例如,分片集群中一个复杂的聚合查询会自动地根据 Shard Key(片键)分配到多个结点上运行,尽可能将计算任务下推到数据结点上,最后在一个结点上聚合所有结点的结果。分片还可以在各个结点间自动迁移数据,均衡其数据量。为了升级数据库容量,相较于关系型数据库分库分表需要同时改动应用和数据库,分片的运维也简单很多,能够做到对应用透明。

我们希望即使机器硬件有问题,数据也不要丢,服务不受影响。分片后机器更多,任一机器出问题的概率也就更高。Replication 复制解决了这个问题。它把同一份数据实时地复制到多个结点,自动地在其中切换。MongoDB 的商业客户中,不论规模大小,使用复制集已经成了一个标准,因为数据丢失的代价远大于冗余机器的成本。

事务

事务是2018年夏天要推出的 MongoDB 4.0 版本最大的新功能。事务是一直以来用户要求最强烈的功能之一,尽管通过 Schema 的设计,可以把关系型数据库里多个表的多条记录,放在一个文档里,能解决一部分对事务的需求。4.0 中,用户可以在一个复制集内,交互式地在多个集合里插入修改多个文档,整个事务满足 ACID,满足 Snapshot Isolation(快照隔离)一致性要求。过去半年,这是我们组的主要工作。明年 MongoDB 4.2 计划加入对分片集群事务的支持。

有时候相关数据放在一个文档里不方便,JOIN 不可避免。MongoDB 提供了 $lookup 聚合查询,可以通过外键查询另一个集合。在 4.0 事务提供的快照隔离下,数据一致性也有了保障。

十年积累

从 MongoDB 开发者的角度看,MongoDB 站在几十年数据库理论巨人的肩膀上,也积累了自己的理论创新。例如复制功能中 Raft 协议每个细节的改动都经过仔细考量,确保不会影响正确性。可是更重要也更困难的是工程问题。几年前「距离排序查询」的性能问题反馈比较多。有用户建议地理位置索引使用 R-Tree 代替 GeoHash。然而我的两个实习生工程上的优化就可以把性能提升十倍。如同 cPython 一遇到性能问题首先怪罪全局锁一样,用一个数据结构或者简单模型就能一蹴而就地解决问题,当然非常漂亮,但是现实往往并非如此。

庞大系统的工程问题的解决最终是日拱一卒带来的,就像 Oracle 的质量是一个个 Hotfix 积累的结果。十年过去,MongoDB 早已成熟。这不仅是 Bug 不断被修复,系统的设计、测试和流程也都更加完善。5 年前引入 Raft 时,复制功能全部都有单元测试,JavaScript 集成测试数量也翻了一番。专门开发集成测试环境今天可以在一个小时跑完 Linux 上总计 27 个小时的全部测试,每天跑完近 80 个操作系统 X 硬件架构 X 存储引擎下总计 1100 个小时的测试。

想起 3 年前有人发文章批评 7 年前的版本,怎么说呢……哎,不说了,要被人批评不谦虚了。数据库升级了解一下,生命周期过了的版本我们都不更新了。

什么时候不用 MongoDB

十年间,从最初几年证明文档数据模型的价值,到分布式系统架构日臻成熟,再到 4.0 支持事务,今天的 MongoDB 可以胜任关系型数据库的所有应用场景。MongoDB 也不再是一个创业公司,今天已经全球超过 1000 名员工,包括中国分公司。

那什么时候用 MongoDB 不合适呢?

一次,我在一个 pet project 里需要地铁列车时刻表,官方提供的是一个满足行业标准的 SQL 格式的数据。站点、路线、班次、调度安排按照三范式分成了好几张表,但是数据量很小。我花了一晚上把数据导入到了 MongoDB 里,按照访问模式设计了文档结构,最终运行得很好。可如果再来一次,我可能会选择使用关系型数据库。因为如果源数据格式是 SQL 数据,没法控制;数据量小;数据间交叉引用关系复杂;查询模式丰富;应用又不需要高性能;不担心机器故障等高可用性问题,那么继续沿用关系型数据库也是一个务实的选择。我的 pet project 最终只有我一个用户,然后尘封在 BitBucket 的代码库里。早知如此,用关系型数据库里也没什么不可以 ¯\_(ツ)_/¯

这个 pet project 教给我的,也是我最后想说的:一个项目的好坏很少取决于你用哪个工具。不管用 Vim 还是 Emacs 都能成为很厉害的程序员,但是他们不是因为 Vim 或者 Emacs 才这么厉害的。他们理解工具能带来什么,并用其创造价值。希望 MongoDB 能成为你工具箱里一个称手的工具,当下次需要的时候,多快好省地解决问题,让你更多地关心应用本身。现在就开始学习 MongoDB 吧?

特别感谢

提出多处修改意见。

References

 收藏 (0) 打赏

您可以选择一种方式赞助本站

支付宝赞助

微信钱包赞助

版权所有丨本站资源仅限于学习研究,严禁从事商业或者非法活动!:ABC资源站 » MongoDB 等 NoSQL 与关系型数据库相比,有什么优缺点及适用场景?

评论 抢沙发

评论前必须登录!

立即登录   注册

切换注册

登录

忘记密码 ?

切换登录

注册