首页 文章

Couchbase使用错误的索引和N1QL参数化查询

提问于
浏览
4

我有理解沙发基础查询计划的工作方式的问题 . 我将SpringData与Couchbase 4.1一起使用,并且我提供了Couchbase Repository的自定义实现 . 在我的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查询优化器的正确和合法行为?这是否意味着查询计划没有考虑参数的实际值?我是否手动将值放入查询字符串或存在eny其他方式使用N1Ql参数化查询和正确的索引选择?

EDIT

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

2 回答

  • 1

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

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

    因此,请选择't pass any fields mentioned in your index' s 'where'作为选择参数 . 相反,在您的选择中硬编码 _class = "path/MyClass" 的方式与 create index 相同 . 一切都应该没问题 .

    这是沙发基线问题跟踪系统中关于该问题的票据 .

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

  • 2

    首先,您需要知道N1QL参数化查询的工作原理查询应该如下传递:

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

    现在查询应该传递为:

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

    其中 jsonObject 将包含所有占位符值 .

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

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

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

相关问题