MongoDB开发规范

概述

存储选型

  1. 主要解决大量数据的访问效率问题,减少mysql压力。MongoDB内建了多种数据分片的特性,可以很好的适应大数据量的需求。内建的Sharding分片特性避免系统在数据增长的过程中遇到性能瓶颈。
  2. 复杂数据结构,以多种不同查询条件去查询一份数据。MongoDB的BSON数据格式非常适合文档化格式的存储及查询;支持丰富的查询表达式,可轻易查询文档中内嵌的对象和数组及子文档。
  3. 非事务并且关联性集合不强都可以使用(MongoDB4.0+支持跨Collection事务,MongoDB4.2+支持跨Shard事务)。
  4. 无多文档事务性需求及复杂关联检索。
  5. 业务快速迭代,需求频繁变动的业务。
  6. 数据模型不固定,存储格式灵活的场景。
  7. 单集群读写并发过大无法支持业务增长的场景。
  8. 期望5个9的数据库高可用场景。

MongoDB库的设计规范

  • 库名全部小写,禁止使用任何"_"(下划线)外的特殊字符,禁止使用数字打头的库名。
    如:db_xxx;
    说明:mongoDB中库以文件夹的形式存在,使用特殊字符或其他不规范的命名方式会导致命名混乱。
  • 数据库名最多64字符。
  • 在创建新的库前应尽量评估该库的体积、QPS等,提取与DBA讨论是应该新建一个库还是专门为该库创建一个新的集群。

使用示例

mongoDB集合的设计

  • 集合名全部小写,禁止使用任何"_"(下划线)以外的特殊字符,禁止使用数字打头的集合名称,如:
    123_abc,禁止system打头(system是系统集合前缀)。
  • 集合名称最多为64字符。
  • 【建议】一个库中写入较大的集合会影响其他集合的读写性能,如果业务比较繁忙的集合在一个DB中,建议最多80个集合,同时也需要考虑磁盘I/O的性能。
  • 【建议】如果评估单集合数据量较大,可以将一个大表拆分为多个小表,然后将每个小表存放在独立的库中或者sharding分表;
  • 【建议】创建集合规则
