Postgres 仅索引扫描耗时太长

Posted

技术标签:

【中文标题】Postgres 仅索引扫描耗时太长【英文标题】:Postgres index-only scan taking too long 【发布时间】:2019-08-28 23:52:33 【问题描述】:

我有一个具有以下结构和索引的表:

     Table "public.client_data"
         Column          |  Type   | Modifiers 
-------------------------+---------+-----------
 account_id              | text    | not_null
 client_id               | text    | not null
 client_type             | text    | not null
 creation_time           | bigint  | not null
 last_modified_time      | bigint  | not null

 Indexes:
    "client_data_pkey" PRIMARY KEY, btree (account_id, client_id)
    "client_data_last_modified_time_index" btree (last_modified_time)

我需要从这个表中找到最旧的记录 - 为此我使用了以下查询:

SELECT last_modified_time FROM client_data ORDER BY last_modified_time ASC LIMIT 1;

但是,在 AWS Aurora Postgres 9.6 中的 db.r4.2xlarge RDS 实例中,此表上大约 6100 万行的查询运行速度非常慢(90-100 分钟),没有其他并发查询在运行。

但是,将查询更改为使用 DESC 会立即完成。可能是什么问题呢?我期待,因为我有一个 last_modified_time 的索引,仅查询由该列排序并应用了限制的列将涉及一个仅索引查询,该查询应在索引中的第一个条目之后停止。

这里是解释分析的输出:

EXPLAIN ANALYZE SELECT last_modified_time FROM client_data ORDER BY last_modified_time ASC LIMIT 1;

                                                                                                   QUERY PLAN                                                                                                   
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.57..2.31 rows=1 width=8) (actual time=6297823.287..6297823.287 rows=1 loops=1)
   ->  Index Only Scan using client_data_last_modified_time_index on client_data  (cost=0.57..1049731749.38 rows=606590292 width=8) (actual time=6297823.287..6297823.287 rows=1 loops=1)
         Heap Fetches: 26575013
 Planning time: 0.078 ms
 Execution time: 6297823.306 ms

DESC 版本的查询结果相同

EXPLAIN ANALYZE SELECT last_modified_time FROM client_data ORDER BY last_modified_time DESC LIMIT 1;
                                                                                                 QUERY PLAN                                                                                                  
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.57..2.32 rows=1 width=8) (actual time=2.265..2.266 rows=1 loops=1)
   ->  Index Only Scan Backward using client_data_last_modified_time_index on client_data  (cost=0.57..1066049674.69 rows=611336085 width=8) (actual time=2.264..2.264 rows=1 loops=1)
         Heap Fetches: 9
 Planning time: 0.095 ms
 Execution time: 2.278 ms

任何指针?

【问题讨论】:

看起来 postgres 经常访问堆,可能是为了检查旧行的可见性。 autovacuum 是否跟上此表中的更改? 不确定,但自动吸尘器最近运行过,可能是这样。 是否有可能有很多 first 相同的 last_modified_time(与 ASC),但只有少数 last last_modified_time(与 @987654329 @)? 【参考方案1】:

区别是这样的:

缓慢的计划有

Heap Fetches: 26575013

快速计划

Heap Fetches: 9

堆提取是将快速的仅索引扫描转变为慢速的普通索引扫描。

该表最近是否经历过大规模更新或删除?

缓慢扫描的原因是它必须在第一次匹配之前遍历许多不可见(已删除)的元组。

在桌子上运行VACUUM,两次扫描都会很快。

【讨论】:

我们确实对表进行了删除操作,删除了超过 30 天的数据。删除完成后,我们运行最旧的记录查询以进行监控。运行最旧记录查询时,删除已完成,直到最旧记录查询完成后才会运行。此外,ASC 和 DESC 查询可互换运行多次,每次 DESC 查询完成但 ASC 查询卡住。 我的错,我没有注意到LIMIT。我已经更新了答案。 谢谢。我会试试的。您之前的文章链接提供了非常丰富的信息。

以上是关于Postgres 仅索引扫描耗时太长的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Postgres 在使用覆盖索引时仍然进行位图堆扫描?

sysdate() 导致 Postgres 忽略索引并进行昂贵的顺序扫描

索引扫描时 Postgres 不使用索引是更好的选择

索引扫描不适用于 postgres 中的 json 数据集

防止在 Postgres 中为特定查询使用索引

如何强制 Postgres 使用特定索引?