Имя: Пароль:
IT
Админ
как ускорить запрос
,
0 GANR
 
31.08.23
12:56
Всем добрый день. Как построить индекс, чтобы запрос работал быстрее??

-- установил расширения
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS btree_gin;
-- построил составной gin индекс
DROP INDEX IF EXISTS idx_gin_svjul_ltrim_inn_id;
CREATE INDEX IF NOT EXISTS idx_gin_svjul_ltrim_inn_id ON svjul USING gin (ltrim(data ->> 'INN') gin_trgm_ops, id);

-- запрос к таблице (размер более 100 Гб)
SELECT * FROM svjul svjul1 WHERE ltrim(svjul1.data ->> 'INN') ilike '7839074221%'
ORDER BY svjul1.id ASC LIMIT 20 OFFSET 0;

-- ожидаемый результат: select выше работает быстро
-- фактический результат: select выше работает мелденно и в плане запроса не видно срабатывания индекса
1 Волшебник
 
31.08.23
13:00
уберите звёздочку
уберите ltrim
Вытащите INN в отдельное поле таблицы
2 GANR
 
31.08.23
13:01
(1) Ни запрос, ни структуру таблицы менять нельзя. В этом то и прикол.
3 asady
 
31.08.23
13:18
В SELECT * FROM svjul svjul1

svjul и svjul1 = это две таблицы
или svjul1 - алиас таблицы svjul ?
4 stopa85
 
31.08.23
13:20
(0) хоть бы сказал, что за СУБД.
5 Волшебник
 
31.08.23
13:22
(3) очевидно да
(4) PostgreSQL, судя по префиксу "pg" и попытке индексировать json-поле
6 stopa85
 
31.08.23
13:25
(5) тогда где комментарий про VACUUM FULL?

(0) Нужен explain без этого никак
7 GANR
 
31.08.23
13:35
(3) (4) подтверждаю слова (5)
8 GANR
 
31.08.23
13:38
(6) появилось предположение, что постгрес при сортировке по id решает использовать первичный ключ вместо индекса (коим id является)

Index Scan using svjul_pkey on svjul as svjul
Filter: (ltrim((data ->> 'INN'::text)) ~~* '7839074221%'::text)

сейчас попробую изменить order by, может быть подмена первичного ключа спасет отца русской демократи
9 GANR
 
31.08.23
13:40
(6) (5) я в (8) был прав, стоило мне заменить в секции ORDER BY id на другое поле и запрос заработал быстро!!

SELECT * FROM svjul WHERE ltrim(data ->> 'INN') ilike '7839074221%'
ORDER BY created ASC LIMIT 20 OFFSET 0;
10 stopa85
 
31.08.23
13:51
А какое количество строк он возвращает и ожидает вернуть, где оценки?
11 GANR
 
31.08.23
13:56
(10) По одному ИНН 1-3 записи, LIMIT 20 стоит ограничитель, вот только без составного индекса этот ограничитель будет работать медленно. Видимо придется добавить колонку id1, которую сделать первичным ключом, а по id построить просто индекс. Не знаю прокатит ли такое.
12 stopa85
 
31.08.23
14:34
(10) У вас какой-то кастрированный explain.

Вот у меня какой (из заббикса табличка)

explain select * from history where itemid = 42259 order by clock limit 20;
                                        QUERY PLAN
-------------------------------------------------------------------------------------------
Limit  (cost=0.43..73.34 rows=20 width=24)
   ->  Index Scan using history_pkey on history  (cost=0.43..53292.28 rows=14620 width=24)
         Index Cond: (itemid = 42259)
(3 rows)
13 GANR
 
31.08.23
14:54
(12) суть проста - scan using svjul_pkey вместо нужного мне индекса вот и всё
14 H A D G E H O G s
 
31.08.23
15:14
Нихера не понятно, но очень интересно.
Руки бы оторвать этим любителям сокращений.

select * - вот причина.
Поменяй на select INN
15 GANR
 
31.08.23
15:21
(14) это мимо
16 GANR
 
31.08.23
15:22
Ответ в (9). Другой вопрос как элегантнее решить проблему
17 H A D G E H O G s
 
31.08.23
15:29
(15) Как скажешь
18 H A D G E H O G s
 
31.08.23
15:33
Если считаешь, что твоя сортировка волшебным образом заставляет использоваиь некластерный индекс - ну так выбери во временную таблицу, а потом досортируй как надо.
19 H A D G E H O G s
 
31.08.23
15:37
Скорее всего ты просто прогрел кэш и запрос все также шарашит по кластерному индексу, ведь ему нужны остальные поля для select *, которых нет в некластерном и за которыми надо сходить по ridlookup. Да, limit скорее всего позволит это сделать только не более 20 раз, но так ли умен оптимизатор pg.
20 GANR
 
31.08.23
15:37
(18) запрос менять нельзя в том то и проблема
21 H A D G E H O G s
 
31.08.23
15:40
(20) конкурсы все интереснее. Ну ты план запроса то хоть собери для
1) ORDER BY created
2) Select inn

