只读表
此页面包含历史信息或已弃用的文章。
v0.2 2007年12月12日
Postgres 支持冻结元组的概念,这样它们就可以永远存在于数据库中,而无需进一步写入。目前没有命令可以保证表已被完全冻结。这使得将数据文件可靠地写入 WORM 介质以进行长期存档变得困难。(WORM 表示“一次写入,多次读取”。)同样,仅因为少量行需要冻结,而不得不再次对大型表进行 VACUUM 操作也是很痛苦的。
因此,我们需要一个 DDL 命令来确保所有元组都被冻结,然后将表标记为只读。理想情况下,我们希望以一种不会占用长时间的完整表锁的方式来执行此操作,因为我们希望数据始终保持可访问。
所以……VACUUM FREEZE table SET READ ONLY;
将是我首先想到的,但我猜测每个人都会敦促我支持更明显的
ALTER TABLE table SET READ ONLY;
此命令将在表上放置一个 ShareLock(仅),阻止任何人写入表,同时我们将其冻结。ShareLock 与对表进行过写入的任何事务都不兼容,因此,当我们获取锁时,所有写入表的事务都将完成。然后我们运行相当于 VACUUM FREEZE 的操作,这将能够在一遍扫描中冻结 *所有* 行(而不是所有行,除了最新行)。
冻结扫描将一直进行,直到它看到一个可能不可见的行,即由最近的事务 ID 写入的行。此时,ALTER TABLE 将等待该事务 ID 完成,然后继续进行。我们假设只有很少的行可能是不可见的,因此我们不会在扫描表时更新 OldestXmin。因此,由长时间运行的事务最近更新的表可能会导致 ALTER TABLE 等待很长时间,远远超过表扫描的时间。
在冻结扫描完成后,我们将更新 pg_class 条目以显示它现在是只读的,因此我们将模仿 VACUUM 的做法。
这种形式的 ALTER TABLE 命令需要进行修改,以便它只能在事务块之外运行,并且只获取 ShareLock 而不是 AccessExclusiveLock。
反转此过程很简单,因为我们只需要关闭 pg_class 中的标志即可
ALTER TABLE table SET READ WRITE;
也许可以在不获取 AccessExclusiveLock 的情况下执行此操作,尽管这不是此实现的重要部分。
只读表不需要 VACUUM 操作,因此我们可以让 autovacuum 和显式 vacuum 忽略它们。
只读表不能写入,但仍允许对表持有隐式或显式 INSERT、UPDATE 和 DELETE 权限。尝试写入表将导致一个特定的“只读表不能修改”错误。这允许将表置于只读模式很长时间,如果需要更新,则可以将其切换回读写模式。这对各种性能原因很有用,稍后会详细说明。我们不能使用权限机制来阻止写入,因为超级用户可以绕过它们。(有想法吗?)
对只读表发出元组级 SHARE 锁请求(例如 FOR SHARE)将被忽略,因为它们实际上已经存在。因此,我们不需要更改锁的内部机制,也不需要编辑 RI 代码以删除对 SHARE 锁引用表的调用。在解析后分析期间执行此操作。
可以使用以下命令将表复制到 WORM 介质中
ALTER TABLE table SET TABLESPACE tblspc;
这也会使用 ShareLock 而不是 AccessExclusiveLock,借用上面提到的工作成果。
同时运行 SET TABLESPACE 和 SET READ ONLY 听起来像个好主意,但 ISTM 将需要两个截然不同的代码路径,因此如果我们要这样做,它将是稍后的添加。