MongoDB是当下非常流行的文档型NoSQL数据库,凭借灵活的 schema 设计和良好的水平扩展能力,被广泛应用于各类互联网业务场景。不过很多开发者在使用过程中容易忽略一些核心细节,导致后续出现性能问题或者数据异常。
一、合理设计文档结构
MongoDB的文档结构不需要提前定义固定 schema,但并不意味着可以随意设计。需要根据业务查询场景决定字段的嵌套层级,避免过深的嵌套导致查询和更新复杂度上升。对于频繁一起查询的字段可以放在同一个文档中,减少跨文档关联查询。
二、正确使用索引
索引是提升查询性能的核心手段,但是不能盲目创建索引。需要根据实际查询条件创建对应的索引,同时避免创建过多冗余索引,因为索引会占用额外的存储空间,还会降低写入性能。可以使用explain()方法分析查询语句的执行计划,判断索引是否生效。
// 创建复合索引,针对查询条件为name和age的场景
db.users.createIndex({ name: 1, age: -1 })
// 分析查询语句的执行计划
db.users.find({ name: "张三", age: { $gt: 18 } }).explain("executionStats")
三、控制批量操作的大小
MongoDB支持批量插入、批量更新和批量删除操作,但是批量操作的数据量不能过大。如果单次批量操作的数据量太多,会导致请求超时或者占用过多的内存资源。一般建议单次批量操作的数据量控制在1000条以内,根据实际的业务场景和服务器配置调整。
四、避免大文档存储
MongoDB单个文档的最大存储限制是16MB,虽然这个限制看起来很大,但是不建议存储接近这个大小的文档。大文档会导致网络传输和内存占用的开销上升,同时更新大文档的某一个小字段时,也会产生额外的性能消耗。如果有大文件存储需求,建议使用GridFS。
五、合理设置副本集
生产环境的MongoDB集群建议至少配置3个节点的副本集,保证数据的高可用性。副本集的节点数量建议为奇数,避免出现选举时的票数平局问题。同时需要根据业务需求设置合适的读写偏好,比如读操作可以配置为从从节点读取,分担主节点的压力。
六、分片集群的合理规划
当数据量超过单节点的存储上限或者读写压力超过单节点的处理能力时,需要引入分片集群。分片的片键选择非常关键,需要选择基数大、分布均匀的字段作为片键,避免出现数据倾斜的问题。同时分片集群的配置相对复杂,需要提前做好容量规划。
七、使用投影减少返回数据量
查询数据时如果只需要部分字段,一定要使用投影参数指定需要返回的字段,避免返回整个文档的所有字段。这样可以减少网络传输的数据量,提升查询的响应速度,尤其是在文档字段比较多或者文档比较大的场景下,效果非常明显。
// 只返回users集合中文档的name和age字段,不返回_id和其他字段
db.users.find({ status: "active" }, { name: 1, age: 1, _id: 0 })
八、注意事务的使用场景
MongoDB 4.0版本之后支持多文档事务,但是事务会带来额外的性能开销,不要滥用事务。只有在需要保证多个文档操作的原子性时才使用事务,比如转账场景中需要同时更新两个账户的余额。对于单文档的操作,MongoDB本身就可以保证原子性,不需要使用事务。
九、定期监控和清理过期数据
对于有时效性的数据,比如日志、会话信息等,可以使用TTL索引自动清理过期数据,避免无用数据占用过多的存储空间。同时需要定期监控MongoDB的各项指标,比如CPU使用率、内存使用率、磁盘IO、慢查询数量等,及时发现潜在的问题。
// 创建TTL索引,createdAt字段的值超过3600秒后自动删除对应文档
db.logs.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })
十、做好数据备份和恢复
不管业务规模大小,都需要做好MongoDB的数据备份工作。可以使用mongodump工具定期备份数据,备份文件需要存储在不同的物理位置,避免因为服务器故障导致备份文件也丢失。同时要定期测试数据恢复流程,确保在数据丢失时可以快速恢复。