db.createCollection("logs",
{ "storageEngine": { "wiredTiger": 
{ "configString": "internal_page_max=16KB,
   leaf_page_max=16KB,leaf_value_max=8KB,os_cache_max=1GB"} } 
})
  1. 如果是读多写少的表在创建时我们尽量将page size设置的比较小,比如16KB,如果表数据量不大(“internal_page_max=16KB,leaf_page_max=16KB,leaf_value_max=8KB,os_cache_max=1GB”)
  2. 如果这个读多写少的表数据量比较大,可以为其设置一个压缩算法,例如:“block_compressor=zlib, internal_page_max=16KB,leaf_page_max=16KB,leaf_value_max=8KB”。
  3. 注意:该zlib压缩算法对cpu消耗特别大,如果使用snapp消耗20% cpu,而且使用zlib能消耗90%cpu,甚至100%。
  4. 如果写多读少的表,可以将leaf_page_max设置到1MB,并开启压缩算法,也可以为其制定操作系统层面page cache大小的os_cache_max值,让它不会占用太多的page cache内存,防止影响读操作。
  5. 总结
    - 读多写少的集合
    internal_page_max=16KB 默认为4KB
    leaf_page_max=16KB 默认为32KB
    leaf_value_max=8KB 默认为64MB
    os_cache_max=1GB 默认为0
    数据量比较大 block_compressor=zlib 默认为snappy

使用示例

mongoDB文档的设计

  • 文档中的key禁止使用任何"_"(下划线)以外的特殊字符。
  • 禁止使用_id,如向_id中写入自定义内容,_id是集合默认的主键。
    说明:MongoDB的表与InnoDB相似,都是索引组织表,数据内容跟在主键后,而_id是MongoDB中的默认主键,一旦_id的值为非自增,当数据量达到一定程度之后,每一次写入都可能导致主键的二叉树大幅度调整,这将是一个代价极大的写入, 所以写入就会随着数据量的增大而下降,所以一定不要在_id中写入自定义的内容。
  • 尽量将同样类型的文档(文档的字段一致)存放在一个集合中,将不同类型的文档分散在不同的集合中;相同类型的文档能够大幅度提高索引利用率,如果文档混杂存放则可能出现查询经常需要全表扫描的情况。
    -【建议】 尽量不要让数组字段成为查询条件。
  • 【建议】如果字段较大,应尽量压缩存放。
    说明:不要存放太长的字符串,如果这个字段为查询条件,那么确保该字段的值不超过1KB;MongoDB的索引仅支持1K以内的字段,如果你存入的数据长度超过1K,那么它将无法被索引。
  • 尽量存储统一了大小写后的数据。

mongoDB索引的设计

  • MongoDB 的组合索引使用策略与 MySQL 一致,遵循"最左原则"。
  • 优先使用覆盖索引(查询条件和投影字段被索引包含)。
  • 索引名称长度不要超过128个字符。
  • 尽可能的将单列索引并入组合索引以降低索引数量。
  • 【建议】创建组合索引的时候,应评估索引中包含的字段,尽量将数据基数大(唯一值多的数据)的字段放在组合索引的前面。
  • 【建议】MongoDB支持TTL索引,该索引能够按你的需要自动删除XXX秒之前的数据并会尽量选择在业务低峰期执行删除操作,看业务是否需要这一类索引。
  • 【建议】在数据量较大的时候,MongoDB索引的创建是一个缓慢的过程,所以应当在上线前或数据量变得很大前尽量评估,按需创建会用到的索引;
  • 【建议】如果你存放的数据是地理位置信息,比如:经纬度数据。那么可以在该字段上添加 MongoDB 支持的地理索引:2d 及 2dsphere,但他们是不同的,混用会导致结果不准确;

索引使用技巧

API使用规范

  • 在查询条件的字段或者排序条件的字段上必须创建索引,提高查询性能。
  • 查询结果只包含需要的字段,而不查询所用字段。
  • 文档级别插入、更新是原子性的,比如一条插入10个文档的语句在插入3个文档后由于某些原因失败,应用程序必须根据自己的策略来处理这些失败。
    示例:
try {
  db.comment.insertMany([{"articleid":"100001","content":"我们不应该把清晨浪费在手机上。","userid":"1002"},{"articleid":"100002","content":"我们不应该把清晨浪费在手机上。","userid":"1003"}]);
} catch(Exception e) {
  // handle exception
}
  • 【建议】单个文档的BSON size不能超过16M。
  • 【建议】禁用不带条件的update、remove或者find语句。
  • 【建议】限定返回记录条数,每次查询结果不超过2000条。如果需要查询2000条以上的数据,在代码中使用多线程并发查询。
  • 【建议】在写入数据的时候,如果你需要实现类似MYSQL中的INSERT INTO ON DUPLICATE KEY UPDATE的功能,那么可以选择upsert()函数。
  • 【建议】写入大量数据的时候可以选择使用batchInsert,但目前MongoDB每一次能够接受的最大消息长度为48MB,如果超出48MB,将会自动拆分为多个48MB的消息。
  • 【建议】索引中的-1和1是不一样的,一个是逆序,一个是正序,应当根据自己的业务场景建立适合的索引排序。
  • 【建议】在开发业务的时候尽量检查自己的程序性能,可以使用explain()函数检查你的查询执行详情,另外hint()函数相当于MySQL中的force index();
  • 【建议】如果你结合体积大小/文档数固定,那么建议创建capped(封顶)集合,这种集合的写入性能非常高并无需专门清理老旧数据,需要注意的是capped表不支持remove()和update()操作。
  • 【建议】查询中 n e , n o t e x i s t s , ne,not exists, nenotexistsnin, n o r , 尽 量 在 业 务 中 不 要 使 用 。 说 明 : nor,尽量在业务中不要使用。 说明: nor使exists,$ne:如果当取反的值为大多数,则会扫描整个索引。
    n i n , nin, ninnor:扫描整个索引或者全表扫描。
    示例:
db.storage.find({"orderId":{$ne:"rBAGWF8D2b-AN0CiAAAUB0a_VwU774"}});
db.storage.find({$or:[{"orderId":"rBAGWF79eP-ANFkeAADBnX_NY2w775"},{"orderId":"rBAGVl7-8BWAKtHEAACEltXgOfc214"}]});
db.storage.find({$nor:[{ orderId:"rBAGWF79eP-ANFkeAADBnX_NY2w775"}, {"orderId":"rBAGVl7-8BWAKtHEAACEltXgOfc214"}]});
db.storage.find({"orderId":{$nin:["rBAGWF79eP-ANFkeAADBnX_NY2w775","rBAGVl7-8BWAKtHEAACEltXgOfc214"]}});
db.storage.find({"orderId":{$exists:false}});
  • 【建议】不要一次取出太多的数据进行排序,MongoDB 目前支持对32MB以内的结果集进行排序,如果需要排序,那么请尽量限制结果集中的数据量;
  • 【建议】MongoDB 的聚合框架非常好用,能够通过简单的语法实现复杂的统计查询,并且性能也不错;
  • 【建议】如果需要清理掉一个集合中的所有数据,那么 remove() 的性能是非常低下的,该场景下应当使用 drop();remove() 是逐行操作,所以在删除大量数据的时候性能很差;
  • 【建议】在使用数组字段做为查询条件的时候,将与覆盖索引无缘;这是因为数组是保存在索引中的,即便将数组字段从需要返回的字段中剔除,这样的索引仍然无法覆盖查询;
  • 【建议】在查询中如果有范围条件,那么尽量和定值条件放在一起进行过滤,并在创建索引的时候将定值查询字段放在范围查询字段前;

批量查询技巧

连接规范

  • 正确连接副本集,副本集提供了数据的保护、高可用和灾难恢复的机制。如果主节点宕 机,其中一个从节点会自动提升为从节点。
  • 【建议】合理控制连接池的大小,限制连接数资源,可通过Connection String URL中的 maxPoolSize 参数来配置连接池大小。
  • 【建议】复制集读选项 默认情况下,复制集的所有读请求都发到Primary,Driver可通过设置的Read Preference 来将 读请求路由到其他的节点。

springBoot操作MongoDB

参考

高效mongoDB开发规范
SpringBoot重点详解–使用MongoDB
2020最新MongoDB规范你应该了解一下

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

融极

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值