PostgreSQL 奇遇记,第一集

来自 PostgreSQL wiki
跳转到导航跳转到搜索

第一集:使用 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

呼!我们回来了,恢复正常了!