MongoDB 性能优化

158 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第16天,点击查看活动详情

在实际开发中,有的时候需要对开发和操作 MongoDB 的应用进行性能分析,从而进一步对性能进行优化。本实验将会讲解一些 MongoDB 中常见的性能优化方案,比如:索引查询优化、数据库设计优化等。

MongoDB Profiler

我们都知道在 MySQL 数据库中有个慢查询日志,它可以帮助我们进行 SQL 语句的优化,提高系统的稳定性和流畅性。同样在 MongoDB 数据库中也有类似的功能,它就是 MongoDB Profiler。我们可以通过它来记录一些超过阈值的查询,后面就可以根据这些超过阈值的记录进行相应的优化(下文将超过阈值的查询简称为“慢查询”)。

MongoDB 中的慢查询记录存储在 system.profile 中,默认情况是关闭的。如果想开启,可以通过 db.setProfilingLevel(level) 进行配置(也可以直接在 MongoDB 配置文件中进行配置)。其中 level 的值如下表所示:

状态码描述
0表示关闭慢查询,默认值。
1收集超过阈值的查询。
2为所有的数据库开启慢查询记录,收集所有的数据。

➡️ 启用慢查询可以有两种方式:

  • 使用 MongoDB 交互式客户端使用命令启用(即 MongoDB shell)

    a. 给所有的数据库开启慢查询记录。

    db.setProfilingLevel(2)
    

    b. 给数据库 lanqiao 开启慢查询,并指定阈值,如果查询超过 20 毫秒就需要被记录。

    use test
    db.setProfilingLevel(1,{slowms:20})
    

    c. 给数据库 lanqiao 设置随机采集慢查询的百分比值,采集 42% 的内容。

    db.setProfilingLevel(1,{sampleRate:0.42})
    

    ✨ 说明:sampleRate 默认为 1,表示全部采集;上面设置的 0.42 表示只采集 42% 的内容。

  • 在 MongoDB 的 mongod.conf 配置文件中进行配置

    a. 用 = 号的常规格式配置。

    profile=1
    slowms=20
    

    ✨ 说明:profile 设置开启慢查询的级别,slowms 设置阈值。

    b. 用 YAML 配置文件格式配置。

    operationProfiling:
       mode: <string>
       slowOpThresholdMs: <int>
       slowOpSampleRate: <double>
    

    ✨ 说明:

    • mode:设置开启慢查询的级别,可选值 off、slowOp(对应上面的等级 1)、all(对应上面的等级 2),默认为 off。
    • slowOpThresholdMs:设置阈值,默认值为 100,单位毫秒。
    • slowOpSampleRate: 设置随机采集慢查询的百分比值,sampleRate 值默认为 1,表示都采集,0.42 表示采集 42% 的内容。

➡️ 查看慢查询的记录: 可以使用 db.system.profile.find() 命令查看。

  • 查看最近的 5 个慢查询日志

    db.system.profile.find().limit(5).sort({ts:-1}).pretty()
    

    ✨ 说明:ts 指的是命令执行的时间。

  • 查看数据库 lanqiao 中集合为 course 的日志

    db.system.profile.find({ns:'lanqiao.course'}).pretty()
    

    ✨ 说明:ns 指的是操作的数据库和集合。

  • 查询低于 5 毫秒的日志

    db.system.profile.find({millis:{$gt:5}}).pretty()
    

    ✨ 说明:millis 指的是从 MongoDB 操作开始到结束耗费的时间。

➡️ 关闭慢查询db.setProfilingLevel(0)

查询优化

查询,我们可以进行哪些优化。

  1. 避免一些低效率的操作符,比如下表中的操作符:
操作符说明
$where 和 $exits这两个操作符不能使用索引。
$ne此操作符用于取反操作,而一般来说取反操作效率比较低。因为它必须查看所有的索引条目,此时就不得不扫描整个索引。
$not有时候可以使用索引,但是通常情况下不知道如何使用索引,所以大多数情况下 $not 还是会执行全表扫描。
$nin此操作符总是会全表扫描。
  1. OR 查询

    $or 可以对每个语句都适用索引,因为 $or 实际上是执行两次查询然后将结果合并。通常来说,使用 or 查询多次再合并结果,不如单次查询的效率高。对于单个字段,应该使用 $in

  2. 创建索引

    索引是一个很重要的东西,有了正确的索引,MongoDB 就可以为应用程序提供快速的查询。但不是索引越多越好,有时候错误的使用索引反而会产生慢查询。因为使用索引需要进行两次查找:一次查找索引条目,一次根据索引指针去查找相应的文档。而全表扫描只需要进行一次查询。假如现在是最坏的情况,使用索引进行查找次数会是全表扫描的两倍,此时效率就明显比全表扫描低。

    并没有一个严格的规定说,什么时候索引很有用,我们可以根据慢查询或者一些其他的查询分析,来判断索引的使用。

  3. MongoDB 查询优化器

    MongoDB 的查询计划会将多个索引并行的去执行,最早返回 100 个结果的就是胜者,其他查询计划都会被终止。

数据库设计优化

实际开发中,在数据库的设计阶段,就需要明确集合用途,这对性能调优非常重要。根据集合中数据最常用的操作,对于频繁更新和频繁查询的集合,我们最需要关注的重点是他们的范式化程度

✨ 说明:范式是为了消除重复数据减少冗余数据,从而让数据库内的数据更好的组织,让磁盘空间得到更有效利用的一种标准化标准,满足高等级的范式的先决条件是满足低等级范式

  • 数据库的设计优化

    • 范式化设计
    • 反范式化设计
    • 范式化和反范式化设计