写点什么

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

2018 年 9 月 29 日

本文要点

  • 即使 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 年 9 月 29 日 18:306667
用户头像

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

关注

评论

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

几个快速提升工作效率的小工具(Listary等)分享一下(强烈建议收藏)

码农飞哥

Python基础语法和数据类型最全总结

华为云开发者社区

Python

2021最全网页视频下载攻略 【建议收藏】

科技猫

软件 工具 网站 分享 视频处理

Dubbo 四大角色

青年IT男

dubbo

Dubbo快速入门

青年IT男

dubbo

Dubbo 集群容错

青年IT男

dubbo

[TcaplusDB小知识]TcaplusDB的备份与回档机制

数据人er

数据库 nosql TcaplusDB

源中瑞智慧党建系统,打造党建管理系模式

13530558032

LeetCode题解:73. 矩阵置零,栈,JavaScript,详细注释

Lee Chen

算法 LeetCode 前端进阶训练营

B站HR炫耀资产、贬低面试者?无独有偶,校招污点事件记录帮你避雷

北游学Java

Java 面试

Dubbo 服务配置

青年IT男

dubbo

Dubbo 服务启动检查

青年IT男

dubbo

用C++ 和OpenCV 实现视频目标检测(YOLOv4模型)

博文视点Broadview

区块链食品溯源--为食品溯源认证

13530558032

身边的最优化问题

青木老师

人工智能 算法

RocketMQ NameServer工作原理与源码解析

HByte

RocketMQ

Leveldb解读之四:Compaction

Jowin

leveldb

Dubbo 入门简介

青年IT男

dubbo

情指行一体化平台建设解决方案,智慧公安警务系统开发

WX13823153201

区块链电子签章应用平台--助推企业数字化转型升级

13530558032

Dubbo 直连提供者

青年IT男

dubbo

ISO/IEC 5055:软件代码质量的标尺

华为云开发者社区

软件 代码质量 源代码 ISO/IEC 软件质量模型

Dubbo 服务注册与发现

青年IT男

dubbo

如何推进一个“以行动为导向的”事件驱动架构?

VoltDB

数据库 数据分析 实时数据分析 事件驱动架构

Coinbase上市意味着什么?

猫Buboo

马斯克的狗狗币实验

猫Buboo

面对大规模 K8s 集群,如何先于用户发现问题?

阿里巴巴云原生

容器 云原生 k8s 监控 调度

使用 rocketmq-spring-boot-starter 来配置、发送和消费 RocketMQ 消息

阿里巴巴云原生

容器 开发者 云原生 中间件 API

40个问题让你快速掌握Java多线程的精髓

华为云开发者社区

Java 多线程 synchronized Thread Semaphore

马丁量化策略交易系统开发,做市机器人,市值管理

WX13823153201

What's new in dubbo-go v1.5.6

阿里巴巴云原生

go 开发者 云原生 dubbo 中间件

Leader修炼指“北”:管理路上的大小Boss

Leader修炼指“北”:管理路上的大小Boss

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