33

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

 5 years ago
source link: http://www.infoq.com/cn/articles/Starting-With-MongoDB?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本文要点

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

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

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

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

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

忘记限制MongoDB的攻击面

MongoDB的 安全检查清单 为降低网络渗透和数据泄露风险提供了很好的建议。我们很容易会认为,开发服务器不需要高等级的安全。不是这样的:安全对于所有MongoDB服务器都很重要。尤其是,除非有非常好的理由要使用 mapReducegroup$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事务处理,这是以创新方式引入重大改善的一个很好的例子。多文档、多语句事务现在是原子的了,它允许开发人员调整用于获取锁的时间,过期挂起事务以及修改隔离级别。

关于作者

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK