Couchbase использует неправильные индексы с параметризованными запросами N1QL

У меня проблемы с пониманием того, как работает план запроса 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 создает следующий объект json, представляющий запрос к Couchbase:

{
    "$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 я просто заменяю ссылку на параметры точными значениями.

После добавления предложения EXPLAIN перед оператором select я упомянул разные планы выполнения. Выполнение этого запроса как параметризованного запроса через Java Spring Data или N1QL Rest Api. Я уже упоминал, что запрос не использует индекс, который я создал именно для этого случая. Определение индекса можно найти ниже:

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

Итак, когда я выполняю запрос через консоль cbq, Couchbase использует мой idnex, и производительность запросов очень хорошая. Но когда я выполняю этот запрос через N1QL rest api или Java, я вижу, что этот запрос не использует мой индекс. Ниже вы можете найти часть плана выполнения, подтверждающую этот факт:

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

Итак, вопрос в том, что правильное и законное поведение оптимизатора запросов couchbase? Значит ли это, что план запроса не учитывает реальные значения параметров? И вводил ли я вручную значения в строку запроса или существовал ли другой способ использовать параметризованный запрос N1Ql с правильным выбором индекса?

ИЗМЕНИТЬ

Согласно ответу шаши радж, я добавляю параметр N1qlParams.build (). Adhoc (false) в параметризованный запрос N1QL. Это не решает мою проблему, потому что у меня все еще есть проблемы с производительностью с этим запросом. Более того, когда я печатаю запрос, я вижу, что он такой же, как я описал ранее. Итак, мой запрос все еще неправильно проанализирован и вызывает снижение производительности.


person 4the3eam    schedule 18.01.2017    source источник
comment
Можете ли вы попробовать Couchbase 4.5.1, чтобы узнать, исправлено ли это.   -  person geraldss    schedule 19.01.2017
comment
Не-а, не исправлено даже в 4.6   -  person gaperton    schedule 22.09.2017
comment
И, судя по всему, поправить нечего :). Смотрите мой ответ ниже.   -  person gaperton    schedule 22.09.2017


Ответы (2)


Проблема в вашем случае вызвана тем, что у вас есть индекс с предложением 'where' WHERE ( _class = "path/MyClass"), и в то же время вы передаете _class в качестве параметра в своем запросе.

Таким образом, оптимизатор запросов, анализирующий параметризованный запрос, не подозревает, что этот запрос может использовать индекс, созданный для _class = "path/MyClass", потому что он _class = $class в select's where. Довольно просто, правда?

Итак, не передавайте поля, упомянутые в индексе «где», в качестве параметров выбора. Вместо этого жестко закодируйте _class = "path/MyClass" в своем выборе так же, как вы делали для create index. И все должно быть хорошо.

Вот билет в системе отслеживания проблем с диванами по этому поводу.

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

person gaperton    schedule 22.09.2017

Прежде всего вам нужно знать, как работают параметризованные запросы 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 запросов.

person shashi raj    schedule 21.01.2017
comment
Извините, но это не решило мою проблему. У меня все еще есть проблемы с производительностью, даже если я создам запрос с этим флагом, установленным на false. Может быть, у Вас есть другие предложения? - person 4the3eam; 26.01.2017
comment
Попробуйте использовать покрывающие индексы, они решают большинство проблем с производительностью. Предположим, если вы хотите запросить с использованием 5 атрибутов, укажите эти атрибуты при создании индекса. - person shashi raj; 26.01.2017
comment
Убедитесь, что отображение переменной в объекте запроса n Json должно быть таким же. Иначе ничего не получится. - person shashi raj; 26.01.2017
comment
Я использую покрывающие индексы и даже создал индекс для функции ARRAY_COUNT (myDatabase.Array). Кроме того, когда я вручную передаю значения параметров в запрос, я вижу, что анализатор запросов использует правильный индекс. - person 4the3eam; 28.01.2017
comment
И когда я добавил N1QLParams.build (). Adhoc (false) в конструктор запросов, я увидел тот же запрос. Я думал, что когда я добавлю этот параметр в построитель запросов, Java автоматически передаст значения этих параметров в предложение оператора в json-объекте запроса N1QL. Поскольку, как я понимаю, за процессы оптимизации запросов отвечает внутренний движок Couchbase, а не Java, поэтому я предположил, что построитель запросов должен передавать значения в строку запроса. - person 4the3eam; 28.01.2017