高品质的音视频能力是怎样的? | Qcon 全球软件开发大会·上海站邀请函 了解详情
写点什么

开始使用 MongoDB 之前应该知道的 14 件事

  • 2018-09-29
  • 本文字数:3812 字

    阅读完需:约 13 分钟

本文要点

  • 即使 MongoDB 没有强制要求,设计一个模式还是至关重要。
  • 类似地,在设计模式及访问模式时设计好索引。
  • 避免大对象,尤其是大数组。
  • 谨慎对待 MongoDB 的设置,尤其是关乎安全和稳定性时。
  • MongoDB 没有查询优化器,因此,对于如何安排查询操作的顺序,你必须格外小心。

我从事数据库相关工作已经很长时间了,但是最近才开始使用 MongoDB。在开始使用 MongoDB 之前,我希望有些事情我已经知道。根据一般经验,对于数据库是什么以及它们能干什么,人们会有先入为主的认识。为了给他人提供方便,本文列出了一些常见的错误。

创建一个无需身份验证的 MongoDB 服务器

很遗憾,MongoDB 在安装时默认不启用身份验证。在只从本地访问的工作站上,这没什么不好。但是,由于 MongoDB 是一个多租户系统,它会尽可能地占用内存,因此最好是安装在服务器上,最大限度地提供内存,即使是开发工作。在服务器上使用默认端口安装而不启用身份验证是在自找麻烦,尤其是可以在查询中运行任意 JavaScript 时(例如把 $where作为注入攻击的载体)。

身份验证方法有多种,但是用户 ID/ 密码凭证最容易安装和管理。当你考虑基于 LDAP 的身份验证时,可以采用那个方法。在我们谈论安全时,MongoDB 必须保持最新,而且,在日志里查找未授权访问的迹象总是值得的。我不喜欢使用默认端口。

忘记限制 MongoDB 的攻击面

MongoDB 的安全检查清单为降低网络渗透和数据泄露风险提供了很好的建议。我们很容易会认为,开发服务器不需要高等级的安全。不是这样的:安全对于所有MongoDB 服务器都很重要。尤其是,除非有非常好的理由要使用 mapReduce group $where ,否则你应该在配置文件中设置 javascriptEnabled:false ,禁用 JavaScript。因为标准 MongoDB 的数据文件是不加密的,另外,使用专门的用户运行 MongoDB 也是一个明智的做法,对数据文件的完全访问仅限于那个用户,这样就可以使用操作系统自带的文件访问控制了。

没有设计一个模式

对于模式,MongoDB 没有强制要求。这不是说它不需要模式。如果你真想保存文档而又没有一致的模式,那么你可以非常快速、简单地保存它们,但是检索会十分麻烦

MongoDB 模式设计的六大经验原则”是一篇值得一读的经典文章,而第三方工具(如 Studio 3T)提供的类似“模式浏览器( Schema Explorer )”这样可以执行定期模式检查的特性也是值得拥有的。

忘记排序规则(排序顺序)

这比其他任何的配置错误都会导致更多的挫折和时间浪费。MongoDB 默认使用二进制排序规则。这对任何地方的文化都是不利的。在 80 年代,大小写敏感、重音敏感、二进制排序规则,和念珠、土耳其长衫和卷胡子一起,被视为奇怪的时代错误。现在,他们没法辩解了。在现实生活中,motorbike 和 Motorbike 就是一样,而 Britain 和 britain 就是同一个地方。小写字母和大写字母只是书写上的等价。就不要让我再说重音字符排序规则了。当你创建一个 MongoDB 数据库时,使用一种合乎系统用户语言和文化重音敏感、大小写敏感排序规则。这使得字符串数据的检索容易许多。

创建大文档集合

MongoDB 乐于把最大 16MB 的文档置于集合中,而 GridFS 设计用于超过 16MB 的大文档。但是,可以容纳大文档并不意味着那是一个好主意。MongoDB 在单个文档的大小为几 KB 时表现最好,处理它们的方式更像宽 SQL 表的行。大文档会导致多种性能问题

使用大数组创建文档

文档可以包含数组。最好是把数组元素的数量保持在四位数以下。如果数组频繁添加,会使得包含它的文档过大,那样,它在磁盘上的位置就需要移动,反过来,这意味着每个索引都必须更新。当一个包含大数组的文档重新索引时,由于每个数组元素都有一个单独的索引条目,所以会发生大量的索引重写。此外,这种重新索引在这类文档插入或删除时也会发生。

