From c97dd57e1c231a4ed540b7c82029c548a9818564 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sun, 28 Jun 2026 14:39:05 +0000 Subject: [PATCH] =?UTF-8?q?perf(objective=5Flots):=20drop=203=20dead=20ind?= =?UTF-8?q?exes=20(status/rooms=5Farea/bank)=20=E2=80=94=20write-amp=20(#1?= =?UTF-8?q?953)=20(#2056)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../177_drop_dead_objective_lots_indexes.sql | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 data/sql/177_drop_dead_objective_lots_indexes.sql diff --git a/data/sql/177_drop_dead_objective_lots_indexes.sql b/data/sql/177_drop_dead_objective_lots_indexes.sql new file mode 100644 index 00000000..d5c7796a --- /dev/null +++ b/data/sql/177_drop_dead_objective_lots_indexes.sql @@ -0,0 +1,65 @@ +-- Контекст: perf-hunt finding #4 (follow-up эпика #1953) — срезаем +-- write-amplification на горячей UPSERT-таблице objective_lots. +-- +-- objective_lots — append/UPSERT-per-snapshot, очень write-heavy: за 35 дней +-- аптайма (pg_stat без reset, prod 2026-06-28) n_tup_ins=1.76M, n_tup_upd=1.39M. +-- Каждый INSERT/UPDATE обязан поддерживать ВСЕ индексы таблицы → мёртвый индекс = +-- чистый write-amp + место на диске + WAL, ноль пользы для чтения. +-- +-- Дропаем 3 индекса, у которых pg_stat_user_indexes.idx_scan почти нулевой за весь +-- аптайм И в кодовой базе (backend services/api/workers + data/sql views) НЕТ ни +-- одного sargable-запроса, который мог бы их использовать. По каждому проверено +-- EXPLAIN на prod: планировщик берёт Seq Scan на реальной форме запроса даже при +-- наличии индекса (доказывает, что индекс не служит ничему). +-- +-- ── DROP 1: objective_lots_status_idx ── (idx_scan=8 за 35д, 19 MB) +-- Опр.: ON objective_lots(status) WHERE status IS NOT NULL (68_schema_objective.sql:250) +-- Единственные потребители status: market_metrics._STOCK_SQL, +-- supply_layers, competitors, special_indices — ВСЕ через +-- `LOWER(COALESCE(ol.status,'')) = 'продан'`. Обёртка LOWER(COALESCE(...)) +-- НЕ sargable против plain B-tree на status → индекс не используется НИКОГДА. +-- Bare `status = :x` / `status IN (...)` в коде НЕ встречается (grep пуст). +-- Колонка status уже в payload covering-индекса (176_*, INCLUDE) — для проекции +-- отдельный индекс тоже не нужен. Триггер истории (166_*) читает NEW.status / +-- prev.status построчно (без индекса). +-- EXPLAIN prod: `WHERE LOWER(COALESCE(status,''))='продан'` → Parallel Seq Scan. +-- +-- ── DROP 2: objective_lots_rooms_area_idx ── (idx_scan=7 за 35д, 78 MB) +-- Опр.: ON objective_lots(rooms_int, area_pd) WHERE area_pd IS NOT NULL (:252) +-- rooms_int / area_pd встречаются только в SELECT-проекциях и внутри CASE-bucket +-- выражений (market_metrics, sales_series, analytics) — НЕ как WHERE/ORDER/JOIN +-- предикат. Room-bucket фильтр оборачивает обе колонки в CASE → не sargable. +-- Никакого `WHERE rooms_int = / IN` или `area_pd >/