【问题标题】:How do I access the explain() method and executionStats when using Spring Data MongoDb v2.x?使用 Spring Data MongoDb v2.x 时如何访问 explain() 方法和 executionStats?
【发布时间】:2025-12-14 04:10:01
【问题描述】:

是时候询问社区了。我在任何地方都找不到答案。

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

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

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

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

【问题讨论】:

    标签: spring-data-mongodb


    【解决方案1】:

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

    总结Enes Altınkaya's blog post。使用@autowired MongoTemplate,我们使用 runCommand 通过传递 Document 对象来执行服务器端数据库查询。获得 explain 输出,我们将 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
                    }
    // ...
    

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

    【讨论】:

      最近更新 更多