为了最小化这个问题,MongoDB 有一个“填充因子( padding factor )”,为文档增长提供空间。

你也许会想,你可以通过不建立数组索引来绕开这个问题。遗憾的是,没有索引,你会遇到其他问题。因为文档会从头到尾扫描,找到一个接近数组尾部的元素需要花更多的时间,大部分处理这个文档的操作都会变慢

忘记聚合情况下的阶段排序

在有查询优化器的数据库系统中,你编写的查询是说明你想要什么而不是如何获取它。这就像在餐馆中点餐;你通常只需要点菜,而不必对厨师发出详细的指令。

在 MongoDB 中,你是对厨师发指令。例如,你需要通过 $match 和 $project 确保管道中的数据尽早减少,排序只在数据减少时发生一次,查找按照你希望的顺序执行。查询优化器省去了不必要的工作,优化阶段顺序,选择连接类型,这会把你宠坏。MongoDB 给了你更多的控制,但这种便利是有成本的。

Studio 3T 这样的工具使构建准确的 MongoDB 聚合查询变得更容易。它的聚合编辑器特性使你可以一次对一个阶段应用管道操作符,你可以在每个阶段验证输入和输出,更便于调试。

使用快速写

永远不要把 MongoDB 设为低稳定性的高速写。看上去,“file-and-forget”模式使得写入速度变快了,因为命令在实际写入任何东西前就返回了。如果系统在数据写入磁盘之前崩溃了,就会丢失,存在出现不一致状态的风险。所幸,64 位的 MongoDB 启用了“日志(Journaling)”。

MMAPv1 和 WiredTiger 存储引擎都使用日志预防上述情况,不过,在日志关闭的情况下,WiredTiger 也可以在还原过程中恢复到最后一致的检查点

日志可以确保数据库在恢复时处于一致状态,它会保存日志写入时的所有数据。日志写入的时间间隔可以使用运行时选项 commitIntervalMs 来配置。

为了确保写入,就要确保在配置文件中启用日志(storage.journal.enabled),而且提交间隔要和你能够承担的数据丢失相对应。

无索引排序

在搜索和聚合中,你经常希望排序数据。但愿那是在最后阶段完成的,在结果过滤之后,从而减少需要排序的数据量。即使在那个时候,你需要一个可以覆盖排序的索引。单键索引或混合索引都可以。

当没有合适的索引可用时,MongoDB 就不得不在没有索引的情况下排序。对于排序操作中所有文档的总大小,有 32MB 的内存限制,如果 MongoDB 达到了这个限值,它就会产生错误,或者有时候仅仅返回一个空的记录集

Lookup 而没有索引支持

Lookup 的功能和 SQL 联合查询类似。为了获得良好的性能,作为外键的键值上需要有索引。这并不明显,因为其使用并没有在 explain() 中报告。这些索引并不包含在 explain() 记录的索引里,那些索引是供管道操作符 $match、$sort 出现在管道开始时使用的。现在,索引可以覆盖聚合管道的任何阶段

不使用多条更新

db.collection.update() 方法用于修改一个已存在文档的一部分或全部,或者是整个替换一个已存在的文档,这取决于你提供的更新参数。除非你设置 multi 参数,更新匹配查询条件的所有文档,否则它不会更新集合里的所有文档。这一点不是那么明显。

忘记哈希对象中键序的意义

在 JSON 中,一个对象包含一个无序集合,而该集合中有零个或多个名 / 值对,其中名是一个字符串,而值是一个字符串、数值、布尔值、空、对象或数组。

遗憾的是,BSON 在做搜索时给顺序赋予了意义。在 MongoDB 中,嵌入对象中键的顺序很重要,也就是说,{ firstname: "Phil", surname: "factor" }和{ surname: "factor", firstname: "Phil" }就不匹配。这意味着,你必须保留文档中名 / 值对的顺序,如果你想确保可以找到它们的话。

混淆“null”和“undefined”

根据正式的 JSON 标准(ECMA-404 第 5 节),“undefined”值在 JSON 中从来就是不合法的,虽然它事实上已经在 JavaScript 中使用。而且,它在 BSON 中是“deprecated”,会转换成 $null,这并不是一个总令人满意的解决方案。在 MongoDB 中,要避免使用“undefined”

使用 $limit() 而未用 $sort()

