我有一个包含约 210 万行的 Postgres 表。我对其进行了以下更新:
WITH stops AS (
SELECT id,
rank() OVER (ORDER BY offense_timestamp,
defendant_dl,
offense_street_number,
offense_street_name) AS stop
FROM consistent.master
WHERE citing_jurisdiction=1
)
UPDATE consistent.master
SET arrest_id=stops.stop
FROM stops
WHERE master.id = stops.id;
Run Code Online (Sandbox Code Playgroud)
这个查询运行了 39 个小时。我在一个 4(物理)核心 i7 Q720 笔记本电脑处理器上运行它,有足够的内存,绝大多数时间没有其他东西运行。没有硬盘空间限制。该表最近已被清空、分析和重新索引。
在查询运行的整个过程中,至少在初始WITH
完成之后,CPU 使用率通常很低,并且 HDD 使用 100%。硬盘的使用非常用力,以至于任何其他应用程序的运行速度都比正常情况慢得多。
笔记本电脑的电源设置为高性能(Windows 7 x64)。
这是解释:
Update on master (cost=822243.22..1021456.89 rows=2060910 width=312)
CTE stops
-> WindowAgg (cost=529826.95..581349.70 rows=2060910 width=33)
-> Sort (cost=529826.95..534979.23 rows=2060910 width=33)
Sort Key: consistent.master.offense_timestamp, consistent.master.defendant_dl, consistent.master.offense_street_number, consistent.master.offense_street_name
-> Seq Scan on master (cost=0.00..144630.06 rows=2060910 width=33)
Filter: (citing_jurisdiction = 1)
-> Hash Join (cost=240893.51..440107.19 rows=2060910 width=312)
Hash Cond: (stops.id = consistent.master.id)
-> CTE Scan on stops (cost=0.00..41218.20 rows=2060910 width=48)
-> Hash (cost=139413.45..139413.45 rows=2086645 width=268)
-> Seq Scan on master (cost=0.00..139413.45 rows=2086645 width=268)
Run Code Online (Sandbox Code Playgroud)
citing_jurisdiction=1
只排除几万行。即使有这个WHERE
条款,我仍然在处理超过 200 万行。
硬盘使用 TrueCrypt 7.1a 进行整个驱动器加密。这会稍微减慢速度,但不足以导致查询花费那么多小时。
该WITH
部分只需大约 3 分钟即可运行。
该arrest_id
字段没有外键索引。该表有 8 个索引和 2 个外键。查询中的所有其他字段都被索引。
该arrest_id
领域没有限制,除了NOT NULL
。
该表共有 32 列。
arrest_id
是类型character variables(20)。我意识到rank()
产生一个数值,但我必须使用字符变化(20),因为我有其他行在citing_jurisdiction<>1
该字段中使用非数字数据。
arrest_id
对于所有带有 的行,该字段为空白citing_jurisdiction=1
。
这是一款个人高端(截至 1 年前)笔记本电脑。我是唯一的用户。没有其他查询或操作正在运行。锁定似乎不太可能。
此表或数据库中的任何其他地方都没有触发器。
此数据库上的其他操作永远不会花费大量时间。通过适当的索引,SELECT
查询通常非常快。
小智 29
我最近在一个包含 350 万行的表中发生了类似的事情。我的更新永远不会完成。经过大量的实验和挫折,我终于找到了罪魁祸首。原来是表上的索引正在更新。
解决方案是在运行更新语句之前删除正在更新的表上的所有索引。一旦我这样做了,更新会在几分钟内完成。更新完成后,我重新创建了索引并重新开始工作。在这一点上,这可能对您没有帮助,但可能会有其他人在寻找答案。
我会将索引保留在您从中提取数据的表上。那将不必不断更新任何索引,并且应该有助于查找要更新的数据。它在速度较慢的笔记本电脑上运行良好。
Cra*_*ger 15
您最大的问题是在笔记本电脑硬盘上进行大量写入繁重、搜索繁重的工作。无论您做什么,这都不会很快,特别是如果它是在许多笔记本电脑中提供的那种较慢的 5400RPM 驱动器。
TrueCrypt 使写入的速度减慢不止“一点”。读取速度相当快,但写入会使 RAID 5 看起来很快。在 TrueCrypt 卷上运行数据库将是写入的折磨,尤其是随机写入。
在这种情况下,我认为您尝试优化查询是在浪费时间。无论如何,您正在重写大多数行,并且您的可怕写入情况会很慢。我的建议是:
BEGIN;
SELECT ... INTO TEMPORARY TABLE master_tmp ;
TRUNCATE TABLE consistent.master;
-- Now DROP all constraints on consistent.master, then:
INSERT INTO consistent.master SELECT * FROM master_tmp;
-- ... and re-create any constraints.
Run Code Online (Sandbox Code Playgroud)
我怀疑这比单独删除和重新创建约束要快,因为 UPDATE 将具有相当随机的写入模式,这会杀死您的存储。两次批量插入,一次插入未记录的表,另一次插入没有约束的 WAL 记录表,可能会更快。
如果您有绝对最新的备份并且不介意必须从备份中恢复您的数据库,您还可以使用fsync=off
参数重新启动 PostgreSQL,并且full_page_writes=off
临时用于此批量操作。任何意外问题(如断电或操作系统崩溃)都会使您的数据库在fsync=off
.
相当于“无日志记录”的 POSTGreSQL 是使用未记录的表。如果数据库在脏时不干净地关闭,这些未记录的表将被截断。使用未注册的表将至少减半你的写入负载,减少寻道的次数,这样他们就可以成为一个LOT更快。
就像在 Oracle 中一样,删除索引然后在大批量更新后重新创建它可能是个好主意。PostgreSQL 的规划器无法计算出正在发生大更新,暂停索引更新,然后在最后重建索引;即使可以,它也很难弄清楚在什么时候值得这样做,尤其是提前。
归档时间: |
|
查看次数: |
25502 次 |
最近记录: |