Mar*_*ery 8 postgresql alter-table locking
许多 PostgreSQLALTER TABLE命令(例如添加具有默认值的新列)在最新版本的 PostgreSQL 中进行了巧妙的优化,一旦 Postgres 短暂获取了表上的锁,即使在大型表上,它们也能基本上立即执行。
不幸的是,最后的警告很重要。链接博客文章中类似这样的命令
ALTER TABLE users ADD COLUMN credits bigint NOT NULL DEFAULT 0;
Run Code Online (Sandbox Code Playgroud)
仍然需要等待表上的独占锁users才能运行,尽管一旦获取锁就会立即执行。更糟糕的是,当它等待该锁时,它会阻止涉及该表的所有写入和读取。
重现此问题的一些简单步骤(在 Postgres 13.3 中测试):
在一个psqlshell 中,创建一个表,然后启动一个事务,从表中进行读取,但不提交:
CREATE TABLE users (id SERIAL, name TEXT);
INSERT INTO users (name) VALUES ('bob'), ('fred');
START TRANSACTION;
SELECT * FROM users WHERE id = 1;
Run Code Online (Sandbox Code Playgroud)
让第一个 shell 打开,然后打开第二个 shell 并尝试更改表:
ALTER TABLE users ADD COLUMN credits bigint NOT NULL DEFAULT 0;
Run Code Online (Sandbox Code Playgroud)
请注意,此查询挂起,等待第一个 shell 中的事务提交。
打开第三个终端,然后尝试运行
SELECT * FROM users WHERE id = 2;
Run Code Online (Sandbox Code Playgroud)
观察这也挂起;现在它被阻止等待ALTER TABLE命令完成,而命令又被阻止等待第一个事务完成。
似乎大多数或所有ALTER TABLE命令的行为都是这样的。即使操作本身非常快或者可以在不持有整个操作锁的情况下运行,仍然需要在开始ALTER TABLE工作之前短暂获取表上的独占锁,并且在等待该锁时,所有其他语句触摸桌子 - 甚至阅读!- 被阻止。
不用说,如果您想要对偶尔涉及长时间运行的事务的表进行更改,这种行为会很成问题。如果该ALTER TABLE语句被一个长时间运行的事务阻塞,而该事务在该语句运行时恰好持有涉及该表的任何类型的锁ALTER TABLE,则与该表的所有交互都会被阻塞,直到该随机长时间运行的事务结束为止,任何依赖于该表的东西都可能会经历停机。
这个问题有规范的解决方案吗?
我尝试过的一个粗略的解决方案是使用一个包装器脚本,该脚本反复尝试ALTER TABLE通过lock_timeout设置为一个小值(例如 5 秒)的连接来运行该语句。如果ALTER TABLE由于锁定超时而失败,事务将中止,脚本会捕获错误,等待一两分钟,然后再次尝试整个过程。这避免了彻底的停机,但仍然会对性能产生影响,因为每次运行该ALTER TABLE语句的失败尝试仍然会阻塞查询几秒钟。
我真正想做的是以某种方式告诉 Postgres 我希望该ALTER TABLE语句等待一段时间,以便它可以获取表上的锁,同时不会阻塞其他查询。(我不介意这是否意味着它需要等待几个小时,直到它最终达到没有其他查询接触表的时刻;如果它避免阻塞其他查询,这绝对是一个可以接受的权衡。)有什么方法可以做到这一点 - 也许我可以在语句中包含一些咒语ALTER TABLE,或者我可以设置一些配置参数来改变这种行为?
不幸的是,除了循环重试之外,没有其他更好的选择。但你也许可以让重试变得更加聪明。当我需要执行此操作并且可以位于事务块中时,我会显式获取锁定,并使用NOWAIT选项。
但仍然会对性能产生影响,因为每次运行 ALTER TABLE 语句失败的尝试仍然会阻塞查询几秒钟。
您可以将超时值设置为低于(远低于)几秒。或者您可以使用NOWAIT,这应该与将 lock_timeout 设置为尽可能低的值大致相同,但一旦获取该锁就会自动重置(与多语句事务相关)。
我真正想做的是以某种方式告诉 Postgres 我希望 ALTER TABLE 语句等待一段时间,以便它可以获取表上的锁,同时不会阻塞其他查询。
是的,这里有一些更好的选择会很好。不过,要弄清楚它到底是什么样子可能会引起争议。也许类似于 MySQL 的低优先级锁,它将自己保留在等待队列中,但如果其他等待者可以立即以它想要的模式获得锁定,则允许其他等待者跳过它。
| 归档时间: |
|
| 查看次数: |
8582 次 |
| 最近记录: |