mongodb-convention
version 0.0.1
基础规范
-
【强制】禁止在线上环境做数据库压力测试
-
【强制】测试,开发,线上数据库环境必须隔离
-
【强制】设计前, 应该了解mongodb的一些限制, 参考https://docs.mongodb.com/manual/reference/limits/
解读: 例如: 文档大小限制为16M; 例如: In the $lookup stage, the from collection cannot be sharded 参考: https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/index.html#sharded-collection-restrictions
命名规范
-
【强制】数据库名db_xxxx, 全部小写,禁止使用任何”_”(即下划线)以外的特殊字符,禁止使用数字打头的库名
-
【强制】数据库名最多为64个字符
-
【强制】集合名全部小写,禁止使用任何”_”(即下划线)以外的特殊字符,禁止使用数字/system打头的集合名
-
【强制】集合名称最多为64字符
-
【强制】集合中的key禁止使用任何”_”(即下划线)以外的特殊字符
-
【强制】文档中的字段名等均应尽量保持短小
库设计规范
-
【强制】在创建新的库前应尽量评估该库的体积、QPS等,提前与DBA讨论是应该新建一个库还是专门为该库创建一个新的集群
连接规范
-
【强制】正确连接副本集,副本集提供了数据的保护、高可用和灾难恢复的机制。如果主节点宕机,其中一个从节点会自动提升为从节点。
-
【建议】合理控制连接池的大小,限制连接数资源,可通过Connection String URL中的 maxPoolSize 参数来配置连接池大小。
-
【建议】复制集读选项默认情况下,复制集的所有读请求都发到Primary,Driver可通过设置的Read Preference来将读请求路由到其他的节点。
集合设计规范
-
【建议】一个库中写入较大的集合会影响其它集合的读写性能,如果业务比较繁忙的集合在一个DB中,建议最多80个集合,同时也要考虑磁盘I/O的性能
-
【建议】如果评估单集合数据量较大,可以将一个大表拆分为多个小表,然后将每一个小表存放在独立的库中或者sharding分表
-
【建议】MongoDB的集合拥有”自动清理过期数据”的功能,只需在该集合中文档的时间字段增加一个TTL索引即可实现该功能
-
【建议】设计轮询集合—集合是否设计为Capped限制集,结合实际业务设计是否需要
-
【建议】创建集合规则, 不同的业务场景是可以使用不同的配置
解读: https://docs.mongodb.com/manual/reference/method/db.createCollection/#std-label-create-collection-storage-engine-options
文档/列/字段设计规范
-
【强制】文档中的字段使用短字段名
解读: 与关系型数据库不同,集合中的每一个文档都需要存储字段名,长字段名会需要更多的存储空间 参考: https://docs.mongodb.com/manual/core/data-model-operations/#storage-optimization-for-small-documents
-
【建议】选择内嵌还是引用
▪ Comparison of embedding versus references: Embedding is better for... References are better for... Small subdocuments Large subdocuments Data that does not change regularly Volatile data When eventual consistency is acceptable When immediate consistency is necessary Documents that grow by a small amount Documents that grow by a large amount Data that often need to perform a second query to fetch Data that often exclude from the results Fast reads Fast writes ▪ 什么时候使用引用方式 内嵌文档太大,数M/超过16M Q: 如果field超过了16M呢? A: GridFS https://docs.mongodb.com/manual/core/gridfs/ 内嵌文档或数组元素会频繁修改 内嵌数组元素会持续增长且没有封顶 ▪ 引用设计的限制 使用引用的集合之间无主外键检查 使用聚合框架的$lookup来模仿关联查询 $lookup只支持left outer join $lookup的关联目标(from)不能是分片表 单个文档的更改是原子的,而引用涉及到多个文档, 对多个文档的更新需要考虑原子性和一致性
-
【建议】选择对象还是数组
通常对象的扩展性和易用性比数组更好 如果对象/数组比较大, 建议作一下基准测试, 比较时间长短与空间大小
-
【建议】了解MongoDB的一些设计模式: https://www.mongodb.com/blog/post/building-with-patterns-a-summary
-
【强制】尽量将同样类型的文档存放在一个集合中,将不同类型的文档分散在不同的集合中
解读: 相同类型的文档能够大幅度提高索引利用率,如果文档混杂存放则可能会出现查询经常需要全表扫描的情况;
-
【建议】尽量不要让数组字段成为查询条件
-
【建议】如果字段较大,应尽量压缩存放
解读: 不要存放太长的字符串,如果这个字段为查询条件,那么确保该字段的值不超过1KB MongoDB的索引仅支持1K以内的字段,如果你存入的数据长度超过1K,那么它将无法被索引
-
【建议】如果评估单集合数据量较大,可以将一个大表拆分为多个小表,然后将每一个小表存放在独立的库中或者sharding分表
索引规范
-
【强制】MongoDB 的组合索引使用策略与MySQL一致,遵循”最左原则”
-
【强制】索引名称长度不要超过128字符
-
【强制】应尽量综合评估查询场景, 通过评估尽可能的将单列索引并入组合索引以降低索引数量
-
【建议】优先使用覆盖索引
-
【建议】创建组合索引的时候,应评估索引中包含的字段,尽量将数据基数大(唯一值多的数据)的字段放在组合索引的前面
-
【强制】充分评估是否在数组中创建索引,在数组中创建索引实际上会对数组中的每个元素建一个索引条目, 因此数组索引的代价比单值索引要高
-
【建议】MongoDB支持TTL索引,该索引能够按你的需要自动删除XXX秒之前的数据并会尽量选择在业务低峰期执行删除操作;看业务是否需要这一类型索引
-
【建议】在数据量较大的时候,MongoDB索引的创建是一个缓慢的过程,所以应当在上线前或数据量变得很大前尽量评估,按需创建会用到的索引
-
【建议】如果数据是地理位置信息,可以在该字段上添加MongoDB支持的地理索引
API/SQL规范
-
【建议】优先使用updateOne, updateMany代替update, 同理还有insert等
解读: 更清晰的语义 例如update需要设置multi
-
【建议】使用findOneAndUpdate等代替find和insert/update
解读: 一次操作通常比两次操作效率要大幅提高
-
【强制】在查询条件的字段或者排序条件的字段上必须创建索引
-
【强制】查询结果只包含需要的字段,而不查询所有字段
-
【强制】在文档级别更新是原子性的,这意味着一条更新10个文档的语句可能在更新3个文档后由于某些原因失败。应用程序必须根据自己的策略来处理这些失败
-
【建议】限定返回记录条数
-
【建议】在开发业务的时候尽量检查自己的程序性能,可以使用explain()函数检查你的查询执行详情,另外hint()函数相当于MySQL中的force index()
-
【建议】如果体积大小/文档数固定,建议创建capped集合,这种集合写入性能非常高并无需专门清理老旧数据,需要注意的是capped表不支持remove()和update()操作
-
【建议】查询中的某些操作符可能会导致性能低下,如ne, exists, or尽量在业务中不要使用
解读: exist: 因为松散的文档结构导致查询必须遍历每一个文档 ne: 如果当取反的值为大多数,则会扫描整个索引 not: 可能会导致查询优化器不知道应当使用哪个索引,所以会经常退化为全表扫描 nin: 全表扫描 or: 有多少个条件就会查询多少次,最后合并结果集,所以尽可能的使用in
-
【建议】不要一次取出太多的数据进行排序,MongoDB目前支持对32MB以内的结果集进行排序,如果需要排序,那么请尽量限制结果集中的数据量
-
【建议】如果需要清理掉一个集合中的所有数据,那么remove()的性能是非常低下的,该场景下应当使用drop(); remove()是逐行操作,所以在删除大量数据的时候性能很差
-
【建议】在查询中如果有范围条件, 那么尽量和定值条件放在一起进行过滤, 并在创建索引的时候将定值查询字段放在范围查询字段前
操作规范
参考
-
SpringForAll社区
-
《MongoDB.The.Definitive.Guide.3rd》