首页 > 解决方案 > 使用 Spring Data MongoDb v2.x 时如何访问 explain() 方法和 executionStats?

问题描述

是时候问问社区了。我无法在任何地方找到答案。

我想创建一个通用方法,可以跟踪我所有的存储库查询,并在查询未优化(也就是缺少索引)时警告我。

使用 Spring Data MongoDb v2.x 及更高版本以及引入 Document API,我无法弄清楚如何访问 DBCursor 和 explain() 方法。

旧的方法是这样做: https ://enesaltinkaya.com/java/how-to-explain-a-mongodb-query-in-spring/

对此的任何建议表示赞赏。

标签: spring-data-mongodb

解决方案


我知道这是一个老问题,但想从我在使用 Java Mongo API 驱动程序 v2.X 的 cosmos Db 项目的容量规划中提出的类似要求中提供意见。

总结Enes Altınkaya 的博文。使用@autowiredMongoTemplate,我们使用runCommand通过传递 Document 对象来执行服务器端数据库查询。获得解释输出,我们将 Query 或 Aggregate 对象解析为新的 Document 对象并添加条目{"executionStats": true}(或{"executionStatistics": true}用于 cosmos Db)。然后使用“解释”作为属性将其包装在另一个文档中。

例如:

查询

public static Document documentRequestStatsQuery(MongoTemplate mongoTemplate, 
Query query, String collectionName) {
    Document queryDocument = new Document();
    queryDocument.put("find", collectionName);
    queryDocument.put("filter", query.getQueryObject());
    queryDocument.put("sort", query.getSortObject());
    queryDocument.put("skip", query.getSkip());
    queryDocument.put("limit", query.getLimit());
    queryDocument.put("executionStatistics", true);
    Document command = new Document();
    command.put("explain", queryDocument);
    Document explainResult = mongoTemplate.getDb().runCommand(command);
    return explainResult;
  }

聚合

public static Document documentRequestStatsAggregate(MongoTemplate mongoTemplate, 
Aggregation aggregate, String collection) {
    Document explainAggDocument = Document.parse(aggregate.toString());
    explainAggDocument.put("aggregate", collection);
    explainAggDocument.put("executionStatistics", true);
    Document command = new Document();
    command.put("explain", explainAggDocument);
    Document explainResult = mongoTemplate.getDb().runCommand(command);
    return explainResult;
  }

对于实际监控,由于 Service 和 Repository 类是 MongoTemplate 抽象,我们可以使用 Aspects 在应用程序运行时捕获查询/聚合执行细节。

例如:

@Aspect
@Component
@Slf4j
public class RequestStats {
  @Autowired
  MongoTemplate mongoTemplate;

  @After("execution(* org.springframework.data.mongodb.core.MongoTemplate.aggregate(..))")
  public void logTemplateAggregate(JoinPoint joinPoint) {
    Object[] signatureArgs = joinPoint.getArgs();
    Aggregation aggregate = (Aggregation) signatureArgs[0];
    String collectionName = (String) signatureArgs[1];
    Document explainAggDocument = Document.parse(aggregate.toString());
    explainAggDocument.put("aggregate", collectionName);
    explainAggDocument.put("executionStatistics", true);
    Document dbCommand = new Document();
    dbCommand.put("explain", explainAggDocument);
    Document explainResult = mongoTemplate.getDb().runCommand(dbCommand);
    log.info(explainResult.toJson());
  }
  
}

每次执行后输出如下内容:

{
                "queryMetrics": {
                    "retrievedDocumentCount": 101,
                    "retrievedDocumentSizeBytes": 202214,
                    "outputDocumentCount": 101,
                    "outputDocumentSizeBytes": 27800,
                    "indexHitRatio": 1.0,
                    "totalQueryExecutionTimeMS": 15.85,
                    "queryPreparationTimes": {
                        "queryCompilationTimeMS": 0.21,
                        "logicalPlanBuildTimeMS": 0.5,
                        "physicalPlanBuildTimeMS": 0.58,
                        "queryOptimizationTimeMS": 0.1
                    },
                    "indexLookupTimeMS": 10.43,
                    "documentLoadTimeMS": 0.93,
                    "vmExecutionTimeMS": 13.6,
                    "runtimeExecutionTimes": {
                        "queryEngineExecutionTimeMS": 1.56,
                        "systemFunctionExecutionTimeMS": 1.36,
                        "userDefinedFunctionExecutionTimeMS": 0
                    },
                    "documentWriteTimeMS": 0.68
                }
// ...

我通常将其记录到另一个集合中或写入文件。


推荐阅读