概述
为了规范MongoDB的部署和运维工作、指导开发人员正确地使用MongoDB,IT运维团队结合Redis的技术特点以及公司实际现状,制定了本规范。
目前百胜MongoDB正在逐渐向公有-自建混合云部署模式过渡,本文档中的技术标准基于百胜自建MongoDB规范和华为云MongoDB服务(产品名称:云数据库MongoDB)。本文档应随百胜的MongoDB管理策略不断进化,扩展和更新内容。
本文档的面向对象为:公司所有与MongoDB相关的IT架构设计、开发、运维和 DBA 人员。
一、mongodb介绍
-
Mongodb简介
MongoDB 中的记录是一个文档,它是由字段和值对组成的数据结构。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档、数组和文档数组。
使用文档的优点是:
- 文档对应于许多编程语言中的原生数据类型。
- 嵌入式文档和数组可以减少成本高昂的连接操作。
- 动态模式支持流畅的多态性。
-
MongoDB 的主要特性
- 文档导向的存储:
- 数据以 BSON(Binary JSON)格式存储,这是一种类 JSON 的格式,但包含更丰富的数据类型。每个文档可以有不同的结构,字段可以包含数组或嵌套的文档。
- 无模式:
- MongoDB 是无模式的,这意味着同一个集合(类似于关系数据库中的表)中的文档可以有不同的结构。这种灵活性使得 MongoDB 非常适合于存储异构数据和快速迭代开发。
- 查询能力:
- MongoDB 提供丰富的查询语言和索引支持,允许执行各种复杂查询,包括文档字段的查询、范围查询、正则表达式查询等。
- 索引:
- 支持多种类型的索引,包括单字段索引、复合索引、地理空间索引等,以优化查询性能。
- 复制和高可用性:
- MongoDB 的复制功能(Replica Set)提供数据的高可用性。一个副本集包括多个 MongoDB 服务器,其中一个作为主节点,其他作为从节点,从节点可以在主节点出现故障时自动接管服务。
- 分片:
- 支持自动分片(Sharding),以支持数据的水平扩展。通过分片,MongoDB 可以分布数据到多个机器上,帮助处理大规模的数据集和高吞吐量的应用。
- 聚合框架:
- 提供强大的聚合工具,可以执行复杂的数据处理和分析,类似于 SQL 语言中的 GROUP BY 和 JOIN 操作。
- 安全性:
- 支持多层次的安全措施,包括访问控制、SSL/TLS 加密通信和审计功能。
- 存储引擎:
- 支持多种存储引擎,如 WiredTiger(默认引擎,提供高性能和数据压缩功能)和 In-Memory(内存存储引擎)。
-
适用场景
Mongodb作为一种内存型的高性能key-value NoSQL存储,其技术设计基于如下出发点:
- 文档很灵活。可以随时修改您的模型,持续集 成新的应用功能,而无需担心复杂的模型迁移。
- 文档具有多种形态。 集合(相当于关系数据库 中的表)中的文档可以与同一集合中的其他文档 具有不同的结构。
- 文档可扩展。可以按照应用所需的任何方式对 数据进行建模,包括丰富的分层文档、平面的表 格式结构、简单键值对、文本、地理空间数据和 图形处理中采用的节点和边缘等。
- 通过多文档 ACID 事务,您可以维持与传统数 据 库 中 相 同 的 数 据 完 整 性, 关 键 区 别 在 于, MongoDB 甚至可以让您通过散布全球的高分布 式集群维持事务保证。
- MongoDB 的表达式查询语言、二级索引和聚合 管道让您几乎能以应用程序所需的任何方式查 询数据,包括简单查找和范围查询、为数据分析 和转型创建优异的处理管道、JOINs、地理空间 处理、按需物化视图和图形遍历。您可以在支持 OLTP 和实时分析以及离线数据湖的数据库中, 利用相同的 MongoDB 查询语言和各种驱动器来 处理数据。
- MongoDB 具有很强的设计一致性,允许您读取 自身的写入数据,撇弃最终一致性系统造成的应 用复杂情况。MongoDB 的一致性保证是完全可 调的,让您实现数据新鲜度与性能之间的平衡。
- MongoDB 将数据存储为 JSON(JavaScript 对象符 号)的二进制形式 BSON(二进制 JSON)。与大 多数数据库将 JSON 数据存储为原始字串和数字 不同,BSON 编码扩大了 JSON 表达,加入 int、 long、日期、浮点和十进制 128 等其他种类。这使 应用程序更轻松可靠地处理、排序和比较数据。
因此,MongoDB在百胜业务模式中的一些常见使用场景如下:
A. 订单信息查询
B. 菜单配置信息库
C. 各个应用的配置中心库
二、 技术规范
-
版本规范
目前百胜云为团队支持对如下mongodb版本的交付与维护工作:
- MongoDB 3.6.23
- MongoDB 4.4.17
- MongoDB 4.0.14
- MongoDB 4.0.28
- MongoDB 5.0.2 (支持力度一般)
-
百胜云mongodb功能
截止2021年11月,在已调研的两家公有云mongodb服务中,均支持所有常见的mongodb命令。与官方开源版本相比,兼容性达到99%以上。百胜自建mongodb基于官方开源版本,与开源的mongodb的命令覆盖率相同。
完整的参数和命令支持清单请见文档附录。
-
架构规范
百胜mongodb服务现阶段只提供副本集的架构。
百胜MongoDB 服务提供基于副本集的⾼可⽤容灾功能,当主节点发⽣故障时,可实现故障的快速主从切换。百胜运维团队⽀持交付的架构有1主2从,1主1从1仲裁,1主3从1仲裁,1主2从1仲裁,1主6从1仲裁副本集集群。生产环境下每台机器对应单实例方式部署。
- 副本集部署架构图:
- 分片集群部署架构图:
-
分片、副本规范
分片只是一个逻辑概念,其物理承载还是由副本承担的。
分片: MongoDB数据不同,互为分片;目的是实现数据的水平切割
副本: MongoDB数据相同,互为副本;目的是实现数据的备份,防止数据丢失
-
路径要求规范
- 应用路径:
/data/MongoDB/ - 配置路径:
/data/MongoDB/27017/conf/mongod.conf - 数据路径:
/data/MongoDB/27017/data - 日志路径:
/data/MongoDB/27017/log - 系统服务配置文件:
/usr/lib/systemd/system/MongoDB_27017.service
-
备份要求规范
集群数据量较小(例如小于 100GB),或者对备份和恢复速度要求不高的情况,使用mongodump 进行数据备份
- 备份时间
- 异地备份,默认放在S3上
- 归档,默认放在S3上
注意:默认部署的mongodb集群是不带备份服务,如有需求必须注明。
-
使用存储引擎规范
无特殊情况,存储引擎统一使用WiredTiger存储引擎。
- 存储引擎概述
存储引擎是MongoDB的核心组件,负责管理数据如何存储在硬盘和内存上。MongoDB支持的存储引擎有MMAPv1 ,WiredTiger和InMemory。InMemory存储引擎用于将数据只存储在内存中,只将少量的元数据(meta-data)和诊断日志(Diagnostic)存储到硬盘文件中,由于不需要Disk的IO操作,就能获取所需的数据,InMemory存储引擎大幅度降低了数据查询的延迟(Latency)。从MongoDB3.2开始默认的存储引擎是WiredTiger,3.2版本之前的默认存储引擎是MMAPv1,MongoDB4.x版本不再支持MMAPv1存储引擎。
- WiredTiger存储引擎优势
1.文档空间分配方式
WiredTiger使用的是BTree存储 MMAPV1 线性存储 需要Padding
2.并发级别
WiredTiger 文档级别锁 MMAPV1引擎使用表级锁
3. 数据压缩
snappy (默认) 和 zlib ,相比MMAPV1(无压缩) 空间节省数倍。
4. 内存 使用
WiredTiger 可以指定内存的使用大小,MMAPV1使用内存,并且是用完为止,很危险。
5.Cache使用
WT引擎使用了二阶缓存WiredTiger Cache, File System Cache来保证Disk上的数据的最终一致性。
而MMAPv1 只有journal 日志。
-
单站副本集部署
最少3节点,1个primary 节点+ 1个secondary节点+1个Arbiter节点
-
跨站副本集部署
5个节点,1个primary节点+ 3个secondary节点+1个云上Arbiter节点
三、资源规格
四、使用规范
-
数据库上启用授权和身份验证
部署 MongoDB 的新实例时,默认情况下该实例没有用户、密码或访问控制,容易发生数据泄漏。
To do
必须创建管理员,示例如下:
> use admin
db.createUser({ user: "zelmar", pwd: "password", roles : [ "root" ] })
配置文件中更改:
#开启认证,并使用keyFile 用于配置复制集或分片集群的成员之间的内部认证
security:
authorization: "enabled"
keyFile: /path/to/keyfile
2. ### oplog设置规范
副本集实例的主从复制是通过oplog(operations log)逻辑日志来完成的,oplog表(local.oplog.rs)是一个特殊的限制集合(capped collection),其中存储了所有对数据库中文档的改动操作。
- 设置合理的oplog大小或oplog保留时间
通常情况下,oplog大小保持默认值即可,但是在以下场景的业务负载下,建议调大oplog的大小:
- 经常对文档进行批量更新
每一个批量更新的操作都会生成多条针对单个文档的更新操作,这会产生大量的oplog记录。
- 反复地插入和删除
如果文档插入一段时间后被删除,那么数据库的磁盘空间并不会有明显增长,但oplog里会有很多相关记录。
- 针对相同文档的大量原地更新
如果业务场景中大部分操作是不会增加文档大小的更新,这些更新会产生大量的oplog记录,但磁盘上的数据量不会有明显变化。
- 如果业务负载是以下类型,也可以酌情调小oplog大小,以更充分地利用磁盘空间:
- 读多写少的业务负载。
- 存储冷数据。
无论是设置oplog大小还是oplog保留时间,都尽量将MongoDB实例的oplog窗口控制在24小时以上。在一些需要额外进行初始化同步(initial sync)的场景,oplog窗口需要覆盖一个节点完成所有数据同步的时间,该时间通常跟实例的整体数据量、库表总数、实例规格等因素相关,oplog窗口可能会需要更长的时间。
-
命名规范
To do
- 数据库命名只包含大小写英文字符,数字,加下划线 _
- 数据库名含多个单词考虑缩小并以下划线连接
- 集合命名只包含大小写英文字符,数字,加下划线 _
Not to do
- 禁止使用数字打头的库名,如:123_abc
-
开发规范
-
客户端连接数据库的时候,要计算业务一共有多少个客户端,每个客户端配置的 连接池大小是多少,总的连接数不要超过当前实例能承受的最大连接数的80%。
-
建议客户端的连接超时时间至少设置为最大业务执行时长的3倍。
-
对于副本集,客户端需要同时配置主备节点的IP地址,对于分片集群,至少配置两个 mongos的IP地址。
-
write concern设置规则:对于关键业务,write concern设置为{w:n},n>0,数字越大,
- w:1表示实际写入主节点完成返回。
- w:1,journal:true表示写主节点和日志后返回。
- w:majority表示大多数备节点写入后返回。
如果没有以w:majority写入数据,则发生主备倒换时,未同步到备机的数据有丢失风险。 对于可靠性有较高要求的,建议采用3az部署的集群。
-
业务程序禁止执行全表扫描的查询。
-
执行查询时,只选择需要返回的字段,不需要的字段不要返回。从而减少网络和 进程处理的负载,修改数据时,只修改变化需要修改的字段,不要整个对象直接 存储全部修改。
-
避免使用not的查询条件将会要 求在一个结果集中扫描所有记录。如果$not是唯一的查询条件,会对集合执行全 表扫描。
-
用or时把匹配最多结果的条件放在 最前面。
-
单个实例中,数据库的总的个数不要超过200个,总的集合个数不要超过500个。
-
业务上线前,一定要对数据库进行性能压测,评估业务峰值场景下,对数据库的负载情况
-
禁止同时执行大量并发事务,且长时间不提交。
-
业务正式上线前, 所有的查询类别,都应该先执行查询计划检查查询性能
-
建议:
- 每个连接在后台都是由一个单独线程处理,每个线程会分配1MB的栈内存。所以 连接数不宜过多,否则会占用过多的内存。
- 使用连接池,避免频繁的建立连接和断开连接,否则会导致CPU过高。
- 减少磁盘读写:避免使用不必要的upsert命令,避免查询不必要的数据。
- 优化数据分布: 大集群需要对数据进行分片,同时分散热点数据,均衡地使用实例资源。
- 减少锁冲突:避免对同一个Key,过频繁地操作。
- 减少锁等待:避免前台创建索引。
-
开发过程中对集合的每一个操作都要通过执行explain()检查其执行计划,如: db.T_DeviceData.find({"deviceId":"ae4b5769-896f"}).explain(); db.T_DeviceData.find({"deviceId":"77557c2-31b4"}).explain("executionStats") ;
-
对于查询而言,因为覆盖查询不需要读取文档,而是直接从索引中返回结果,这样的查询性能好,所以尽可能使用索引覆盖查询。如果explain()的输出显示indexOnly字段 为真,则说明这个查询就被一个索引覆盖。
-
执行计划解析:
- 看执行时间:executionStats.executionStages.executionTimeMillisEstimate和 executionStats.executionStages.inputStage. executionTimeMillisEstimate时间 越短越好。
- – executionStats.executionTimeMillis表示执行计划选择和执行的所有时间。
- – executionStats.executionStages.executionTimeMillisEstimate表示最优执行计划的执行完成时间。
- – executionStats.executionStages.inputStage. executionTimeMillisEstimate 表示最优执行计划下的子阶段执行完成时间。
- 看扫描条数:三个条目数相同为最佳。
- – executionStats. nReturned表示匹配查询条件的文档数。
- – executionStats .totalKeysExamined表示索引扫描条目数。
- – executionStats .totalDocsExamined表示文档扫描条目数。
-
应用约束
在 wiretiger 引擎中,每个集合都需要创建多个文件来保存元数据、数据及索引,磁盘上过多的小文件会导致性能下降。建议单个数据库集合个数控制在100个以内,整个数据库实例集合数量控制在2000个以内
To do
- 文件数限制修改
调整CentOS系统对打开文件数的限制,cat >> /etc/security/limits.conf <<EOF
* soft nofile 165536
* hard nofile 165536
* soft nproc 261072
* hard nproc 261072
EOF
cat >> /etc/security/limits.d/20-nproc.conf <<EOF
* soft nofile 165536* hard nofile 165536* soft nproc 261072* hard nproc 261072
EOF
- 系统内核优化
更新/etc/sysctl.conf配置,增加如下配置参数 :
#增加这个值可以允许系统支持更多的并发线程。
kernel.pid_max = 4000000
#这个参数设置了系统级别的最大线程数(所有用户的线程总和)
kernel.threads-max = 2000000
#这个参数影响的是进程的虚拟内存区域的数量,间接影响可以创建的线程数。
vm.max_map_count = 655360
6. ### 数据库设计规范
Not to do
- 禁止数据库中创建过多的表
在 wiretiger 引擎中,每个集合都需要创建多个文件来保存元数据、数据及索引,磁盘上过多的小文件会导致性能下降。建议单个数据库表个数控制在100个以内,整个数据库实例表数量控制在2000个以内。
To do
- 数据库和集合命名规范
建议数据库名以 db 开头,不能包含除 _ 以外的特殊字符,所有字母全部小写,数据库名不超过64个字符。
建议集合名以 t_开头,不能包含除 _ 以外的特殊字符、集合名不超过120个字符。
-
索引设计规范
Not to do
- 禁止线上库不带 background 参数建索引
MongoDB4.2及之前的版本,createIndex() 命令默认是 foreground 模式,这种模式下创建索引会阻塞数据库的所有操作,造成业务中断,线上业务执行 createIndex() 务必添加 background参数。
注意:
background 需要在 createIndex() 命令的 options 参数中,示例:db.test.createIndex({a: 1}, {unique:true, background: true}),切勿将不同的参数分开,示例:db.test.createIndex({a: 1}, {unique:true}, {background: true})。
- 排序字段需要放到索引中,避免业务大量在 内存 中排序造成数据库 OOM ( Out Of Memory )
MongoDB4.2及之前的版本,一条 sql 默认只允许使用32MB内存进行排序,如果超出会提示 Sort operation used more than the maximum 33554432 bytes of RAM 错误,此时可以通过执行 db.runCommand({ getParameter : 1, "internalQueryExecMaxBlockingSortBytes" : 1 } ) 调整排序内存。
但是线上业务禁止调整,因为这样会让数据库 OOM 的概率增大。建议将排序字段加到索引中,通过索引排序实现排序能力。
MongoDB4.4版本,虽然提供了磁盘排序的选项以避免排序消耗大量内存,但是建议最好使用索引排序。
- 禁止一个表内创建过多的索引
MongoDB 插入每条数据的时候同时需要写索引。索引越多,写入数据时就要花费更多的代价。因此禁止对索引的滥用,如对每个字段都建立索引,哪怕不会根据该字段进行查询。建议单表索引最多不超过10个。
To do
- 建议定期清理无用索引
索引在写操作时会带来额外的资源消耗,因此需要尽量精简索引。
MongoDB4.4之后的版本,建议使用 hidden index 先隐藏掉无用的索引,隐藏后业务确认正常再删除索引。
- 按照最左匹配原则,如果单列索引已经被复合索引包含,建议删除
额外的索引会造成写操作时候性能浪费。
- 建议尽量避免使用 nin 等操作
和其他数据库(如 MySQL)一样,不等于 not in 类的操作无法有效利用索引,尽量应该避免。
- 建议在区分度较大的字段上建立索引
如果索引字段区分度较小,查询扫描的行数依然会比较多,查询效率较低,对数据库负载影响较大。因此建立索引的字段尽量应该有较大的区分度。
-
数据库操作规范
Not to do
- 禁止上线未经过 explain() 确认执行计划的 sql
上线 sql 前需要 explain() 确认执行计划是否符合预期,否则上线可能会引起故障。
- 线上环境禁止关闭鉴权,特别是开放外网访问的数据库
关闭鉴权会将数据库暴露给所有人,特别是数据库服务器开通了外网。建议线上环境打开鉴权。如果一定要关闭鉴权,务必设置防火墙规则或 IP 白名单。
- 禁止在 admin 库、local 中存储业务数据
admin 库读写时会加 db 锁,影响性能;local库只会保存到本地,不会复制到从节点,如果发生主从切换会丢失数据。因此禁止使用 admin 库和 local 库。
- 分片集群禁止执行 db.dropDatabase() 命令后再创建同名的 db
MongoDB4.0及之前的版本,官方文件要求删除 db 并创建同名 db 后,业务读写数据前需要在所有 mongos 节点上执行重启或 flushRouterConfig 命令。
MongoDB4.2版本,官方文件要求删除 db 命令执行完以后,再执行一次删除 db 命令,并在所有 mongos 节点上执行重启或 flushRouterConfig 命令。MongoDB4.4版本,官方文件要求删除 db 命令执行完以后,再执行一次删除 db 命令,方可重建同名的db。
MongoDB5.0及以上版本,执行一次删除 db 命令即可。
因此禁止在业务代码中直接进行 db.dropDatabase() 命令后再创建同名的 db。运维人员在做该操作时,请务必按照官方文档要求进行所有必要的操作。
- 高并发高性能场景,禁止过度使用 in 和 or
in 或者 or 条件语句在数据库底层需要转换成多次查询,过多的 in 和 or 操作在高并发高性能场景,会严重影响请求的响应时延及数据库负载。
- 高并发高性能场景,禁止将复杂的运算操作交给数据库进行
MongoDB 提供了强大的计算能力(如 MapReduce 等),这些特性对开发人员非常友好,极大减轻了业务逻辑。但是这些运算不可避免是需要资源的,如果将复杂运算下沉到数据库层,高并发场景势必会给数据库造成极大的负担,数据库一旦故障会造成整个系统雪崩。
建议在高并发高性能的场景下,数据库操作保持简单,复杂的运算交给服务器并适当在数据库前端增加缓存。
- 线上业务禁止直接进行批量数据 remove
remove 命令到数据库后会先查询符合删除条件记录的 _id,之后一条条按照 _id 进行删除,并记录到 oplog 中(删除每条记录都会写一条 oplog)。
当满足 remove 条件的数据较多时对数据库压力较大,且极容易引起主从延迟突然增大。
线上业务建议直接用 drop 集合或用脚本一条条删除并控制删除速度。或尽量使用 ttl 索引。
- 业务禁止自定义 _id
字段_id 是 MongoDB 内部的默认主键,默认这是一个自增的序列。如果自定义 _id 并且业务无法保证 _id 递增,每次插入数据后,_id 索引不可避免需要对 B 树索引进行调整,这将对数据库带来额外的负担。
- 副本集直连 mongod 节点的场景,使用禁止只在连接串中配置单个 IP;分片集群禁止只连接单个 mongos 地址
线上业务如果只连接副本集主节点,一旦数据库发生 HA 会造成写入中断;如果只连接单个 mongos,这个 mongos 故障后会造成业务中断。
- 线上业务禁止设置 Write Concern j:false
Write Concern 默认一般为 j:true,表示服务端会写入 journal log 完成后再向 client 端返回。一般请勿设置 j:false,否则进程突然故障重启后,可能会造成数据丢失。
- update 语句中禁止不带条件的更新
推荐保持 multi 为默认值(false),避免程序 bug(如由于某些异常造成 query 参数传了{})造成全表数据更新。
- 除非必要,不要在高性能场景大量使用多文档事务
MongoDB4.0及之后的版本,MongoDB 提供了多文档事务。但是多文档事务只是 MongoDB 数据库能力的补充,在高并发高性能场景下,大规模使用多文档事务需要进行充分的压测。
一般来说,多文档事务提交前需要在内存中保留快照,这可能消耗大量的 cache 从而导致性能下降。
- 不建议使用短连接
MongoDB 的认证逻辑是一个比较复杂的运算过程,而且默认 MongoDB 会为每个连接创建一个线程。大量短连接会对数据库产生较大的负担,特别是没有 mongos 的副本集集群。建议使用长连接,详细参考 MongoDB的 Connection Pool 参数。
To do
- 建议局部读写而不是全读全写
查询语句中应尽量使用 set,请勿将文档全部读出来修改后再全量写进去。
- 线上环境慎重使用 db.collection.renameCollection() 命令
renameCollection() 在4.0及之前的版本会阻塞 db 的所有操作;在4.2及其之后版本会阻塞当前表及目标表的操作。而且 renameCollection() 执行期间会造成游标失效、changeStream 失效及带 --oplog 命令的 mongodump 失败等问题。线上环境禁止高峰期直接操作。
- 建议核心业务配置 WriteConcern 为 {w: “majority”} 参数
默认情况下,一般驱动的 WriteConcern 配置为 {w:1},即在主节点写入完成后认为请求成功。如果机器突然发生故障并且写入的数据还未复制到从节点,这样的配置会导致数据丢失。
因此对于线上的核心业务,建议配置 {w: “majority”},这样的配置会等数据同步大多数节点后再返回客户端。当然可靠性和性能不能兼顾,选择了 {w: “majority”} 配置后请求的延迟也会相应的增加。
-
分片集群设计规范
Not to do
- 如果使用 _id 字段作为片键,禁止使用范围分片
id 默认是一个递增的序列,随着数据量的增加会一直增大。如果 _id 作为片键并使用范围分片,集群随着数据的插入不断的进行 balance。
- 分片集群禁止直连 mongod 节点写数据
分片集群应该通过 mongos 写数据,直接通过 mongod 写入的数据无路由信息,会导致访问不到。
- 线上环境禁止长时间关闭 balancer 和 autoSplit 配置
关闭 balance 会导致片之间数据不均衡,关闭 autoSplit 可能会产生 jumbo chunk。
- 分片表尽量避免不带片键的查询
分片表不带片键进行查询,需要扫描所有分片后在 mongos 聚合结果,比较消耗性能,不推荐使用。
- 没有按片建顺序扫描的强需求,不建议使用 range 分片,推荐 hash 分片
range 分片容易引起不均衡和数据热点,而且因为无法预分片所以随着数据的写入 balance 不可避免,因此不建议使用,除非有特殊的按片键范围查询需求。
注意:
在 shardCollection 的时候,sh.shardCollection("records.people", { zipcode: 1 } ) 命令中1表示范围分片,sh.shardCollection("records.people", { zipcode: "hashed" } ) 命令中"hashed"表示 hash 分片。需注意不要用错。
- 分片集群中不建议使用非分片表
MongoDB 的分片集群如果未执行 shardCollection 命令,默认数据只存储在主分片上。大量未分片的表会造成分片和分片间的数据量不一致。集群长时间运行下去,可能会造成某些片数据量特别多甚至会打满磁盘,运维在这种情况下不得不使用 movePrimary 手动进行数据搬迁,从而增加了运维复杂度。
To do
- 建议使用区分度较大的字段作为片键,最理想的情况是使用唯一主键作为片键
假设我们有一个存储人口信息的集合,其使用性别作为片键,这个片键认为区分度较低,因为集合中性别字段相同的数据理论上会有一半之多。
如果片键区分度不大,可能导致大量的记录集中在某些片上,而这种不均衡也无法添加分片进行扩展。因此建议使用区分度较大的字段作为片键。
- 如果使用 hash 分片,建议进行预分配,特别是表比较大且经常需要大量插入数据
shardCollection() 命令默认每个分片只会创建2个 chunk,随着数据量的越来越大,MongoDB 需要不断的 balance 和 splitChunk,这将对数据库带来较大的负担。
因此在对于大集合,建议提前进行预分片(shardCollection 命令指定 numInitialChunks 参数,每个分片最大支持8192个),特别是向大集合中批量导数据。
- 线上环境务必设置 balancer 窗口,避免 balance 对业务造成影响
balance 过程会明显对数据库造成较大的压力,建议放在业务低峰期进行。
五、监控告警
-
常用监控命令
db.stats():获取数据库的统计信息,如数据大小、索引大小等。db.serverStatus():获取服务器的状态信息,包括内存使用情况、连接数、Opcounters等。db.currentOp():显示当前运行的操作,可以用于查看当前活跃的查询和写操作。db.collection.stats():获取集合的统计信息,包括文档数量、集合大小、索引大小等。
-
监控告警架构
Prometheus + MongoDB metrics_port + Grafana方式进行监控,并对核心指标提供监控大屏及告警通知。
-
监控屏注解
| 大屏面板 | 面板参数 | 参数/面板说明 |
|---|---|---|
| Member State面板 | instance | 机器运行的实例 |
| Member status | MongoDB运行状态 | |
| role | MongoDB实例中数据库的数量 | |
| Oplog Lag面板 | - | MongoDB副本集中每个次级成员在指定时间间隔内的最大复制延迟 |
| Member Health面板 | - | MongoDB实例的健康状态 |
| Node Role面板 | - | MongoDB实例的规则 |
| Uptime面板 | - | MongoDB实例的启动持续时间 |
| Available Connection面板 | - | MongoDB实例可用连接数 |
| 连接数面板 | - | MongoDB实例的活跃连接数 |
| Oplog Size面板 | - | MongoDB实例的Oplog日志大小 |
| Memory used面板 | - | MongoDB实例的内存使用大小 |
| Network I/O面板 | - | MongoDB实例的发送和接收使用流量 |
| wiretiger存储引擎的缓冲中脏字节的百分比面板 | - | WiredTiger缓存中脏数据占总配置缓存的百分比 |
| 已经使用的wiretiger存储引擎缓存比例面板 | - | WiredTiger存储引擎中当前使用的缓存大小占配置的最大缓存大小的比例 |
| 正在执行的读取文档个数面板 | - | 尝试获取读取锁的活跃客户端数量 |
| 正在执行全部数量面板 | - | 尝试获取任何类型锁的活跃客户端总数 |
| Replica Query Operations面板 | - | 在指定的时间区间内 MongoDB 复制操作的平均速率 |
| Operations Per Second面板 | - | MongoDB 实例复制操作每秒的平均数量 |
| Document Operations面板 | - | MongoDB 实例文档操作的平均数量 |
| "/" 或者 “/data” usage面板 | - | 服务器"/" 目录或者 “/data”目录下磁盘使用情况 |
| Average CPU Usage面板 | - | 每个实例的每种 CPU 模式平均 CPU 使用率 |
| Average Memory Usage面板 | - | 服务器的内存使用情况 |
| Disk IO busy 面板 | - | 服务器磁盘的IO繁忙情况 |
| Disk iops 面板 | - | 服务器磁盘的读写操作数量 |
| Disk throughput/s 面板 | - | 服务器磁盘读取和写入字节大小 |
| Disk使用率 | - | 服务器挂载磁盘的使用率情况 |
| 句柄数使用量 | - | 服务器已分配的文件描述符数量 |
-
metrics端口
命令示例: /usr/bin/MongoDB_exporter --web.listen-address=:9216 --MongoDB.uri=MongoDB://monitor:Yum!monitor2China@127.0.0.1:27017/admin --compatible-mode
端口: 9216
路由: /metrics
附录
-
mongodb基本数据类型
MongoDB 是一个基于文档的 NoSQL 数据库,它的数据是以 BSON (Binary JSON) 格式存储的。BSON 扩展了 JSON 的数据类型,以支持更多的数据类型。下面是 MongoDB 支持的一些主要数据类型:
| 类型 | 数字代码 | 别名 | 中文名 |
|---|---|---|---|
| Double | 1 | double | 浮点型 |
| String | 2 | string | 字符串 |
| Object | 3 | object | 对象 |
| Array | 4 | array | 数组 |
| Binary data | 5 | binData | 二进制数据 |
| Objectld | 7 | objectld | 对象 id |
| Boolean | 8 | bool | 布尔 |
| Date | 9 | date | 日期 |
| Null | 10 | null | 空值 |
| Regular Expression | 11 | regex | 正则表达式 |
| JavaScript | 13 | javascript | javascript 脚本 |
| 32-bit Integer | 16 | int | 整型 |
| Timestamp | 17 | timestamp | 时间 |
| 64-bit Integer | 18 | long | 长整型 |
| Decimal128 | 19 | decimal | 小数 |
| Min Key | -1 | minKey | 最小值 |
| Max Key | 127 | maxKey | 最大值 |
-
mongodb常用命令参考:
| 类别 | 含义 | 命令参考 | 备注 |
|---|---|---|---|
| MongoDB实例 | 启动服务 | systemctl start mongod_27017 | |
| 查看服务状态 | systemctl status mongod_27017 | ||
| 重启服务 | systemctl restart mongod_27017 | ||
| 停止服务 | systemctl stop mongod_27017 | ||
| 数据库相关操作 | 查询所有数据库 | show dbs | |
| 创建或切换数据库 | use database_name | ||
| 删除数据库 | db.dropDatabase() | ||
| 连接数据库 | mongo [options] | ||
| 集合相关操作 | 创建集合 | db.createCollection(name, options) | name参数 :要创建的集合名称,可选参数,指定有关内存大小及索引的选项options参数:capped如果为true则创建固定集合(有着固定大小的集合);size为固定集合指定一个最大值,如果capped为true需要指定该字段;max 指定固定集合中包含文档的最大数量 |
| 查看所有集合 | show collections | ||
| 删除集合 | db.COLLECTION_[NAME].drop() | ||
| 文档相关操作 | 插入文档 | db.COLLECTION_NAME.insert(document) | 可以使用insert()或insertOne()或insertMany()方法插入文档 |
| 查询文档 | db.COLLECTION_NAME.find(query, projection) | query :可选,使用查询操作符指定查询条件projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。 | |
| 更新文档 | db.collection.update(,,{upsert: ,multi: ,writeConcern: })或者使用db.collection.save(,{writeConcern: }) | query : update的查询条件,类似sql update查询内where后面的。update : update的对象和一些更新的操作符(如inc...)等,也可以理解为sql update查询内set后面的upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。writeConcern :可选,抛出异常的级别。document : 文档数据。 | |
| 删除文档 | db.collection.remove(,) | query :(可选)删除的文档的条件。justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。 | |
| 条件操作符 | 大于(>): $gt | db.collection2.find({age : {$gt : 30}}) | |
| 大于等于(>=): $gte | db.collection2.find({age : {$gte : 30}}) | ||
| 小于(<):$lt | db.collection2.find({age : {$lt : 20}}) | ||
| 小于等于(<=):$lte | db.collection2.find({age : {$lte : 20}}) | ||
| gt | db.collection2.find({age : {lt : 60,}}) |
-
mongodb核心参数:
| 参数 | 配置使用示例 | 说明 |
|---|---|---|
| replication.replSetName | replication:replSetName: myReplSet | 副本集名称,用于标识一个副本集。 |
| replication.members | replication:members:- _id: 0host: node1.example.com:27017priority: 10- _id: 1host: node2.example.com:27017priority: 5 | 定义副本集成员,包括成员的 ID、主机地址和优先级等属性。 |
| replication.oplogSizeMB | replication:oplogSizeMB: 512 | 设置副本集操作日志(oplog)的大小,以 MB 为单位。较大的 oplog 可以减少主从同步的频率,但会占用更多磁盘空间。 |
| sharding.shardKey | sharding:shardKey: { field: 1 } | 指定分片键,用于确定数据在分片集群中的分布。 |
| sharding.chunksize | sharding:chunksize: 64 | 设置数据块(chunk)的大小,以 MB 为单位。影响数据在分片间的迁移和平衡策略。 |
| net.bindIp | net:bindIp: 0.0.0.0 | 指定 MongoDB 服务器监听的 IP 地址,可以是单个 IP 或多个 IP,也可以使用通配符表示监听所有地址。 |
| net.port | net:port: 27017 | 设置 MongoDB 服务器监听的端口号。 |
| storage.dbPath | storage:dbPath: /data/db | 指定数据库存储文件的路径。 |
| systemLog.path | systemLog:path: /var/log/mongodb/mongod.log | 设置 MongoDB 服务器日志文件的路径。 |
| writeConcern | { w: 'majority', j: true, wtimeout: 5000 } | 控制写入操作的确认级别,例如 w 可以指定需要多少个节点确认写入成功,j 表示是否将写入操作同步到磁盘,wtimeout 是写入超时时间。 |
| readPreference | { mode: 'secondaryPreferred' } | 指定读取操作的偏好,例如可以选择从主节点、副本集的次要节点或特定标签的节点读取数据。 |