From 57456b5d106513da1784aaa2619b31387f0db0f1 Mon Sep 17 00:00:00 2001 From: Light1YT Date: Fri, 26 Jun 2026 22:09:01 +0500 Subject: [PATCH] perf(tradein): GIST index on (geom::geography) for estimator radius filter (#1189) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Estimator analog query casts geom::geography for ST_DWithin, but listings_geom_idx is gist(geom) → spatial predicate fell to Filter, geom index used only for geom IS NOT NULL (54.5k-row scan, 180ms). Add gist((geom::geography)) expression index → ST_DWithin uses it as Index Cond. Prod dry-run (BEGIN/CREATE/EXPLAIN/ROLLBACK): 180ms → 38ms (~4.7x). Additive, idempotent (IF NOT EXISTS), non-destructive. --- .../sql/134_listings_geom_geography_gist.sql | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tradein-mvp/backend/data/sql/134_listings_geom_geography_gist.sql diff --git a/tradein-mvp/backend/data/sql/134_listings_geom_geography_gist.sql b/tradein-mvp/backend/data/sql/134_listings_geom_geography_gist.sql new file mode 100644 index 00000000..5da8c9ec --- /dev/null +++ b/tradein-mvp/backend/data/sql/134_listings_geom_geography_gist.sql @@ -0,0 +1,36 @@ +-- 134_listings_geom_geography_gist.sql +-- GIST expression-индекс на (geom::geography) — ускоряет radius-фильтр эстиматора. +-- +-- WHY (#1189 — EXPLAIN-решение об индексе): +-- Эстиматор тянет аналоги через `ST_DWithin(geom::geography, point, radius)` +-- (Tier W/H/S radius-путь). Существующий `listings_geom_idx` = `gist(geom)` — +-- на geometry, а предикат кастует в geography, поэтому планировщик НЕ может +-- использовать его для пространственного условия: ST_DWithin падает в Filter, а +-- geom-индекс отрабатывает только `geom IS NOT NULL` (скан ~54.5k строк). +-- Прод-EXPLAIN (2026-06-26, ЕКБ-точка r=1500, 2-комн 45-65м²): +-- было — BitmapAnd(geom IS NOT NULL=54531) + Filter ST_DWithin → 180 мс, read 3961. +-- стало — Index Scan по (geom::geography) && _st_expand(point,1500) → ~38 мс +-- (~4.7× быстрее, тронуто ~5k строк вместо 54.5k). Замерено dry-run'ом +-- (BEGIN; CREATE INDEX; EXPLAIN ANALYZE; ROLLBACK) на реальном проде. +-- +-- WHAT: +-- GIST-индекс по выражению (geom::geography). Согласован с тем, как эстиматор +-- уже пишет предикат — без правок кода: тот же `ST_DWithin(geom::geography, ...)` +-- начинает использовать индекс как Index Cond. +-- +-- SAFETY: +-- - НЕ деструктивно: только добавление индекса, данные не трогаются. +-- - IF NOT EXISTS → идемпотентно при повторном прогоне (no-op). +-- - Сборка GIST по ~54.5k non-null geom строк — быстро (~1-2с), кратковременный +-- ACCESS EXCLUSIVE lock на время сборки. CONCURRENTLY не используем: раннер +-- миграций оборачивает файл в транзакцию (CREATE INDEX CONCURRENTLY в txn нельзя), +-- а на таком объёме обычная сборка дёшева. +-- - listings_geom_idx (gist(geom)) ОСТАВЛЯЕМ — он нужен другим geometry-путям. +-- +-- DEPENDENCIES: +-- - listings.geom geometry(Point,4326) -- 011_listings_alter.sql / PostGIS +-- +-- Deploy order: after 133_listings_uq_source_source_id.sql. + +CREATE INDEX IF NOT EXISTS listings_geom_geog_idx + ON listings USING gist ((geom::geography));