使用 LIMIT 1 索引 ORDER BY
Posted
技术标签:
【中文标题】使用 LIMIT 1 索引 ORDER BY【英文标题】:Indexed ORDER BY with LIMIT 1 【发布时间】:2013-09-20 22:42:31 【问题描述】:我正在尝试获取表中的最新行。我有一个简单的时间戳created_at
,它已被索引。当我查询ORDER BY created_at DESC LIMIT 1
时,它所花费的时间远远超出我的想象(在我的机器上,36k 行大约需要 50 毫秒)。
EXPLAIN-ing 声称它使用 反向索引扫描,但我确认将索引更改为 (created_at DESC)
不会改变查询计划器中的成本简单的索引扫描。
如何优化这个用例?
运行 postgresql 9.2.4
。
编辑:
# EXPLAIN SELECT * FROM articles ORDER BY created_at DESC LIMIT 1;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Limit (cost=0.00..0.58 rows=1 width=1752)
-> Index Scan Backward using index_articles_on_created_at on articles (cost=0.00..20667.37 rows=35696 width=1752)
(2 rows)
【问题讨论】:
能否将 EXPLAIN 的结果添加到问题中? 【参考方案1】:假设我们正在处理一个大桌子,partial index 可能会有所帮助:
CREATE INDEX tbl_created_recently_idx ON tbl (created_at DESC)
WHERE created_at > '2013-09-15 0:0'::timestamp;
正如您已经发现的那样:下降或上升在这里几乎无关紧要。 Postgres 可以以几乎相同的速度向后扫描(例外情况适用于多列索引)。
查询使用该索引:
SELECT * FROM tbl
WHERE created_at > '2013-09-15 0:0'::timestamp -- matches index
ORDER BY created_at DESC
LIMIT 1;
这里的重点是让索引小很多,这样应该更容易缓存和维护。
-
您需要选择一个保证小于最新时间戳的时间戳。
您应该不时重新创建索引以切断旧数据。
条件必须是
IMMUTABLE
。
因此,一次性效果会随着时间的推移而恶化。 具体问题是硬编码条件:
WHERE created_at > '2013-09-15 0:0'::timestamp
自动化
您可以不时手动更新索引和查询。或者,您可以借助以下功能将其自动化:
CREATE OR REPLACE FUNCTION f_min_ts()
RETURNS timestamp LANGUAGE sql IMMUTABLE AS
$$SELECT '2013-09-15 0:0'::timestamp$$
索引:
CREATE INDEX tbl_created_recently_idx ON tbl (created_at DESC);
WHERE created_at > f_min_ts();
查询:
SELECT * FROM tbl
WHERE created_at > f_min_ts()
ORDER BY created_at DESC
LIMIT 1;
使用 cron 作业或一些基于触发器的事件自动重新创建。您的查询现在可以保持不变。但是您需要在更改此函数后以任何方式重新创建所有索引。只需放下并创建每一个。
首先..
...测试你是否真的用这个来击中瓶颈。
试试简单的DROP index ... ; CREATE index ...
是否能胜任这项工作。那么你的索引可能已经膨胀了。您的 autovacuum 设置可能已关闭。
或者尝试VACUUM FULL ANALYZE
让您的整个表格以及索引处于原始状态并再次检查。
其他选项包括usual general performance tuning 和covering indexes,具体取决于您从表中实际检索到的内容。
【讨论】:
谢谢,在发布问题之前,我已经完成了清理、重新索引、重新创建索引的步骤。这是有效的,现在我只需要记住维护它。 在创建过程中是否评估了部分索引条件?REINDEX
是否再次重新评估条件? (那太好了)
@farnoy:索引的条件必须为IMMUTABLE
。 IE。 Postgres 可以安全地假设结果永远不会改变。如果更改此类函数,则需要使用它重新创建所有索引。以上是关于使用 LIMIT 1 索引 ORDER BY的主要内容,如果未能解决你的问题,请参考以下文章
MySQL实验 内连接优化order by+limit 以及添加索引再次改进
mysql select * order by 索引 limit0,10 为啥是全表扫描
MySQL 进阶 索引 -- SQL优化(插入数据优化:导入本地文件数据主键优化order by优化group by优化limit优化count优化update优化)