从旧的完整转储恢复 PostgreSQL-9.6 数据库 and/or 一个最新的基本目录和其他获救的文件

Restore a PostgreSQL-9.6 database from an old complete dump and/or a up-to-date just base directory and other rescued files

我正在尝试 restore/rescue 我拥有的一些数据库:

编辑:

可能我有 pg_xlog 的内容,但我不知道文件名。我有 5 个大小为 16777216 字节的文件:

#288294 (date 2019-04-01)
#288287 (date 2019-05-14)
#288293 (date 2019-07-02)
#261307 (date 2019-11-27)
#270185 (date 2020-01-28)

另外我的旧转储来自 2019-04-23,所以第一个可以 一样吗?

所以我的下一步将尝试使用 pg_xlogdump 读取这些文件 and/or 试图用这些名称文件命名它们(以 00000001000000000000000A 按日期并将它们放入新的 pg_xlog 目录,我看到系统文件命名它们,可能是?)。另外我发现最后一个有硬盘崩溃的日期,所以我有最后一个。

我从硬盘(已损坏)中抢救出来的PGDATA/base目录包含目录1124061240737972,其中有很多里面的文件。我检查 pg_filedump -fi 我的更新数据是否存储在目录 37972.

中的文件中

相同(但旧)的数据存储在还原转储中目录 PGDATA/base/16387 中的文件中。

我尝试直接将文件从一个文件复制到另一个文件,并在旧数据库上混合更新的数据,但它不起作用。解决权限错误后,我可以通过这种方式进入 "Frankenstein" 数据库:

 postgres@host:~$ postgres --single -P -D /var/lib/postgresql/9.6/main/ dbname

我尝试做一些事情,比如重建索引,但我收到了这个错误:

PostgreSQL stand-alone backend 9.6.16
backend> reindex system dbname;
ERROR:  could not access status of transaction 136889
DETAIL:  Could not read from file "pg_subtrans/0002" at offset 16384: Success.
CONTEXT:  while checking uniqueness of tuple (1,7) in relation "pg_toast_2619"
STATEMENT:  reindex system dbname;

当然 pg_subtrans/0002 文件是 "Frankenstein" 的一部分而不是好的文件(因为我还没有找到它,不是那个名字),所以我尝试:复制另一个首先看起来很相似,然后用 dd 为该文件生成 8192 个零,在这两种情况下我都会得到相同的错误(如果文件不存在则得到 DETAIL: Could not open file "pg_subtrans/0002": No such file or directory.)。无论如何,我不知道那个文件应该是什么。您认为我可以从其他文件中获取该数据吗?或者我可以使用某种工具找到丢失的文件吗?所以 pg_filedump 显示那个目录 pg_subtrans/0000.

中的另一个文件是空的

额外说明: 我发现了这个有用的博客 post,它讨论了使用 pg_filedumppg_class 从刚获救的文件中恢复文件,reindex system 和其他工具,但我很难理解如何使它适应我的具体和更简单的问题(我认为我的问题更容易,因为我有一个转储):https://www.commandprompt.com/blog/recovering_a_lost-and-found_database/

那是注定的。如果没有 pg_xlog 和(特别是)pg_clog 中的信息,您将无法取回信息。

知识渊博的取证专家也许能够挽救您的一些数据,但这不是一个简单的过程。

经过4个部分PGDATA/base/37972目录,我们终于完全恢复了数据库:

  1. 检查并“greping”pg_filedump -fi哪个文件对应 每个 table。为了更容易地“greping”,我们制作了一个脚本。

    #!/bin/bash
    for filename in ./*; do
        echo "$filename"
        pg_filedump -fi "$filename"|grep ""
    done
    

    注意:仅对小字符串有用。

  2. 执行伟大的工具pg_filedump -D-D 是一个新选项(来自 postgresql-filedump 版本 ≥10),使用给定的逗号分隔类型列表解码元组。

    因为我们创建了数据库,所以我们知道类型,我们“只”需要给出与 table 相关的类型的逗号分隔列表。我写“只是”是因为在某些情况下它可能有点复杂。我们的 table 之一需要这种命令:

pg_filedump -D text,text,text,text,text,text,text,text,timestamp,text,text,text,text,int,text,text,int,text,int,text,text,text,text,text,text,text,text,text,text,int,int,int,int,int,int,int,int,text,int,int 38246 | grep COPY > restored_table1.txt

来自 pg_filedump -D 手册:

支持的类型:

  • bigint

  • 大连载

  • 布尔值

  • 字符

  • charN -- 字符(n)

  • 日期

  • 浮动

  • float4

  • float8

  • 整数

  • json

  • macaddr

  • 姓名

  • oid

  • 真实

  • 连载

  • smallint

  • smallserial

  • 文本

  • 时间

  • 时间戳

  • timetz

  • uuid

  • varchar

  • varcharN -- varchar(n)

  • xid

  • xml

  • ~ -- 忽略元组中剩余的所有属性

    所有这些 text 对我们来说都是类型字符 varying(255) 但 varcharN 对我们不起作用,所以在其他测试之后我们最终将其更改为 text

    timestamp 对我们来说是类型 timestamp with time zone 但是 timetz 对我们不起作用,所以在其他测试之后我们最终将其更改为 timestamp 我们选择丢失时区数据。

    此更改非常适合 table。

    其他 table 更容易:

    pg_filedump -D int,date,int,text 38183 | grep COPY > restored_table2.txt

  1. 因为我们得到的只是“原始”数据,所以我们必须重新格式化为 CSV 格式。所以我们为 format from pg_filedump -D output to CSV.

    制作了一个 python 程序
  2. 我们将每个 CSV 插入到 PostgreSQL(在再次创建每个空 table 之后):

    COPY scheme."table2"(id_comentari,id_observacio,text,data,id_usuari,text_old) 
    FROM '<path>/table2.csv' DELIMITER '|' CSV HEADER;
    

我希望这对其他人有帮助:)