Второй по тяжести запрос — поиск товаров по тексту. «МаркетПро» использовали LIKE-запросы, которые не могут использовать обычные B-tree индексы:
-- Проблемный запрос поиска: 6200 мс
SELECT id, name, price
FROM products
WHERE name ILIKE '%ноутбук%'
OR description ILIKE '%ноутбук%';
-- Seq Scan: полный перебор 2.4 млн строк каждый раз
Мы внедрили полноценный полнотекстовый поиск PostgreSQL с GIN-индексом. GIN (Generalized Inverted Index) — специализированный тип индекса для составных значений, идеальный для полнотекстового поиска, массивов и JSONB:
-- 1. Добавляем колонку для поискового вектора
ALTER TABLE products ADD COLUMN search_vector tsvector;
-- 2. Заполняем вектор
UPDATE products SET search_vector =
setweight(to_tsvector('russian', coalesce(name, '')), 'A') ||
setweight(to_tsvector('russian', coalesce(description, '')), 'B');
-- 3. Создаём GIN-индекс
CREATE INDEX CONCURRENTLY idx_products_search
ON products USING gin(search_vector);
-- 4. Триггер для автоматического обновления
CREATE FUNCTION products_search_trigger() RETURNS trigger AS $$
BEGIN
NEW.search_vector :=
setweight(to_tsvector('russian', coalesce(NEW.name, '')), 'A') ||
setweight(to_tsvector('russian', coalesce(NEW.description, '')), 'B');
RETURN NEW;
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_products_search
BEFORE INSERT OR UPDATE ON products
FOR EACH ROW EXECUTE FUNCTION products_search_trigger();
-- 5. Новый запрос поиска: 12 мс
SELECT id, name, price,
ts_rank(search_vector, query) AS rank
FROM products, to_tsquery('russian', 'ноутбук') query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT 50;
Результат: ускорение с 6200 мс до 12 мс — в 516 раз. При этом поиск стал релевантнее за счёт весов (weight A для названия, weight B для описания) и ранжирования результатов.
Оставить комментарий