【发布时间】:2014-06-12 15:36:16
【问题描述】:
我在 PostgreSQL 9.3 数据库中有一个大表(大约 10M 条记录),我正在尝试运行一个简单的更新语句:
UPDATE mytable SET fresh = null WHERE fresh = true;
它已经运行了一个多小时,看不到任何尽头。
但是,我知道:
SELECT count(*) FROM mytable WHERE fresh = true;
它会在几秒钟内运行,它只会影响 7000 条记录。
为什么我的更新需要这么长时间?我的数据库中没有任何触发器,fresh 列的索引为:
CREATE INDEX mytable_fresh ON mytable USING btree (fresh);
运行EXPLAIN UPDATE mytable SET fresh = null WHERE fresh = true 给出:
Update on mytable (cost=0.00..455553.18 rows=9525759 width=167)
-> Seq Scan on mytable (cost=0.00..455553.18 rows=9525759 width=167)
Filter: fresh
我是否正确,它正在扫描所有 950 万条记录而不使用索引?如果是这样,我该如何解决这个问题?
编辑:我的 fresh 列是可为空的布尔类型。我为true 值添加了一个部分索引,这大大加快了它的速度(22 毫秒)。不确定为什么部分索引有效而通用索引被完全忽略。我发现 Postgres 中的这种利基行为很常见,并且对于大型数据仓库项目非常令人沮丧。
【问题讨论】:
-
是的,它没有使用索引。 9M 行的行数是否准确?让我们看看您的表是如何定义的以及任何其他索引,只是为了完整。另外,对
SELECT count做一个解释,并确保它正在使用索引。 -
如果
fresh是一个布尔列,优化器可能不会考虑使用索引。通常,在布尔值上有一个索引是没有意义的。即使您考虑 NULL,您也将只有 3 个值:true、false 和 NULL,如果您假设均匀分布,这将导致 33% 的分区。这通常太多了,无法从索引中获得任何优势。 Postgres 是否支持查询提示或类似的东西? -
顺便说一句,您可以尝试一个针对小分区进行优化的部分索引,在您的情况下为“fresh=true”:
create index mytable_fresh on MyTable(fresh) where fresh is true; -
扫描 950 万条记录还是使用索引都没有关系,无论哪种方式都不应该花一个小时。你说你没有触发器,但是你有外键吗?您的服务器在做什么:CPU、IO 等?
-
除了@Martin 指出的由于cardinality 非常低而不使用索引之外,它还必须更新索引。
标签: sql postgresql postgresql-9.3 database-optimization