Чтобы понимать картину
22 H A D G E H O G s
 
31.08.23
15:41
(20) добавь в твой индекс все поля запроса по include
23 H A D G E H O G s
 
31.08.23
15:41
**все поля таблицы
24 Garykom
 
гуру
31.08.23
16:30
Каким местом извлечение из jsonb в текст, затем ltrim а затем еще и ilike
Может работать быстро?
25 Kesim
 
31.08.23
16:36
(0) проблема в том что не по-русски написано)))
26 GANR
 
31.08.23
17:36
(24) Может, это ведь GIN индекс как раз для ilike. Ну а если бы ilike не было то и btree сгодился бы.
27 Garykom
 
гуру
31.08.23
17:43
>CREATE INDEX IF NOT EXISTS idx_gin_svjul_ltrim_inn_id ON svjul USING gin (ltrim(data ->> 'INN') gin_trgm_ops, id);

не нравится мне что индекс по вычисляемому полю

как sql поймет что один ltrim(data ->> 'INN' равен другому ltrim(data ->> 'INN' да еще для ilike ?
28 Garykom
 
гуру
31.08.23
17:45
(27)+ имхо делай ты отдельную табличку с INN и используй ее
29 GANR
 
31.08.23
17:52
(27) Легко понимает, если я меняю первичный ключ с id на другую колонку. И запрос из 0 работает быстро. Вот только теперь выяснено, что приложение каким то боком подхватывает новый первичный ключ строит по нему другой order by и снова получается зависание. Получается замена первичного ключа не катит. Как-то статистику покрутить может можно??
30 GANR
 
31.08.23
17:52
(29) к (28)
31 GANR
 
31.08.23
17:55
(28) к сожалению запрос формирую не я, а стороннее приложение... так бы давно перекроил запрос и забыл как страшный сон
32 Garykom
 
гуру
31.08.23
17:55
а если убрать id?

CREATE INDEX IF NOT EXISTS idx_gin_svjul_ltrim_inn_id ON svjul USING gin (ltrim(data ->> 'INN') gin_trgm_ops);
33 Garykom
 
гуру
31.08.23
18:04
(32)+ гм не понял а gin_trgm_ops это что?
34 Garykom
 
гуру
31.08.23
18:38
(33)+ понял указание на триграмный
35 GANR
 
31.08.23
22:41
(32) тогда он сможет ускорить запрос без order by id - не катит
(34) полнотекстовый мудреж
36 GANR
 
31.08.23
22:41
(35) опечатка, наоборот НЕ сможет
37 GANR
 
31.08.23
22:43
Из за order by id
38 Garykom
 
гуру
31.08.23
23:10
сделай view
39 Djelf
 
01.09.23
07:47
А может попробовать убрать "USING gin"?
Вроде этот индекс не так работает, как хотелось бы (0) https://postgrespro.ru/docs/postgrespro/9.5/datatype-json#json-indexing
40 GANR
 
01.09.23
11:33
(38) не пойдет - приложуха с view работать не сможет
(39) не пойдет - кроме gin с ilike не работет ничего

Как ни странно, я добавил колонку id1, перекинул первичный ключ с id на id1, затем перекинул ключ обратно на id и удалил id1 - в итоге запрос начал подхватывать мой индекс gin вместо первичного ключа, каким образом мои манипуляции могли повлиять знает кто-нибудь?
41 Djelf
 
01.09.23
11:51
(40) Хм, проверил онлайн запросом, ilike сработал без gin.
Откуда сведения? В документации нет таких ограничений https://postgrespro.ru/docs/postgresql/9.6/functions-matching
42 GANR
 
01.09.23
12:05
(41) он сработал, но только на больших объемах то медленно будет, в нашем случае это = не работает
43 GANR
 
01.09.23
14:27
Удалось добиться ускорения поиска по ИНН вот такими заклинаниями:
CREATE INDEX IF NOT EXISTS idx_gin_svjul_ltrim_id_inn ON svjul USING gin (id, ltrim(data ->> 'INN') gin_trgm_ops);
ALTER INDEX idx_gin_svjul_ltrim_id_inn ALTER COLUMN 2 SET STATISTICS 1000;
ANALYZE svjul;
44 GANR
 
01.09.23
14:29
(38) (39) (21) (10) (1) благодарю за содействие. Хотелось бы поподробнее до каких пор эта штука у меня проработать может ещё и если недолго то как продлить?
45 ansh15
 
01.09.23
16:57
(44) Многопоточность как-нибудь применяется? Скажем, max_parallel_workers_per_gather=6
и в плане запроса
Workers Planned: 6
   Workers Launched: 6
   ->  Parallel Seq Scan и т.д.
46 GANR
 
01.09.23
17:57
(45) Даже и не знаю. А как это на план запроса влияет?
47 ansh15
 
01.09.23
19:05
(46) Здесь есть примеры https://habr.com/ru/articles/305662/
Статья старенькая, но все же..