В моем приложении у меня есть концепция «сезонов», которые дискретно меняются с течением времени. Все сущности связаны с каким-то временем года. Все объекты имеют сезонные индексы, а также некоторые индексы других полей. Когда происходит смена сезона, postgresql решает использовать фильтрованный план сканирования на основе индекса сезона, а не более конкретных индексов полей. В начале сезона стоимость планирования такого решения очень мала, так что все в порядке, но проблема в том, что смена сезона приводит к тому, что МНОГИЕ пользователи приходят в самом начале сезона, поэтому план запросов на основе сканирования postgresql очень быстро становится плохим. - он просто сканирует все сущности в новом сезоне и фильтрует целевые предметы. После первого автоматического анализа postgres решает использовать хороший план, НО автоматический анализ работает ОЧЕНЬ МЕДЛЕННО из-за разногласий, и я полагаю, что это похоже на снежный ком - чем больше запросов выполняется, тем больше разногласий происходит из-за плохого плана, и поэтому автоматический анализ работает медленно и медленно. Максимальное время работы автоматического анализа составляло около часа на прошлой неделе, и это становится настоящей проблемой. Я знаю, что архитекторы postgresql решили отключить возможность выбора индекса, используемого в запросе, но как тогда лучше всего решить мою проблему?
Просто чтобы уточнить, вот DDL, один из «медленных» запросов и объяснение результатов до и после автоматического анализа.
DDL
CREATE TABLE race_results (
id INTEGER PRIMARY KEY NOT NULL DEFAULT nextval('race_results_id_seq'::regclass),
user_id INTEGER NOT NULL,
opponent_id INTEGER,
season_id INTEGER NOT NULL,
type RACE_TYPE NOT NULL DEFAULT 'battle'::race_type,
elo_delta INTEGER NOT NULL,
opponent_elo_delta INTEGER NOT NULL DEFAULT 0,
);
CREATE INDEX race_results_type_user_id_index ON race_results USING BTREE (season_id, type, user_id);
CREATE INDEX race_results_type_opponent_id_index ON race_results USING BTREE (season_id, type, opponent_id);
CREATE INDEX race_results_opponent_id_index ON race_results USING BTREE (opponent_id);
CREATE INDEX race_results_user_id_index ON race_results USING BTREE (user_id);
Запрос
SELECT 1000 + COALESCE(SUM(CASE WHEN user_id = 6446 THEN elo_delta ELSE opponent_elo_delta END), 0)
FROM race_results
WHERE type = 'battle' :: race_type AND (user_id = 6446 OR opponent_id = 6446) AND
season_id = current_season_id()
Результаты объяснения перед автоматическим анализом (как вы видите, более тысячи элементов уже удалены фильтром, и скоро их становится сотни тысяч для каждого запроса)
Результаты анализа объяснения после автоматического анализа (теперь postgres решает использовать правильный индекс и фильтрация больше не требуется, но проблема в том, что автоматический анализ занимает слишком много времени, отчасти из-за неэффективного выбора индекса на предыдущем изображении) а>
ps: Сейчас решаю проблему, просто отключив сервер приложений через 10 секунд после смены сезона, чтобы постгрес получил новые данные и начал автоанализ, а затем включит его, когда автоанализ закончится, но такое решение связано с простоем, который не является желательно и в целом выглядит странно
race_results_type_user_id_index
- что в этом плохого? План выполнения на второй картинке на самом деле кажется менее эффективным. - person a_horse_with_no_name   schedule 19.12.2016explain (analyze)
, а не толькоexplain
) для медленного запроса, а не для быстрого запроса. - person a_horse_with_no_name   schedule 19.12.2016season_id = current_season_id()
Проблема в том, что планировщик не может угадать значение функции и, следовательно, не может использовать статистику дляseason_id
(который, вероятно, в любом случае является столбцом с низкой кардинальностью). Я предлагаю вам удалить функцию и вместо этого использовать подготовленный запрос. И, возможно, добавить составной индекс, включающийseason_id
(при втором чтении я вижу, что он у вас уже есть) - person joop   schedule 19.12.2016language sql
? Волативность это что:stable
? - person Jasen   schedule 29.12.2016