暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MongoDB-从0到1-注意事项

Snipaste_20240125_134647.png

作者:赵师的工作日(赵明中)
现役Oracle ACE、MySQL 8.0 ocp、TiDB PCTA\PCTP、Elasticsearch Certified Engineer
微信公众号:赵师的工作日
CSND:赵师的工作日
二维码.png

本文将介绍 MongoDB 开发中的一些最佳实践和常见注意事项。

一、数据建模规范

MongoDB中数据是以 BSON 格式(类似于 JSON)存储的。在设计数据库时,合理的 数据建模 是保证系统性能和可扩展性的关键。

1、选择正确的存储模型

MongoDB 提供了两种常见的存储模型:嵌套文档 和 引用文档。

  • 嵌套文档(Embedding):将相关数据嵌套在同一个文档中,适用于查询时需要一次性获取所有数据的场景。适合一对多关系和高读取需求的情况。
    示例:
  {
    "_id": 1,
    "name": "John",
    "address": {
      "street": "123 Main St",
      "city": "New York",
      "zip": "10001"
    }
  }
  • 引用文档(Referencing):在一个文档中存储另一个文档的引用,适用于数据频繁变化或有复杂关系的数据模型,避免了数据冗余。适合一对多、多对多关系的场景。
    示例:
  {
    "_id": 1,
    "name": "John",
    "address_id": 101
  }

  {
    "_id": 101,
    "street": "123 Main St",
    "city": "New York",
    "zip": "10001"
  }

选择建议:

  • 如果查询需要频繁一次性加载所有数据,尽量使用嵌套文档;
  • 如果数据量大且不常更新,使用引用文档;
  • 如果涉及到复杂的多对多关系,尽量避免嵌套过深,选择引用文档。

2、限制文档大小

MongoDB 的单个文档最大大小为 16MB。设计文档时,应确保文档的大小不超过这一限制。避免文档过大,因为较大的文档可能会影响读取性能,尤其是在查询和更新时。

3、数据冗余与规范化

在 MongoDB 中,数据冗余并非坏事,但要适度。为了查询性能,有时可以通过冗余来避免复杂的查询操作。然而,过多的数据冗余会导致更新和删除操作变得困难。根据应用场景选择合适的数据模型:

  • 对于读多写少的场景,可以适当冗余数据,减少查询时的连接开销;
  • 对于写多读少的场景,应尽量避免冗余,以减少更新时的复杂性。

二、查询优化规范

在 MongoDB 中,查询性能直接影响系统的响应时间和整体效率。因此,合理设计查询语句和索引是非常重要的。

1、使用索引优化查询

MongoDB 提供了多种索引类型,可以加速查询性能:

  • 单字段索引:为常用的查询条件字段创建索引。
    示例:
  db.users.createIndex({ "email": 1 })
  • 复合索引:当查询中包含多个字段时,使用复合索引可以优化查询效率。需要注意的是,复合索引的顺序很重要,通常应将查询条件中选择性较高的字段放在前面。
    示例:
  db.orders.createIndex({ "user_id": 1, "status": 1 })
  • 地理空间索引:如果有地理位置数据,使用地理空间索引可以优化地理位置查询。
    示例:
  db.locations.createIndex({ "location": "2dsphere" })
  • 哈希索引和唯一索引:用于提高哈希查询和保证字段唯一性的效率。

索引注意事项:

  • 索引的创建会消耗磁盘空间,并且每次插入、更新或删除操作时都会增加额外的成本,因此应该根据实际查询需求合理创建索引。
  • 定期分析查询性能,删除不再使用的索引,避免冗余的索引增加存储负担和性能负担。

2、查询优化

  • 避免全表扫描:使用索引时,避免不加限制的查询(如 find()),否则会导致全表扫描,性能差。使用适当的查询条件,尽量确保查询条件能够使用到索引。
  • 使用投影(Projection):通过投影返回查询结果时,只获取需要的字段,避免返回不必要的数据,提高查询效率。
    示例:
  db.users.find({ "age": { $gte: 18 } }, { "name": 1, "age": 1 })
  • 避免深度嵌套的查询:尽量避免深度嵌套的查询条件,避免查询效率低下。如果需要查询多个嵌套字段,考虑重构数据模型或使用聚合框架。

三、数据一致性与事务

虽然 MongoDB 是一个分布式数据库,并且在某些场景下不强制事务支持,但它也提供了 ACID 事务 来确保操作的一致性。事务可以保证多个操作的原子性,特别是对于涉及多个集合或多文档更新的操作。

1、使用事务管理复杂操作

MongoDB 从 4.x 版本开始支持多文档事务。对于一些涉及多个文档或跨多个集合的操作,可以使用事务来确保操作的原子性。
示例:

const session = client.startSession();
session.startTransaction();
try {
  db.orders.insertOne({ ... }, { session });
  db.inventory.updateOne({ _id: productId }, { $inc: { stock: -1 } }, { session });
  session.commitTransaction();
} catch (error) {
  session.abortTransaction();
} finally {
  session.endSession();
}

事务使用建议:

  • 尽量避免在性能要求较高的场景中使用事务,因为事务会增加额外的开销。
  • 在高并发场景下,合理控制事务的范围,避免长时间占用锁。

2、数据一致性模型

  • MongoDB 默认提供 最终一致性,即数据可能在一段时间内处于不一致状态,尤其是在分布式集群中。如果你的应用对数据一致性要求较高,可以通过调整写入策略、启用事务等方式来保证一致性。
  • 在副本集模式下,设置合适的 写入关注级别(Write Concern)和 读取关注级别(Read Concern),以保证数据一致性。

