乍一看,重命名数据库中的实体似乎应该很容易。SQL 是一个非常简单的单行语句ALTER ... RENAME TO ...,那么会出现什么问题呢?
好吧,事实证明很多。任何以前运行过生产数据库的人都会认识到,在学术环境之外,这实际上有点困难。问题不在于数据库本身,而在于数据库客户端。当重命名发生时,任何仍然在旧名称上运行的东西都会立即中断,从而导致停机和重大用户影响。
另一种方法是暂时禁用所有客户端,然后进行重命名,确实,“我们正在停机维护”屏幕在 2000 年代很常见,但 2020 年代的服务旨在不停机。这对用户来说很烦人,如果服务在做一些关键业务,也会很痛苦。
实际上,管理生产数据库的最简单方法是永远不要重命名任何内容,并且要接受某些名称不是最佳的事实。我敢猜测这就是大多数商店的运作方式——人们通常更愿意酌情重命名,但实际上这比它的价值更多的是时间、风险和精力。
复制此代码
但是对于结构狂热者(比如我自己)来说,这不是一个令人满意的答案。我们想重命名事物,但也希望在零停机和零用户影响的情况下这样做。幸运的是,Postgres 相对容易地使这成为可能。
下面是一些用于重命名表的复制/粘贴迁移代码(在本例中为chainwheel-> sprocket):
BEGIN;
ALTER TABLE chainwheel
RENAME TO sprocket;
CREATE VIEW chainwheel AS
SELECT *
FROM sprocket;
COMMIT;
并在部署后(在所有客户轮换之后)跟进:
DROP VIEW chainwheel;
细节
为什么这样有效:
RENAME TO立即使该表以其新名称sprocket.- 为了支持仍然使用旧名称运行的程序,我们使用表的旧名称创建一个视图代替
chainwheel表。 - Postgres 支持可更新视图,这意味着有一些注意事项,视图可以支持针对其基础表的
INSERT、UPDATE、DELETE操作 - Postgres 支持事务性 DDL,以便
RENAME TO和CREATE VIEW原子性的发生。对于其他数据库使用者来说,表已被重命名但新视图尚不可用的时刻永远不会发生。这是使更改成为可能而不会影响用户的关键功能。
概念验证演示
我做了一个小演示项目,演示了即使在程序运行时重命名的安全性。
我们启动一个小应用程序来读取和写入现有名称的表:
$ TABLE_NAME=chainwheel bundle exec ruby app.rb
Inserted and read 100 records of 'chainwheel'
Inserted and read 100 records of 'chainwheel'
Inserted and read 100 records of 'chainwheel'
Inserted and read 100 records of 'chainwheel'
Inserted and read 100 records of 'chainwheel'
Inserted and read 100 records of 'chainwheel'
Inserted and read 100 records of 'chainwheel'
...
在它运行时,迁移到新名称:
bundle exec sequel -m migrations/ -M 2 postgres://localhost:5432/postgres-table-rename-test
并注意应用程序如何继续愉快地运行:
...
Inserted and read 100 records of 'chainwheel'
Inserted and read 100 records of 'chainwheel'
Inserted and read 100 records of 'chainwheel'
...
然后我们可以启动一个使用新名称的进程:
$ TABLE_NAME=sprocket bundle exec ruby app.rb
Inserted and read 100 records of 'sprocket'
Inserted and read 100 records of 'sprocket'
Inserted and read 100 records of 'sprocket'
...
在现实生活中,这将是原始应用程序的新部署,其表引用已从 更新chainwheel为sprocket。在它上线并清除旧进程后,我们将运行DROP VIEW chainwheel.
干净的工作空间
该演示继续展示可更新视图如何用于重命名,但不能保证会一直保持可更新的状态。如果将原始表的结构更改为需要的列不在视图中,例如添加NOT NULL新列,则视图上的插入将开始失败。
为避免生产问题,请务必完成重命名操作的后续跟进。重命名并创建视图,部署并重新启动针对数据库运行的代码,然后删除视图。不要让它在未来成为其他人的猎枪。
原文地址:https://brandur.org/fragments/postgres-table-rename
原文作者:brandur




