索引列上的非常慢的不同查询
Extremely slow distinct query on indexed column
在 Postgres 数据库中,我正在查询具有 3 亿行的大型 table 中 MY_DATE
的不同值。大约有 400 个,MY_DATE
列已编入索引。
Select distinct MY_DATE from MY_TABLE;
查询 运行s 22 分钟。
在我的 Oracle 数据库上使用完全相同的数据集和相同的索引定义进行相同的查询 运行s 11 秒。
查询计划显示查询正在使用索引:
EXPLAIN Select distinct MY_DATE from MY_TABLE LIMIT 200;
给出:
QUERY PLAN
Limit (cost=0.57..7171644.14 rows=200 width=8)
-> Unique (cost=0.57..15419034.24 rows=430 width=8)
-> Index Only Scan using idx_obsdate on my_table (cost=0.57..14672064.14 rows=298788038 width=8)
当我限制结果时,查询会变得更快。例如
Select distinct MY_DATE from MY_TABLE LIMIT 5;
运行 亚秒。
但是:
Select distinct MY_DATE from MY_TABLE LIMIT 50;
已经需要几分钟了。时间似乎随着 LIMIT
子句呈指数增长。
我希望 Postgres 查询在几秒钟内达到 运行,就像我的 OracleDB 一样。
索引扫描需要 20 分钟 - 即使是大的 table - 似乎也不合适。
对导致问题的原因以及我可以做什么有什么建议吗?
distinct values ... 300 million rows ... about 400 of them ... column ... indexed.
有很多 更快的技术。模拟一个loose index scan(a.k.a。跳过扫描),并假设my_date
被定义为NOT NULL
(或者我们可以忽略NULL值):
WITH RECURSIVE cte AS (
SELECT min(my_date) AS my_date
FROM my_table
UNION ALL
SELECT (SELECT my_date
FROM my_table
WHERE my_date > cte.my_date
ORDER BY my_date
LIMIT 1)
FROM cte
WHERE my_date IS NOT NULL
)
TABLE cte;
相关:
- Optimize GROUP BY query to retrieve latest record per user
使用您提到的索引,它应该在 毫秒内完成 。
Oracle DB ... 11 seconds.
因为 Oracle 有本机索引跳过扫描而 Postgres 没有。在 Postgres 12 中有 ongoing efforts 实现类似的功能。
目前(Postgres 11),虽然使用索引效果很好,但即使在仅索引扫描中,Postgres 也无法向前跳过,必须顺序读取索引元组。如果没有LIMIT
,则必须扫描完整的索引。因此我们在您的 EXPLAIN
输出中看到:
Index Only Scan ... rows=298788038
建议的新查询通过读取 400 个索引元组(每个不同值一个)实现相同的效果。 差别大。
使用 LIMIT
(没有 ORDER BY
!),就像您测试的那样,一旦检索到足够的行,Postgres 就会停止。增加限制具有 线性 效果。但是,如果每个不同值的行数可以变化,那么增加的成本也会变化。
在 Postgres 数据库中,我正在查询具有 3 亿行的大型 table 中 MY_DATE
的不同值。大约有 400 个,MY_DATE
列已编入索引。
Select distinct MY_DATE from MY_TABLE;
查询 运行s 22 分钟。
在我的 Oracle 数据库上使用完全相同的数据集和相同的索引定义进行相同的查询 运行s 11 秒。
查询计划显示查询正在使用索引:
EXPLAIN Select distinct MY_DATE from MY_TABLE LIMIT 200;
给出:
QUERY PLAN
Limit (cost=0.57..7171644.14 rows=200 width=8)
-> Unique (cost=0.57..15419034.24 rows=430 width=8)
-> Index Only Scan using idx_obsdate on my_table (cost=0.57..14672064.14 rows=298788038 width=8)
当我限制结果时,查询会变得更快。例如
Select distinct MY_DATE from MY_TABLE LIMIT 5;
运行 亚秒。
但是:
Select distinct MY_DATE from MY_TABLE LIMIT 50;
已经需要几分钟了。时间似乎随着 LIMIT
子句呈指数增长。
我希望 Postgres 查询在几秒钟内达到 运行,就像我的 OracleDB 一样。 索引扫描需要 20 分钟 - 即使是大的 table - 似乎也不合适。
对导致问题的原因以及我可以做什么有什么建议吗?
distinct values ... 300 million rows ... about 400 of them ... column ... indexed.
有很多 更快的技术。模拟一个loose index scan(a.k.a。跳过扫描),并假设my_date
被定义为NOT NULL
(或者我们可以忽略NULL值):
WITH RECURSIVE cte AS (
SELECT min(my_date) AS my_date
FROM my_table
UNION ALL
SELECT (SELECT my_date
FROM my_table
WHERE my_date > cte.my_date
ORDER BY my_date
LIMIT 1)
FROM cte
WHERE my_date IS NOT NULL
)
TABLE cte;
相关:
- Optimize GROUP BY query to retrieve latest record per user
使用您提到的索引,它应该在 毫秒内完成 。
Oracle DB ... 11 seconds.
因为 Oracle 有本机索引跳过扫描而 Postgres 没有。在 Postgres 12 中有 ongoing efforts 实现类似的功能。
目前(Postgres 11),虽然使用索引效果很好,但即使在仅索引扫描中,Postgres 也无法向前跳过,必须顺序读取索引元组。如果没有LIMIT
,则必须扫描完整的索引。因此我们在您的 EXPLAIN
输出中看到:
Index Only Scan ... rows=298788038
建议的新查询通过读取 400 个索引元组(每个不同值一个)实现相同的效果。 差别大。
使用 LIMIT
(没有 ORDER BY
!),就像您测试的那样,一旦检索到足够的行,Postgres 就会停止。增加限制具有 线性 效果。但是,如果每个不同值的行数可以变化,那么增加的成本也会变化。