【发布时间】:2013-04-04 06:06:59
【问题描述】:
有两个通过 id 链接的表:
item_tbl (id)
link_tbl (item_id)
item_tbl 中有一些记录在link_tbl 中没有匹配的行。计算其数量的选择是:
SELECT COUNT(*)
FROM link_tbl lnk LEFT JOIN item_tbl itm ON lnk.item_id=itm.id
WHERE itm.id IS NULL
我想从link_tbl 中删除那些孤立记录(那些在另一个表中没有匹配的记录),但我能想到的唯一方法是:
DELETE FROM link_tbl lnk
WHERE lnk.item_id NOT IN (SELECT itm.id FROM item_tbl itm)
link_tbl 中有
262,086,253 条记录
3,033,811 中有item_tbl
16,844,347 孤儿记录link_tbl.
服务器有 4GB RAM 和 8 核 CPU。
EXPLAIN DELETE FROM link_tbl lnk
WHERE lnk.item_id NOT IN (SELECT itm.id FROM item_tbl itm)
返回:
Delete on link lnk (cost=0.00..11395249378057.98 rows=131045918 width=6)
-> Seq Scan on link lnk (cost=0.00..11395249378057.98 rows=131045918 width=6)
Filter: (NOT (SubPlan 1))
SubPlan 1
-> Materialize (cost=0.00..79298.10 rows=3063207 width=4)
-> Seq Scan on item itm (cost=0.00..52016.07 rows=3063207 width=4)
问题是:
- 有没有更好的方法来删除
link_tbl中的孤立记录? -
上述解释的准确性如何,或者删除这些记录需要多长时间?
- 编辑:根据 Erwin Brandstetter 的评论修复。
- 编辑:PostgreSql 版本是 9.1
- 编辑:postgresql.config 的某些部分
- shared_buffers = 368MB
- temp_buffers = 32MB
- work_mem = 32MB
- maintenance_work_mem = 64MB
- max_stack_depth = 6MB
- fsync = 关闭
- synchronous_commit = 关闭
- full_page_writes = 关闭
- wal_buffers = 16MB
- wal_writer_delay = 5000 毫秒
- commit_delay = 10
- commit_siblings = 10
- 有效缓存大小 = 1600MB
- 编辑:根据 Erwin Brandstetter 的评论修复。
分辨率:
谢谢大家的建议,很有帮助。我终于使用了 Erwin Brandstetter https://stackoverflow.com/a/15959896/1331340 建议的删除,但我稍微调整了一下:
DELETE FROM link_tbl lnk
WHERE lnk.item_id BETWEEN 0 AND 10000
AND lnk.item_id NOT IN (SELECT itm.id FROM item itm
WHERE itm.id BETWEEN 0 AND 10000)
我比较了 NOT IN 和 NOT EXISTS 的结果,输出如下,虽然我使用了 COUNT 而不是 DELETE,但我认为应该是相同的(我的意思是为了进行相对比较):
EXPLAIN ANALYZE SELECT COUNT(*)
FROM link_tbl lnk
WHERE lnk.item_id BETWEEN 0 AND 20000
AND lnk.item_id NOT IN (SELECT itm.id
FROM item_tbl itm
WHERE itm.id BETWEEN 0 AND 20000);
QUERY PLAN
Aggregate (cost=6002667.56..6002667.57 rows=1 width=0) (actual time=226817.086..226817.088 rows=1 loops=1)
-> Seq Scan on link_tbl lnk (cost=1592.50..5747898.65 rows=101907564 width=0) (actual time=206.029..225289.570 rows=566625 loops=1)
Filter: ((item_id >= 0) AND (item_id <= 20000) AND (NOT (hashed SubPlan 1)))
SubPlan 1
-> Index Scan using item_tbl_pkey on item_tbl itm (cost=0.00..1501.95 rows=36221 width=4) (actual time=0.056..99.266 rows=17560 loops=1)
Index Cond: ((id >= 0) AND (id <= 20000))
Total runtime: 226817.211 ms
EXPLAIN ANALYZE SELECT COUNT(*)
FROM link_tbl lnk WHERE lnk.item_id>0 AND lnk.item_id<20000
AND NOT EXISTS (SELECT 1 FROM item_tbl itm WHERE itm.id=lnk.item_id);
QUERY PLAN
Aggregate (cost=8835772.00..8835772.01 rows=1 width=0)
(actual time=1209235.133..1209235.135 rows=1 loops=1)
-> Hash Anti Join (cost=102272.16..8835771.99 rows=1 width=0)
(actual time=19315.170..1207900.612 rows=566534 loops=1)
Hash Cond: (lnk.item_id = itm.id)
-> Seq Scan on link_tbl lnk (cost=0.00..5091076.55 rows=203815128 width=4) (actual time=0.016..599147.604 rows=200301872 loops=1)
Filter: ((item_id > 0) AND (item_id < 20000))
-> Hash (cost=52016.07..52016.07 rows=3063207 width=4) (actual time=19313.976..19313.976 rows=3033811 loops=1)
Buckets: 131072 Batches: 4 Memory Usage: 26672kB
-> Seq Scan on item_tbl itm (cost=0.00..52016.07 rows=3063207 width=4) (actual time=0.013..9274.158 rows=3033811 loops=1)
Total runtime: 1209260.228 ms
NOT EXISTS 慢了 5 倍。
实际删除数据并没有我担心的时间,我可以分5批删除(10000-20000,20000-100000,100000-200000,200000-1000000和1000000-1755441) .一开始我发现了max item_id,我只需要穿过一半的桌子。
当我尝试 NOT IN 或 EXISTS without the range (with select count) 时,它甚至没有完成,我让它在夜间运行,它仍在早上运行。
我想我是在 Wildplasser 的回答 https://stackoverflow.com/a/15988033/1331340 中寻找 DELETE with USING,但为时已晚。
DELETE FROM one o
USING (
SELECT o2.id
FROM one o2
LEFT JOIN two t ON t.one_id = o2.id
WHERE t.one_id IS NULL
) sq
WHERE sq.id = o.id
;
【问题讨论】:
-
我想你会想要不存在:techonthenet.com/sql/delete.php
-
我以前从未见过如此高的成本,即使在解释明显荒谬的陈述时也是如此。所以我猜想#2的答案是“比任何人都愿意等待的时间要长得多”。除了看看 explain 对 lucas 的建议说了什么之外,我还想在 item_tbl.id 上放置一个索引,以及从朋友那里借更多的 RAM。
-
我的猜测是要么没有合适的索引/键,要么预期的命中率太高(低熵索引)。也可能是 work_mem 设置得太高,而 random_page_cost 是默认值(:= 等于 sequence_page_cost)
-
顺便说一句:简单的数学运算:删除/触摸 16M/252M 将导致约 6% 的行被删除。如果分布(你有有效的统计数据吗?)不是太偏,这实际上意味着你需要接触每一页(加上索引)seq scan 可能是一个不错的选择。
-
请解决您的问题。文字与代码和数字所传达的内容相反。孤儿在哪里?您还应该提供您的 PostgreSQL 版本、有关现有索引的信息以及是否可以在任何地方存在 NULL 值。
标签: sql postgresql exists bigdata sql-delete