ускорение sqlite-запроса с присоединением к виртуальной таблице - PullRequest
0 голосов
/ 06 марта 2020

У меня есть две таблицы, t1 и виртуальная таблица FTS5 vt1 с содержанием t1

sqlite> EXPLAIN QUERY PLAN
   ...> SELECT Count(*) as num FROM t1 WHERE deleted = 0;
QUERY PLAN
--SEARCH TABLE t1 USING COVERING INDEX ix_t1_t1Id (deleted=?)
sqlite> SELECT Count(*) as num FROM t1 WHERE deleted = 0;
308498
Run Time: real 0.043 user 0.023668 sys 0.009005

Как видно выше, фактический запрос занимает ~ 43 мс

sqlite> EXPLAIN QUERY PLAN
   ...> SELECT Count(*) as num FROM vt1 WHERE vt1 MATCH 'foo';
QUERY PLAN
--SCAN TABLE vt1 VIRTUAL TABLE INDEX 131073:
sqlite> SELECT Count(*) as num FROM vt1 WHERE vt1 MATCH 'foo';
80789
Run Time: real 0.047 user 0.008021 sys 0.009640

Фактический запрос занимает ~ 47 мс. Все идет нормально. Но проблема возникает, когда я соединяю две таблицы

sqlite> EXPLAIN QUERY PLAN
   ...> SELECT Count(*) as num
   ...> FROM t1 JOIN vt1 ON t1.t1Id = vt1.t1Id
   ...> WHERE t1.deleted = 0 AND vt1 MATCH 'foo';
QUERY PLAN
|--SCAN TABLE vt1 VIRTUAL TABLE INDEX 0:m
 --SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (t1Id=?)
sqlite> SELECT Count(*) as num
   ...> FROM t1 JOIN vt1 ON t1.t1Id = vt1.t1Id
   ...> WHERE t1.deleted = 0 AND vt1 MATCH 'foo';
80789
Run Time: real 26.218 user 1.396376 sys 5.413630

Ответ правильный, но запрос занимает больше 26 секунд! Конечно, я хотел бы ускорить этот запрос на несколько порядков, но я также хотел бы понять, почему это объединение вызывает замедление.

update: так, после многих ударившись головой о стену sql, я придумал следующее - как отмечалось выше, у меня действительно есть два различных набора запросов, которые я могу сделать отдельно, например, так:

Q1: (ВЫБЕРИТЕ t1Id ОТ t1 ГДЕ…) КАК

Q2: (ВЫБЕРИТЕ t1Id ОТ vt1 ГДЕ vt1 СООТВЕТСТВУЕТ 'bar') КАК b

Затем я могу сделать следующее -

ВЫБРАТЬ СЧЕТ ( *) ОТ ГДЕ a.t1Id IN b

Конечно, на самом деле, я делаю это не отдельно, а в одном go, чтобы сделать действительно грязный SQL, но очень быстрый запрос, пару сотен мс в отличие от> 25 с

Вы можете заметить, что в моем Q2 выше я соответствовал 'bar' вместо 'foo'. Это потому, что 'bar' возвращает меньше строк, чем 'foo'. Проблема остается, когда в запросе FTS слишком много совпадений, и в этом случае сам запрос FTS является медленным, например, с 'foo', который соответствует> 80К строк.

Теперь одна интересная точка сравнения - запрос того же типа (с точки зрения пользователя) к экземпляру ElasticSearch (то есть всем строкам с 'foo' в любом месте в тексте) очень быстрый, порядка нескольких сотен мс. Я понимаю, что сравнивать SQLite с ElasticSearch может быть нечестно, но все же. (Или это справедливое сравнение?)

1 Ответ

0 голосов
/ 07 марта 2020

Одним из способов ускорить вызов может быть два обмена порядка таблицы в вашем соединении.

ОТ vt1 СОЕДИНЕНИЕ t1 ВКЛ t1.t1Id = vt1.t1Id

Из документации SQLite :

Порядок вложенных циклов по умолчанию в объединении по умолчанию для самой левой таблицы в предложении FROM для формирования внешней l oop и самой правой таблицы для формирования внутренней l oop

Еще одна вещь, которую стоит отметить, состоит в том, что объединение двух таблиц приводит к вложенному l oop. Ваши две таблицы кажутся довольно большими. Match также выполняет полнотекстовый поиск. Вы должны увидеть, можете ли вы использовать команду «LIKE» для одного или нескольких столбцов, чтобы получить тот же результат.

@ Ge3ng также говорит, что когда вы объединяетесь с таблицей FTS, sqlite больше не может переставлять ваш запрос для оптимизация. Вы также можете попробовать добавить MATCH к самому оператору соединения.

FROM vt1 JOIN t1 ON (t1.t1Id = vt1.t1Id AND vt1 MATCH 'foo')

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...