Скорее всего, не будет гарантии на время ответа 2-3 секунды. По крайней мере, до тех пор, пока задействован дисковый ввод-вывод, и вы не используете последний SSD (или даже лучше: NVMe) с высоким IOPS и самой низкой задержкой. Также достаточно оперативной памяти является требованием здесь. Пожалуйста, учтите это, прежде чем принять решение о стратегии индексации.
Если ни ваши данные, ни индексы не помещаются в память, вы должны максимально уменьшить количество дисковых операций ввода-вывода для каждого запроса или позволить PostgreSQL использовать стратегии, которые помогают хотя бы уменьшить случайные операции ввода-вывода (например, для чего выполняется сканирование растрового индекса).
Текстовый поиск с использованием LIKE
в содержит подстроку , способ не будет хорошо работать на любом большом столе.
Альтернативой (будет работать, только если запросы ищут одинаковые детали в file_path
) может быть (для вашего примера поиск CTX_XXXX
):
-- create a function to extract the specific file_path substring
CREATE OR REPLACE FUNCTION get_filename_part(file_path text, idx int)
RETURNS text
LANGUAGE SQL
IMMUTABLE
AS $$
SELECT regexp_replace(file_path, '.*/(CT.{6}).*-(CT.{6}).*', E'\\' || idx);
$$;
-- create a helper function for querying...
CREATE OR REPLACE FUNCTION check_filename_parts(file_path text, c_value text)
RETURNS boolean
LANGUAGE SQL
IMMUTABLE
AS $$
SELECT get_filename_part(file_path, 1) = c_value OR get_filename_part(file_path, 2) = c_value;
$$;
-- create indexes...
CREATE INDEX idx_filename_ct_first ON text_search (get_filename_part(file_path, 1));
CREATE INDEX idx_filename_ct_second ON text_search (get_filename_part(file_path, 2));
... и используйте запрос, такой как:
SELECT *
FROM text_search
WHERE check_filename_parts(file_path, 'CT1_1111');
Поясняется с данными испытаний
Обратите внимание, что следующие тесты проводились на 8-летнем оборудовании потребительского класса (но, по крайней мере, на SSD).
Создание тестовых данных (8 000 000 строк - почти случайных):
CREATE TABLE text_search (id serial PRIMARY KEY, file_path text);
INSERT INTO text_search (file_path)
SELECT '/directory1/123456/CT' || (random() * 8 + 1)::int || '_' || (random() * 8999 + 1000)::int || '_' || (random() * 899 + 100)::int || '_' || (random() * 899 + 100)::int || '-CT' || (random() * 8 + 1)::int || '_' || (random() * 8999 + 1000)::int || '_' || (random() * 899 + 100)::int || '_' || (random() * 899 + 100)::int || '-fail.xml'
FROM generate_series(1, 8000000);
--- and analyze...
ANALYZE text_search;
... объясненный выше запрос выбора (после перезапуска сервера):
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on text_search (cost=5.49..409.93 rows=203 width=66) (actual time=0.092..0.882 rows=110 loops=1)
Recheck Cond: ((get_filename_part(file_path, 1) = 'CT1_1111'::text) OR (get_filename_part(file_path, 2) = 'CT1_1111'::text))
Heap Blocks: exact=110
Buffers: shared read=116
I/O Timings: read=0.576
-> BitmapOr (cost=5.49..5.49 rows=203 width=0) (actual time=0.071..0.072 rows=0 loops=1)
Buffers: shared read=6
I/O Timings: read=0.036
-> Bitmap Index Scan on idx_filename_ct_first (cost=0.00..2.70 rows=102 width=0) (actual time=0.038..0.038 rows=48 loops=1)
Index Cond: (get_filename_part(file_path, 1) = 'CT1_1111'::text)
Buffers: shared read=3
I/O Timings: read=0.017
-> Bitmap Index Scan on idx_filename_ct_second (cost=0.00..2.69 rows=101 width=0) (actual time=0.032..0.032 rows=62 loops=1)
Index Cond: (get_filename_part(file_path, 2) = 'CT1_1111'::text)
Buffers: shared read=3
I/O Timings: read=0.019
Planning Time: 4.996 ms
Execution Time: 0.922 ms
(18 rows)
Общий фильтр с использованием gin_trgm_ops
... по сравнению с общим запросом LIKE
с использованием индекса gin_trgm_ops
(после 3 запусков - данные в кеше):
-- create index...
CREATE INDEX idx_filename ON text_search USING gin (file_path gin_trgm_ops);
EXPLAIN (ANALYZE, BUFFERS)
SELECT *
FROM text_search
WHERE file_path LIKE '%CT1_1111%';
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on text_search (cost=94.70..1264.40 rows=800 width=66) (actual time=20.699..27.775 rows=110 loops=1)
Recheck Cond: (file_path ~~ '%CT1_1111%'::text)
Rows Removed by Index Recheck: 8207
Heap Blocks: exact=7978
Buffers: shared hit=8277
-> Bitmap Index Scan on idx_filename (cost=0.00..94.50 rows=800 width=0) (actual time=19.328..19.328 rows=8317 loops=1)
Index Cond: (file_path ~~ '%CT1_1111%'::text)
Buffers: shared hit=299
Planning Time: 0.722 ms
Execution Time: 27.912 ms
(10 rows)
TL; DR
Если возможно, вкладывать средства в небольшую инфраструктуру, чтобы добиться максимальной производительности, используя внутреннее сравнение =
. Это значительно сэкономит на вводе / выводе процессора, по сравнению с любым другим подходом. Но также следите за ухудшением производительности записи с ростом индексов. Вы можете просто прийти к компромиссу.