【发布时间】:2016-12-18 20:44:20
【问题描述】:
在我的应用程序中,我有一个“季节”的概念,它会随着时间而离散地变化。所有实体都与某个季节有关。所有实体都有基于季节的指数以及其他领域的一些指数。当季节变化发生时,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 秒后关闭应用程序服务器,以便 postgres 获取新数据并启动自动分析,然后在自动分析完成时将其打开,但这样的解决方案涉及停机时间,这是不可取的,总的来说它看起来很奇怪
【问题讨论】:
-
将 DDL 和查询添加到您的问题会有所帮助。
-
@wildplasser 添加了更多信息
-
如果您想知道为什么查询很慢,请向我们展示 slow 查询的执行计划(使用
explain (analyze)生成 - 而不仅仅是explain),而不是用于快速查询。 -
season_id = current_season_id()问题是规划器无法猜测函数值,因此无法使用season_id的统计信息(无论如何这可能是一个低基数列)我建议您删除该函数并使用而是准备好的查询。并且可能添加一个涉及season_id的复合索引(在二读时,我看到你已经有了一个)
标签: postgresql indexing postgresql-performance