4

我在理解 couchbase 查询计划的工作方式方面存在问题。我将 SpringData 与 Couchbase 4.1 一起使用,并提供 Couchbase 存储库的自定义实现。在我的 Couchbase 存储库的自定义实现中,我有以下方法:

String queryAsString = "SELECT MyDatabase.*, META().id as _ID, META().cas as _CAS FROM MyDatabase WHERE segmentId = $id AND _class = $class ORDER BY executionTime DESC LIMIT 1";
JsonObject params = JsonObject.create()
        .put(CLASS_VARIABLE, MyClass.class.getCanonicalName())
        .put(ID_VARIABLE, segmentId);

N1qlQuery query = N1qlQuery.parameterized(queryAsString, params);
List<MyClass> resultList = couchbaseTemplate.findByN1QL(query, SegmentMembers.class);
return resultList.isEmpty() ? null : resultList.get(0);

结果,Spring Data 向 Couchbase 生成以下 json 对象表示的查询:

{
    "$class":"path/MyClass",
    "statement":"SELECT MyDatabase.*, META().id as _ID, META().cas as _CAS from  MyDatabase where segmentId = $id AND _class = $class ORDER BY executionTime DESC LIMIT 1",
    "id":"6592c16a-c8ae-4a74-bc17-7e18bf73b3f8"
}

当我通过 Java 和 N1QL Rest Api 或通过 cbq consol 执行它时,问题在于性能。为了在 cbq 中执行此查询,我只需将参数引用替换为精确值。

在 select 语句之前添加 EXPLAIN 子句后,我提到了不同的执行计划。通过 Java Spring Data 或 N1QL Rest Api 将此查询作为参数化查询执行我已经提到该查询不使用我专门为这种情况创建的索引。索引定义如下:

CREATE INDEX `testMembers` ON MyDatabase `m`(`_class`,`segmentId`,`executionTime`) WHERE (`_class` = "path/MyClass") USING GSI;

因此,当我通过 cbq consol 执行查询时,Couchbase 使用我的 idnex 并且查询性能非常好。但是,当我通过 N1QL rest api 或 Java 执行此查询时,我看到该查询不使用我的索引。您可以在下面找到证明这一事实的部分执行计划:

"~children": [
{
  "#operator": "PrimaryScan",
  "index": "#primary",
  "keyspace": "CSM",
  "namespace": "default",
  "using": "gsi"
},

那么,问题在于,couchbase 查询优化器的权利和合法行为是什么?这是否意味着查询计划不考虑参数的实际值?并且我是否手动将值放入查询字符串或存在任何其他方式来使用具有正确索引选择的 N1Ql 参数化查询?

编辑

根据shashi raj的回答,我将 N1qlParams.build().adhoc(false) 参数添加到参数化的 N1QL 查询中。这并不能解决我的问题,因为我仍然有这个查询的性能问题。此外,当我打印查询时,我看到它与我之前描述的相同。因此,我的查询仍然错误分析并导致性能下降。

4

2 回答 2

2

首先你需要知道 N1QL 参数化查询是如何工作的,查询应该被传递为:

String query=  select * from bucketName where _class=$_class and segmentId=$segmentId LIMIT $limit ;

现在查询应该被传递为:

N1QL.parameterized(query,jsonObject,N1qlParams.build().adhoc(false));

wherejsonObject将包含所有占位符值。

JsonObject jsonObject=JsonObject.create().put("_class","com.entity,user").put("segmentId","12345").put("limit",100);

N1qlParams.build().adhoc(false)是可选的,因为如果您希望优化查询,它将使用它。它利用 LRU 跟踪先前输入的查询并记录它,以便下次它不需要解析查询并从以前的我们称之为准备好的语句中获取它。

唯一的问题是 couchbase 只保留最后 5000 个查询的记录。

于 2017-01-21T04:24:13.667 回答
1

您的问题是由于您有一个带有 'where' 子句的索引,WHERE ( _class = "path/MyClass")同时,您_class在查询中将 ' 作为参数传递。

因此,分析参数化查询的查询优化器不知道该查询可能使用为 创建的索引_class = "path/MyClass",因为它_class = $class位于选择的位置。很简单,对吧?

因此,不要将索引的“位置”中提到的任何字段作为选择参数传递。相反,_class = "path/MyClass"在您的选择中以与create index. 一切都应该没问题。

这是 couchbase 问题跟踪系统中关于该问题的票证。

https://issues.couchbase.com/browse/MB-22185?jql=text%20~%20%22parameters%20does%20not%20use%20index%22

于 2017-09-22T19:32:09.807 回答