更新时间:2022-07-29 GMT+08:00

慢请求定位方法

在相同业务场景下,架构设计和库、集合、索引等设计会影响查询性能,良好的设计可以提高查询性能,反之会出现很多慢请求(执行时间很长的语句),这些慢请求会消耗很多的系统性能。

本文主要介绍导致慢请求的原因和解决方案。

排查思路

DDS支持在控制台上查看慢日志信息,针对慢日志中过长的操作需要从执行最慢的操作入手,逐个进行优化。

  • 对于操作执行时长超过1s时,对应的操作可能会有问题,需要视具体情况分析。
  • 对于操作执行时长超过10s时,则需要优化对应的操作。

    若存在某个聚合操作,因其比较慢,此时操作执行时长超过10s的情况属于正常现象。

分析方法

  1. 连接数据库。

  2. 针对慢查询可以通过如下命令检查其执行计划。

    explain()

    例如:

    db.test.find({"data_id" : "ae4b5769-896f-465c-9fbd-3fd2f3357637"}).explain();
    db.test.find({"data_id" : "775f57c2-b63e-45d7-b581-3822dba231b4"}).explain("executionStats");

    对于查询而言,因为覆盖查询不需要读取文档,而是直接从索引中返回结果,这样的查询非常高效,所以尽可能使用索引覆盖查询。如果explain()的输出显示indexOnly字段为真,则说明这个查询就被一个索引覆盖。

  3. 执行计划解析。

    1. 查看执行时间。

      executionStats.executionStages.executionTimeMillisEstimate和executionStats.executionStages.inputStage. executionTimeMillisEstimate时间越短越好。

      表1 参数说明

      参数名称

      描述

      executionStats.executionTimeMillis

      执行计划选择和执行的所有时间

      executionStats.executionStages.executionTimeMillisEstimate

      最优执行计划的执行完成时间

      executionStats.executionStages.inputStage. executionTimeMillisEstimate

      最优执行计划下的子阶段执行完成时间

    2. 查看扫描条数。

      表2中三个条目相同为最佳。

      表2 参数说明

      参数名称

      描述

      executionStats. nReturned

      匹配查询条件的文档数

      executionStats .totalKeysExamined

      索引扫描条目数

      executionStats .totalDocsExamined

      文档扫描条目数

    3. 查看Stage状态。
      性能较好的Stage状态组合如下:
      • Fetch+IDHACK
      • Fetch+ixscan,
      • Limit+(Fetch+ixscan)
      • PROJECTION+ixscan
      表3 状态说明

      状态名称

      描述

      COLLSCAN

      全表扫描

      SORT

      内存中进行排序

      IDHACK

      根据_id进行查询

      TEXT

      全文索引

      COUNTSCAN

      未用索引计数

      FETCH

      索引扫描

      LIMIT

      使用Limit限制返回数

      SUBPLA

      未用索引的$or查询阶段

      PROJECTION

      使用索引计数

      COUNT_SCAN

      使用索引计数

优化方案

  • 对于无索引覆盖的查询,则需要根据查询条件创建对应索引。
  • 对于点查可以创建hash索引。
  • 对于多字段查询,单字段重复度较高的场景,创建复合索引。
  • 对于范围查找,结果集有序的查询,创建升序或者降序索引。
  • 对于复合索引,由于复合索引是前缀排序查询,所以查询条件的顺序要与索引字段的顺序一致。
  • 对于分区集合(表)、大集合(超过10万记录),不要直接对大数据量的表使用模糊查询(即不能使用like)。这样会造成查询扫描的记录过多,建议先基于索引字段查询,过滤出较小的数据集后再使用模糊查询。
  • 避免使用$not。MongoDB 并不会对缺失的数据进行索引,因此$not的查询条件将会要求在一个结果集中扫描所有记录。如果$not是唯一的查询条件,会对集合进行全表扫描。
  • 用$and时把匹配最少结果的条件放在最前面,用$or时把匹配最多结果的条件放在最前面。
  • 检查实例规格的性能基线,分析当前的业务需求是否达到上限,如已达当前实例性能瓶颈,请及时提升实例规格。