通常,当你在 MongoDB 中开发时,仅仅查看查询或聚合返回的结果的样例会很有用。 $limit() 就是为了满足这个要求,但是,它永远不应该出现在最终版本的代码中,除非你首先使用了 $sort。这是因为,不这样的话,你就无法保证结果的顺序,你就无法可靠地“按页浏览”数据。为了确保可靠性,查询或聚合必须是“确定的”,就是说,它们每次执行都会给出相同的结果。包含 $limit 而不包含 $sort 的代码不是确定的,后续会导致难以跟踪的 Bug。

小结

对于 MongoDB,让你最终感到失望的唯一方式是把它直接和另一种类型的数据库如 RDBMS 比较,或者对它有特别的期待。这就像把桔子和叉子比较。数据库系统有它们的用途。最好是理解并领会这些差别。强迫 MongoDB 开发人员按照 RDBMS 的方式做事就太遗憾了,我希望继续看到解决旧问题的有趣的新方法,如确保数据完整性、使数据系统具有从故障和恶意破坏中恢复的能力。

在 4.0 版本中,MongoDB 引入了 ACID 事务处理,这是以创新方式引入重大改善的一个很好的例子。多文档、多语句事务现在是原子的了,它允许开发人员调整用于获取锁的时间,过期挂起事务以及修改隔离级别。

关于作者

Phil Factor (为保护作者隐去真名),又称数据库摩尔,他有将近四十年的数据库密集型应用程序经验。在 20 世纪 80 年代初的一次展览会上,愤怒的比尔盖茨曾对他大吼大叫,自此,在整个职业生涯中,他就坚决匿名。

查看英文原文: 14 Things I Wish I’d Known When Starting with MongoDB

2018-09-29 18:307171
用户头像

发布了 1008 篇内容, 共 346.9 次阅读, 收获喜欢 330 次。

关注

评论

发布
暂无评论
发现更多内容

微信朋友圈高性能架构分析

面向对象的猫

区块链的宿命,数字经济的局

CECBC

架构实战营 - 模块二

Testcase

架构实战营

架构实战营第二课作业——微信朋友圈的高性能复杂度分析

tt

架构实战营

模块二作业

俊杰

Python OpenCV 图像的膨胀与腐蚀,图像处理取经之旅第 38 篇

梦想橡皮擦

7月日更

微信朋友圈架构设计

summer

极客时间 极客时间架构师一期

健康讲座:如何提升人体免疫能力

石云升

学习 健康 7月日更

设计消息队列存储消息数据的MySQL表格

俞嘉彬

架构实战营

MapReduce案例(一)-- 流量统计

钱江兵

【数据结构】Java 同步工具 AQS

Alex🐒

Java 源码 数据结构

大数据训练营-第一次作业

西伯利亚鼯鼠

架构实战营 - 模块 2 - 微信朋友圈高性能复杂度分析

雪中亮

架构实战营 #架构实战营

多维数据分析(OLAP)技术选型(2):数据分析与OLAP差异

水滴

数据分析 OLAP 技术选型

架构训练营模块二作业

BlingBling

架构实战营

【硬刚Kylin】Kylin入门/原理/调优/OLAP解决方案和行业典型应用

王知无

Presto原理&调优&面试&实战全面升级版

王知无

编程的本质是什么?

白色蜗牛

Java 编程 程序员 软件 计算机

央视曝光APP弹窗广告三大陷阱:如何监管应用软件弹窗广告

石头IT视角

架构训练营模块二作业

老实人Honey

「架构师训练营第 1 期」

进阶指南!深入理解Java注解

Jackpop

Java

知乎热文 | 如何高效学习Spring Boot?

Jackpop

Java Spring Boot

架构实战营模块二作业

Morphling

#架构实战营

2.4如何提高架构设计的质量

Lemon

清晰了!一文彻底理解Java事件处理

Jackpop

Java

架构实战营 - 模块二(作业)

Cingk

架构训练营第 1 期 模块二作业

高远

MVP on Board 没用小技巧 👌

newbe36524

.net MVP ASP.NET Core

《面试补习》--来聊聊削峰填谷!

九灵

Java 分布式 消息队列 异步削峰

架构训练营 1 期 - 模块二作业

蔸蔸

微信朋友圈高性能复杂度分析

gawaine

架构实战营

开始使用MongoDB之前应该知道的14件事_语言 & 开发_Phil Factor_InfoQ精选文章