性能最佳实践:MongoDB数据建模和内存大小调整
我们将讨论在大规模数据下实现高性能,需要在许多重要维度上进行考虑的关键因素,其中包括:
- 数据建模和内存大小调整(工作集)
- 查询模式和分析
- 索引
- 分片
- 事务和读/写关注
- 硬件和操作系统配置
- 基准测试
谁适合阅读这个系列?
我们在此介绍的最佳实践并非巨细无遗,但本系列中的一些建议还是非常有用的,无论你是:
- 刚开始第一个项目的新手,还是一个经验丰富的MongoDB开发者;
- 在完全托管的全球云数据库服务Atlas上运行MongoDB,还是自己管理MongoDB。
这篇文章包含哪些内容?
我们从两个关键的考虑因素开始,它们是本系列其余部分中所讨论的性能最佳实践的基础。首先,我们将介绍模式设计和一些重要的资料,之后会讨论如何为应用程序最常访问的数据和索引来调整内存大小,也就是我们所说的“工作集”。
重要的数据建模
性能优化的第一步是了解应用程序的查询模式,以便于设计数据模型并选择合适的索引。根据应用程序的查询模式调整数据模型会让查询更加高效,提高插入及更新操作的吞吐量,并更有效地将工作负载分散到分片集群中。
MongoDB具有灵活的模式,但这并不意味着你可以忽略模式设计!尽管你可以随时对模式进行修改,但在项目开始时应用模式设计最佳实践可以避免以后潜在的重构工作。
JSON文档的一个主要优点是可以根据应用程序的需要灵活地对数据进行建模。由于文档能够嵌套数组和子文档,这使得它在对数据间的复杂关系进行建模时非常强大。同样也可以对平面、表格和列式结构、简单的键值对、文本、地理空间和时间序列数据,或是连接图形数据结构的节点和边进行建模。应用程序的查询模式决定了什么是最佳的模式设计。
数据建模的关键考虑因素及资料
在设计数据模型时,首先需要做的决定之一是如何对数据间的关系进行建模。决定何时应该使用内嵌文档,何时应该在不同集合中的文档之间建立引用,是特定于应用程序的。然而,在做模式设计时,有一些一般性的考虑可以来指导决策。
内嵌
可以很自然地想到,具有一对一关系的数据可以嵌入到单个文档中。具有一对多关系的数据,如果其中“多”的一方总是与其父文档一起出现,或是会在其父文档的上下文中被查看,也最好通过内嵌来实现。因为这些数据总是被一起访问的,所以将它们存储在同一个文档中是最佳策略。
由于这种数据的局部性,内嵌方式通常为读操作提供了更好的性能,因为它能够在一个数据库内部操作中请求和检索相关数据,而不是对存储在不同集合中的文档进行查找。内嵌数据模型还可以在单个原子写入操作中更新相关数据,因为单个文档的写入是事务性的。
然而,并非所有的一对一和一对多关系都适合嵌入到单个文档中。在下列情况下,应该在不同集合中的文档间使用引用:
- 文档经常被读取,但其中包含了一些很少被访问的数据。嵌入这样的数据只会增加集合的内存需求(工作集)。
- 文档的一部分经常被更新,并且不断增大大小,而文档的其余部分则相对静态。
- 组合到一起的文档大小将超过MongoDB的16MB限制,例如在对像产品评论这样的多对一关系进行建模时。
引用
引用可以帮助解决上面提到的问题,并且通常在多对多关系建模时使用。但是,应用程序需要进行后续的查询来解析引用。这需要额外的服务器往返请求,或者需要使用MongoDB聚合管道中的$lookup操作符来执行“连接”操作。
深入研究
数据建模是一个扩展性很强的话题,之前有很多文章对其进行了讨论。为了帮助你做出正确的决策,以下是一些值得查阅的关键资源摘要:
- MongoDB文档提供了有关数据建模的详细内容,从文档数据模型的一些高层次概念开始,然后到实际的应用示例和设计模式,包括有关引用和内嵌的更多详细信息。
- 你还应该回顾一下我们的使用模式构建博客系列(译注:中文版地址 - https://mongoing.com/archives/26532)进一步了解不同用例的特定模式设计最佳实践,包括目录和内容管理、物联网、移动应用程序、分析应用,以及客户360度单一视图。此系列文章使用特定的设计模式(如版本控制模式、分桶模式、引用模式和图模式)覆盖了这些用例。
- MongoDB大学提供了免费的基于网页的数据建模培训课程。这对于学习文档数据模型设计来说是一个不错的起点。
检查你的数据模型
当开发了一个初始的数据模型并开始用一些应用程序的示例数据对其进行填充,就可以对其进行检查了。
MongoDB Compass是一个免费的MongoDB图形用户界面。你可以使用Compass做很多事情,它是我们在这个博客系列中会经常使用到的工具。它最有用的特性之一是模式可视化,能够以直方图的方式显示文档字段、数据类型和值。正如你将在本系列后面看到的,还可以直接从Compass的用户界面对查询计划和索引覆盖情况进行可视化浏览。
图1:在MongoDB Compass中对模式进行可视化展示
在图1中,我们检查存储在restaurants
集合中文档的模式。对于采样出的文档,Compass会显示字段在每个文档中出现的频率、它们包含的值范围和数据类型,以及categories数组中的元素个数。Compass文档中有更多关于如何分析模式的详细信息。
可以将Compass连接到自己管理的MongoDB实例或MongoDB Atlas上的云数据库。还可以使用数据浏览或“集合”视图直接从Atlas的用户界面查看文档结构。
文档入门
探索和试验数据建模的最佳方法是在完全托管的Atlas云服务上启动MongoDB。
我们的文档将指导你如何在所选地区和云提供商中创建免费的MongoDB数据库集群。你还可以加载我们的样例数据集,这种方式可以使你很容易地熟悉文档模型。
调整内存大小:确保工作集适配于RAM
除了数据建模,性能优化的第二个主要考虑因素就是工作集大小的调整。
与大多数数据库一样,当应用程序的工作集(索引和最常访问的数据)可适配进内存中时,MongoDB的性能最好。RAM大小是实例大小调整的最重要因素;如果RAM不足,其他优化可能无法显著提高数据库的性能。如果性价比比单纯的性能更重要,那么使用快速的固态硬盘来对RAM做一些适当的补偿是一个可行的设计选择。你应该通过测试来寻找工作负载和SLA的最佳平衡。
当应用程序的工作集适配进RAM时,从磁盘中进行读取的频率会很低。你可以使用我们本系列的下一篇关于查询分析的文章中介绍的工具对此进行分析。
如果工作集超过了所选实例大小或服务器的RAM,请考虑迁移到具有更多内存的实例,或者对数据库进行跨多个服务器的分区(分片)。
无论是在Atlas上运行MongoDB还是自己管理MongoDB,将工作集调整到合适的大小都是没错的。
- 查阅有关Atlas大小及分档选择的文档 ,以获取如何计算工作集大小的指南。
- 在本系列的后续文章中,我们会深入研究如何调整自管理MongoDB的硬件规模。
在MongoDB Atlas中,对计算和存储的规模缩放非常简单。你可以勾选群集分档自动缩放,它将根据应用程序需求的变化来调整计算容量。
Atlas中的集群分档自动缩放在定义的时间段内监视CPU和内存利用率,并在配置的限制范围内扩展或收缩实例大小。由于所有缩放事件都以滚动的方式执行,因此对应用程序没有影响。在编写本文时,自动缩放是一个beta版特性。如果想自己控制缩放事件,那么只需在Atlas用户界面上单击几下,或者通过API进行调用就可以了。
接下来的内容
这是性能最佳实践系列的第一篇文章。下一篇会介绍查询模式和分析。
原文:
Performance Best Practices: MongoDB Data Modeling and Memory Sizing
译者:牟天垒 MongoDB中文社区翻译委员
- 全世界最短IE判定if(!+[1,])的解释
- Log4Net与Log2Console配合时中文问题的解决
- 如何改变AspNetPager当前页码的默认红色?
- Flash/Flex学习笔记(48):反向运动学(下)
- 如何给sublime text3安装汉化包?so easy 哦
- Flash/Flex学习笔记(47):反向运动学(上)
- 汉诺塔问题算法介绍
- Flash/Flex学习笔记(36):自己动手实现一个滑块控件(JimmySilder)
- 使用Zabbix服务端本地邮箱账号发送报警邮件及指定报警邮件操作记录
- fckeditor上传问题的解决
- Flash/Flex学习笔记(46):正向运动学
- 异步Socket处理的一些测试值
- .Net中DES加密的细节问题
- 分布式监控系统Zabbix--完整安装记录 -添加web页面监控
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 关于tf.matmul() 和tf.multiply() 的区别说明
- python中执行smtplib失败的处理方法
- PHP+Ajax简单get验证操作示例
- Python matplotlib读取excel数据并用for循环画多个子图subplot操作
- python转化excel数字日期为标准日期操作
- thinkPHP框架通过Redis实现增删改查操作的方法详解
- PHP中引用类型和值类型功能与用法示例
- PHP文件上传小程序 适合初学者学习!
- php的扩展写法总结
- 实例介绍PHP删除数组中的重复元素
- Python迭代器协议及for循环工作机制详解
- PHP CURL中传递cookie的方法步骤
- PHP 结合 Boostrap 结合 js 实现学生列表删除编辑及搜索功能
- Yii2处理密码加密及验证的方法
- PHP实现获取ip地址的5种方法,以及插入用户登录日志操作示例