【发布时间】:2010-12-24 18:06:04
【问题描述】:
我有一个很简单的更新语句:
UPDATE W SET state='thing'
WHERE state NOT IN ('this','that') AND losttime < CURRENT_TIMESTAMP;
表 W 只有 90 行,尽管每行的 losttime 和 state 列大约每 10 秒更新一次。有关于状态和丢失时间的索引(以及主索引)。
我注意到大型数据库(即其他表有很多条目,而不是表 W)在一段时间内,查询变得越来越慢。运行48小时后,我在PqAdminIII的查询窗口中运行计时,耗时17分钟!
我在另一个显示相同问题的表上有一个类似的查询:
UPDATE H SET release='1'
WHERE a NOT IN (SELECT id from A WHERE state!='done') AND release!='1';
H 没有任何索引,但我尝试在 H(release) 上放置和删除索引,而行为没有改变。在数据库运行 48 小时并且表 H 有大约 100k 行之后,此查询需要 27 分钟。 Postgres 服务器在查询期间将有一个完全固定的线程(100% 的 CPU 利用率),因此看起来不会有任何网络、磁盘等争用。
总的来说,我看到的行为是我的数据库按预期运行了大约 5 分钟,然后随着与维护相关的基本 UPDATE 命令的运行时间越来越长,一切都逐渐停止。到第二天,需要一个小时来完成一个简单的维护周期(一些更新),一开始运行大约 100 毫秒。我似乎很清楚,性能下降与数据库中的信息量呈超线性关系——可能是 N^2 或类似的。
Autovacuum 使用默认设置。我(再次)通读了手册,并没有看到任何突然出现在我身上的东西。
我在这里摸不着头脑。我没有看到任何与 9.0.1 和 9.0.2 发行说明相关的错误修复。谁能帮我理解发生了什么?谢谢,M
-x-x-x-x-
好的,我这里可能有两个问题。
第一次更新现在似乎运行得很快。不知道发生了什么,所以我会假设我需要更频繁地运行 VACUUM / ANALYZE 或某种组合 - 说每分钟左右。我真的很想知道为什么 autovacuum 不适合我。
第二次更新继续缓慢运行。查询计划表明索引没有得到有效使用,并且发生了 80k*30k 的交叉,这可能是我观察到的超线性运行时的原因。 (大家同意这个计划的解释吗?)
我可以将 UPDATE 转换为 SELECT:
SELECT * from H
where a not in (SELECT id from A where state='done') AND release!='1';
具有相似的运行时间(27 分钟)。
如果我不信任 postgres 优化器并这样做:
WITH r as (select id from A where state='done')
SELECT a from H
JOIN on H.a=r.id
WHERE H.released='0';
然后查询在 ~500 毫秒内运行。
如何将这些知识转换回以可接受的速度运行的更新? 我的尝试:
UPDATE H SET release='1'
FROM A
where A.state!='done' AND release!='1' AND A.id=H.a;
运行大约 140 秒,速度更快,但仍然非常非常慢。
我可以从这里去哪里?
-x-x-x-x-
VACUUM ANALYZE 已作为“例行维护”的一部分添加,应用程序将大约每分钟左右运行一次,独立于正在运行的任何 autovacuum。
另外,重写第二个查询以消除已知缓慢的 NOT IN 子句,将其替换为“Left Anti-Semi Join”(嗯?)
UPDATE H SET release='1'
WHERE release='0' AND NOT EXISTS (SELECT * FROM A WHERE id=H.a AND state!='done');
【问题讨论】:
-
不更新表格怎么样?只需查看您需要在 W 和 H 表上获取的信息
-
请发布两个更新的计划。
-
@Hao:我对这个建议感到困惑。这不是读操作,我实际上是在修改每个表中的内容。
-
@Quassnoi:第一个 UPDATE 计划:“Seq Scan on W (cost=0.00..2.40 rows=1 width=52)”“过滤器:((state ALL ('{this, that}'::Wstate[])) AND (losttime 1::smallint) AND (NOT (SubPlan 1)))" " SubPlan 1" " -> Materialize (cost=2706.66..3735.35 rows=73969 width=4)" " -> Seq Scan on A (cost =0.00..2343.69 rows=73969 width=4)" " Filter: (state 'done'::Astate)" 我对 A 的 seq 扫描感到困惑:我在 'state.' 上有一个索引。跨度>
-
请不要在 cmets 中发布代码。相反,请更新您自己的帖子。
标签: sql postgresql sql-update vacuum