Postgresql ORDER BY - 选择正确的索引
Posted
技术标签:
【中文标题】Postgresql ORDER BY - 选择正确的索引【英文标题】:Postgresql ORDER BY - choosing right index 【发布时间】:2014-11-25 08:01:04 【问题描述】:有一个表 T(user, timestamp,...)
有 100 毫升以上的记录 (PostgreSQL 9.1)。
形式的查询
SELECT *
FROM T
WHERE user='abcd'
ORDER BY timestamp
LIMIT 1
当有大约 100000 条用户记录时,正在使用 timestamp
索引而不是用户索引。
使用时间戳索引总是会产生糟糕的结果(20+ 秒),因为它最终会扫描所有记录。通过将查询更改为使用ORDER BY DATE(timestamp)
来绕过timestamp
索引将导致查询求助于用户索引并给出小于100 毫秒的结果。
为什么 postgresql 忽略 user
索引而使用 timestamp
索引(时间戳索引需要查看所有记录)?
是否有任何 postgresql 配置参数可以更改以使查询使用用户名索引本身?
【问题讨论】:
wiki.postgresql.org/wiki/SlowQueryQuestions 发布EXPLAIN
结果
最可能的问题 - 表上的统计信息陈旧或配置错误。
这是一个有效的问题,请不要关闭它!
@vyegorov 我不能代表另一个关闭投票,但我的投票是迁移到 dba.stackexchange.com,而不是关闭问题
【参考方案1】:
好问题,我前段时间就遇到过这个问题。
为什么会这样?
您应该像这样查看stats 中user='abcd'
值的数量:
SELECT attname, null_frac, ag_width, n_distinct,
most_common_vals, most_common_freqs, histogram_bounds
FROM pg_stats
WHERE table_name='T';
我的猜测是——这个值经常出现,你会在most_common_vals
输出中找到它。
从most_common_freqs
中选取相同的元素,您将获得该值的比率,将其乘以总行数(可以从pg_class
获得)以获得估计的行数拥有'abcd'
值。
Planner 假定所有值都具有线性分布。事实上,事情当然是不同的。 另外,目前没有correlated stats (although some work is being done in this direction)。
所以,让我们取user='abcd'
值,在对应的most_common_freqs
条目中具有0.001
比率(每个问题)。这意味着值将每 1000 行出现一次(假设线性分布)。看来,如果我们以任何方式扫描表,我们将在大约 1000 行中找到我们的user='abcd'
。听起来应该很快!规划师“认为”相同,并在 timestamp
列上选择索引。
但事实并非如此。如果我们假设您的表T
包含用户活动日志,并且user='abcd'
过去3 周都在休假,那么这意味着我们必须从timestamp
中读取很多行索引(价值 3 周的数据)在我们实际到达我们想要的行之前。好吧,您作为 DBA 知道这一点,但规划器假定线性分布。
那么,如何解决?
你必须欺骗计划者使用你需要的东西,因为你对你的数据有更多的了解。
将OFFSET 0
trick 与子查询一起使用:
SELECT *
FROM
(
SELECT * FROM T WHERE user='abcd' OFFSET 0
)
ORDER BY timestamp
LIMIT 1;
这个技巧可以保护查询免于内联,因此内部部分是自己执行的。
使用CTE
(命名子查询):
WITH s AS (
SELECT * FROM T WHERE user='abcd'
)
SELECT *
FROM s
ORDER BY timestamp
LIMIT 1;
根据文档:
WITH 查询的一个有用特性是,每次执行父查询时,它们只被评估一次,即使它们被父查询或同级 WITH 查询多次引用。 p>
使用count(*)
进行聚合查询:
SELECT min(session_id), count(*) -- instead of simply `min(session_id)`
FROM T
WHERE user='abcd'
ORDER BY timestamp
LIMIT 1;
这个不太适用,但我想提一下。
请考虑升级到 9.3。
附:更多关于行 estiamtes in the docs of course.
【讨论】:
谢谢 vyegorov。这完美地解释了这种行为。我通过检查 pg_stats 确认。 9.3 有什么改进吗? @Anoop,不适用于这种情况,抱歉。但坚持当前的主要版本通常是个好主意,因为它包括其他性能和安全改进。以上是关于Postgresql ORDER BY - 选择正确的索引的主要内容,如果未能解决你的问题,请参考以下文章
order by 在 POSTGRESQL 中的 partition by 子句中不起作用?