四、错误处理与日志记录

1、错误处理

在 MongoDB 开发中,错误处理是确保系统稳定性的关键。常见的错误包括连接错误、查询错误、索引错误等。务必使用 try-catch 块捕获异常,记录详细的错误信息,并采取合适的恢复措施。
示例:

try {
  const result = db.collection("users").find({ _id: 123 }).toArray();
} catch (error) {
  console.error("Error occurred while querying the database:", error);
  // 进一步处理错误
}

2、日志记录

合理的日志记录有助于监控 MongoDB 操作和排查问题。使用日志记录数据库操作和性能监控信息,有助于提前发现潜在的性能瓶颈和故障。
建议:

  • 记录所有关键操作,如插入、更新和删除等。
  • 使用 MongoDB 的 慢查询日志 监控慢查询,优化性能。

五、安全性与访问控制

1、用户认证与权限管理

MongoDB 提供了基于角色的访问控制(RBAC),允许对不同用户设置不同的权限。为不同的用户创建适当的角色,确保数据库的安全性。
示例:

db.createUser({
  user: "app_user",
  pwd: "password",
  roles: [
    { role: "readWrite", db: "mydb" }
  ]
});

2、数据加密

为了确保数据的安全性,MongoDB 提供了多种加密方式,避免数据泄露和未经授权的访问。

  • 传输加密:通过启用 TLS/SSL 加密传输,确保客户端与 MongoDB 服务器之间的数据传输过程不被中途拦截或篡改。
  • 配置传输加密时,需配置服务器端和客户端的证书,以确保安全通信。
    示例配置(mongod.conf):
  net:
    ssl:
      mode: requireSSL
      PEMKeyFile: /path/to/mongodb.pem
      CAFile: /path/to/ca.pem

3、存储加密(Encryption at Rest)

MongoDB 还支持在磁盘存储的数据加密,确保即使数据被窃取,未经授权的用户也无法读取数据内容。
启用存储加密时,可以使用 MongoDB 的 内建加密引擎,或者通过第三方工具(如加密文件系统)实现。
示例(启用加密):

  security:
    enableEncryption: true
    encryptionKeyFile: /path/to/keyfile

4、字段级加密

MongoDB 4.2 版本开始支持 客户端字段级加密,允许对文档中的某些字段进行加密。这样,即使数据库被攻击者访问,数据的敏感部分也能得到保护。
示例(启用字段级加密):

  const encrypted = await encryptData('sensitiveData', encryptionKey);
  await db.collection('users').insertOne({ name: 'John', ssn: encrypted });

5、访问控制和角色管理

MongoDB 使用 角色访问控制(RBAC) 来限制数据库用户的权限。通过为用户分配角色,可以控制他们对数据库的访问级别。MongoDB 提供了几个预定义的角色,如 read, readWrite, dbAdmin, userAdmin 等,开发人员也可以根据需要自定义角色。
例如,为应用创建一个具有特定权限的用户:

db.createUser({
  user: "app_user",
  pwd: "securepassword",
  roles: [
    { role: "readWrite", db: "mydb" },
    { role: "dbAdmin", db: "admin" }
  ]
});

建议:

  • 遵循 最小权限原则,只为每个用户分配必需的权限,避免过度授权。
  • 定期审查用户权限和角色分配,确保没有过期或无用的权限。

六、性能监控与优化

MongoDB 的性能监控是确保系统高效运行的重要组成部分。以下是一些常见的性能优化建议和监控工具。

1、使用 explain 分析查询性能

MongoDB 提供了 explain() 方法,帮助开发人员分析查询的执行计划和性能瓶颈。explain() 返回的结果能够帮助开发人员理解查询如何使用索引,从而优化查询。
示例:

db.orders.find({ "status": "pending" }).explain("executionStats");

通过分析执行计划,开发人员可以发现:

  • 查询是否在索引上执行;
  • 是否存在全表扫描;
  • 查询的效率是否达到预期。

2、启用日志记录和慢查询监控

MongoDB 支持日志记录功能,可以监控数据库的各类操作和性能数据。启用慢查询日志,可以帮助你发现性能瓶颈并及时进行优化。
慢查询日志设置:

operationProfiling:
  slowOpThresholdMs: 100
  mode: all

这个配置会记录所有执行超过 100 毫秒的查询,并可以通过日志进行分析。通过定期审查这些日志,开发人员能够发现哪些查询是性能瓶颈,并进行优化。

3、监控数据库状态

MongoDB 提供了多种 监控工具,如:

  • MongoDB Atlas:提供全面的数据库监控,实时查看数据库的性能指标,如 CPU 使用率、内存占用、磁盘 I/O、查询延迟等。
  • mongostat 和 mongotop:这些命令行工具可以帮助开发人员实时监控 MongoDB 的状态和活动。例如,mongotop 可以显示每个集合的读写操作统计信息。

例如,使用 mongotop 来监控某个数据库的操作:

mongotop mydb

4、缓存与数据压缩

对于频繁查询的数据,可以使用缓存(如 Redis)来减轻数据库压力。将热点数据缓存到内存中,可以显著提高读取性能,并减少数据库的负担。
此外,MongoDB 还提供了 数据压缩 选项,可以在存储数据时进行压缩,以减少存储占用。默认情况下,MongoDB 使用 WiredTiger 存储引擎,它支持数据压缩。

storage:
  wiredTiger:
    collectionConfig:
      blockCompressor: snappy
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论