PostgreSQL 奇遇记,第一集
第一集:使用 Template0 恢复损坏的 Template1
版权所有 © 2002 年 5 月 Josh Berkus。
可以以未经修改的形式重新分发,包括此版权声明。保留所有其他权利。
我们都经历过打字速度比大脑快的情况。通常,对我来说,这发生在电子邮件中,但偶尔我也会在我的数据库服务器上按错了“Enter”键,把事情搞得一团糟。这是其中一次。
我将正在使用的数据库转储了,并编辑了一些引用完整性链接和约束(这可能是 PostgreSQL 中进行此操作的最佳方法)。现在,我准备重新加载我编辑过的数据库。在我的控制台上
chocolate-mousse:> psql -U postgres template1 Welcome to psql, the PostgreSQL interactive terminal. Type: \copyright for distribution terms \h for help with SQL commands \? for help on internal slash commands \g or terminate with semicolon to execute query \q to quit template1=# DROP DATABASE staffos; DROP template1=# CREATE DATABASE staffos; CREATE template1=# \i staffos_revised.pgdump CREATE CREATE CREATE CREATE etc ...
有人发现我的错误了吗?我发现了,大约 30 秒后……这比晚了 30 秒。我把整个数据库模式加载到 Template1 中,Template1 是模板数据库,而不是 StaffOS 数据库!这将破坏我在该服务器上之后创建的每个数据库。
(对于那些不知道的人来说,Template1 是 PostgreSQL 的“数据库模板”。当你想要添加一些东西时,比如用户或 PL/pgSQL 语言,你会修改它,这些东西会添加到你在该服务器上创建的所有数据库中。但是,你不想把它当作一个普通的数据库……永远!)
我能做什么?我可以将整个服务器转储、INITDB,然后一次重新加载所有数据库(11 个数据库——哎哟!)。我可以尝试手动清理 Template1 数据库,但 StaffOS 数据库有 28 个表、40 个视图和 85 个函数,这个解决方案几乎不可能。
因此,我转而求助于邮件列表。PGSQL-SQL 上的一位热心的用户向我指出了这个文档页面。
我欢呼雀跃!我得救了。在 PostgreSQL 7.2 中,有一个第二模板数据库 Template0,它作为 Template1 的紧急备份存在——以防万一有人像我一样犯了如此严重的错误。
说到错误,我立即备份了整个服务器。磁盘空间很便宜,而且我们不能太小心。(注意:如果你继续执行以下任何说明,请注意,修改 Template1 和 Template0 会导致你的数据库服务器崩溃。我们对任何问题概不负责。)
但是,为了保护其作为紧急备份的身份并防止弄乱它,Template0 受保护。因此
template1=# \c template0 FATAL 1: Database "template0" is not currently accepting connections Previous connection kept
秘密在于一个名为 pg_databases 的“系统表”。此表存在于所有数据库中,并定义了数据库的一些基本属性。我们特别感兴趣的是这两个属性,datistemplate 和 datallowconn。“datistemplate”告诉我们哪些是我们的模板数据库,“datallowconn”告诉我们注册用户可以连接到哪些数据库。如果我们查看该表,就会看到
template1=# select * from pg_database; datname | datdba | encoding | datistemplate | datallowconn | -------------+--------+----------+---------------+--------------+ backup_test | 27 | 0 | f | t | britlist | 27 | 0 | f | t | dcl | 1 | 0 | f | t | template0 | 1 | 0 | t | f | kevinprob | 28 | 0 | f | t | kitchen | 1 | 0 | f | t | oooconlist | 103 | 0 | f | t | regression | 1 | 0 | f | t | staffos | 27 | 0 | f | t | template1 | 1 | 0 | t | t | etc ...
为了连接到 Template0,我们需要更改该标志
template1=# UPDATE pg_database SET datallowconn = TRUE template1-# WHERE datname = 'template0'; UPDATE 1
现在我们可以连接,并删除 Template1 数据库,以便用 Template0 的副本替换它。
template1=# \c template0 CONNECT template0=# drop database template1; ERROR: DROP DATABASE: database is marked as a template
哎呀!我们需要设置另一个标志
template0=# UPDATE pg_database SET datistemplate = FALSE template0-# WHERE datname = 'template1'; UPDATE 1
现在,我们可以(这是风险部分,先备份!)
template0=# drop database template1; DROP template0=# create database template1 with template = template0; CREATE
呼!现在,让我们快速把东西恢复到我们发现它们的状态
template0=# UPDATE pg_database SET datistemplate = TRUE template0-# WHERE datname = 'template1'; UPDATE 1 template0=# \c template1 CONNECT template1=# UPDATE pg_database SET datallowconn = FALSE template1-# WHERE datname = 'template0';
不幸的是,在这个过程中,我丢失了一些东西。我在 Template1 中设置了一些函数,我想保留它们,例如“createlang plpgsql”的结果和一些文本解析函数。我不得不手动重新创建它们。我建议你将这些 Template1 修改保存在某个文件中,以节省自己的时间。
最后,根据文档的建议,我做了一件事来确保我的数据库服务器的最高效率
template1-# VACUUM FULL FREEZE; VACUUM
呼!我们回来了,恢复正常了!