青年数据库学习互助会

PostgreSQL 17.6 表被误删除,无备份,如何恢复?

Image
使用PDU工具,一键恢复误删除的数据:
cjc.cjc=# restore del all;▌ 扫描归档目录   起始文件: 000000010000000000000001   终点文件: 00000001000000000000000C当前startwal设置和建议startwal之间差距较大导致恢复效率低下,是否确认执行?y/n  y▌ 事务恢复模式────────────────────────────────────────|-已解析数据条数: 10000▌ 解析完成┌───────────────────────────────────────────────────────────────┐ 表 t100 ● 恢复数据共计 10000 行 ● 成功: 10000      ● 失败: 0 ● 文件路径: restore/public/t100_del_2025-08-30 05:20:55.802215 CST_2025-08-30 18:40:36.241959 CST.csv└───────────────────────────────────────────────────────────────┘

工具作者ZhangChen,微信公众号《ZhangChen-PDU》,可以关注大佬的微信公众号,获取工具。

详细过程如下:

PostgreSQL17.6源码安装

环境说明:

OS:Oracle Linux Server 7.5DB:PostgreSQL 17.6

创建用户,组,目录等

groupadd -g 1500 postgresuseradd -g 1500 -u 1500 postgrespasswd postgresmkdir -p /pg/{app/17,data,log,conf,soft,arch}chown postgres.postgres /pg -R

配置环境变量

su - postgresvi /home/postgres/.bash_profileexport PGHOME=/pg/app/17/pgsqlexport PATH=$PATH:$PGHOME/binexport PGDATA=/pg/dataexport PGLOG=/pg/logexport LD_LIBRARY_PATH=/pg/app/17/lib:$LD_LIBRARY_PATH
source /home/postgres/.bash_profile

下载安装介质

https://www.postgresql.org/ftp/source/v17.6/
Image

解压

[postgres@cjc-db-05 ~]$ ls -lrth /pg/soft/total 27M-rw-r--r-- 1 postgres postgres 27M Aug 30 04:45 postgresql-17.6.tar.gz
cd /pg/soft/tar -zxvf postgresql-17.6.tar.gzmv postgresql-17.6/* /pg/app/17/

编译

yum install -y gcc gzip bzip2 tar perl perl-ExtUtils-Embed readline-devel zlib-devel pam-devel libxml2-devel libxslt-devel openldap-devel python-devel gcc-c++ openssl-devel cmake libicu-devel gcc gzip bzip2 tar systemd-devel
cd /pg/app/17/./configure make && make install
mv /usr/local/pgsql/* /pg/app/17/chown postgres:postgres /pg -R

初始化

su - postgres[postgres@cjc-db-05 ~]$ initdb -D /pg/data

修改参数:

vi /pg/data/postgresql.conflisten_addresses = '*'
vi /pg/data/pg_hba.conf # IPv4 local connections:# host all             all             127.0.0.1/32            trusthost all             all             0.0.0.0/0               trust

启动:

[postgres@cjc-db-05 ~]$ pg_ctl -D /pg/data -l /pg/log/pg.log startwaiting for server to start.... doneserver started

登录

[postgres@cjc-db-05 ~]$ psql psql (17.6)Type "help" for help.

版本

postgres=# select version();version                                                   -------------------------------------------------------------------------------------------------------------PostgreSQL 17.6 on x86_64-pc-linux-gnu, compiled bygcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-28.0.1), 64-bit(1 row)

开启归档

postgres=# show archive_mode;archive_mode --------------off(1 row)

修改参数

archive_mode = on archive_command = 'DATE=$(date "+%%F-%%T");DIR="/pg/arch/$DATE";(test -d $DIR || /bin/mkdir -p $DIR) && /bin/cp %p $DIR/%f'

重启数据库

pg_ctl -D /pg/data -l /pg/log/pg.log stop -m smartpg_ctl -D /pg/data -l /pg/log/pg.log start

查看归档:

postgres=# show archive_mode;archive_mode --------------on(1 row)
postgres=# checkpoint;CHECKPOINT
postgres=# select pg_switch_wal();pg_switch_wal ---------------0/19AE878(1 row)
[postgres@cjc-db-05 ~]$ ls -lrth /pg/arch/total 0drwx------ 2 postgres postgres 38 Aug 30 16:23 2025-08-30-16:23:09
[postgres@cjc-db-05 ~]$ ls -lrth /pg/arch/2025-08-30-16\:23\:09/total 16M-rw------- 1 postgres postgres 16M Aug 30 16:23 000000010000000000000001

新建测试数据

创建用户

create role cjc SUPERUSER PASSWORD '1';alter role cjc with login;CREATE SCHEMA cjc AUTHORIZATION cjc;

创建表空间

CREATE TABLESPACE cjc OWNER cjc LOCATION '/pg/cjc';

创建数据库

create database cjc owner cjc tablespace cjc;grant connect on database cjc to cjc;

登录

[postgres@cjc-db-05 scripts]$ psql -p 5432 -U cjc -W cjcPassword: psql (17.6)Type "help" for help.
cjc=# \c cjcPassword: You are now connected to database "cjc" as user "cjc".

新建表:

创建员工信息表并随机插入10000条数据

CREATE TABLE cjc.t100 (employee_id SERIAL PRIMARY KEY,name VARCHAR(50) NOT NULL,department VARCHAR(50),position VARCHAR(50),age INTEGER CHECK (age >= 18 AND age <= 65),phone VARCHAR(15));

插入10000条随机数据

INSERT INTO cjc.t100 (name, department, position, age, phone)

SELECT 

-- 生成随机姓名(中文)

(array['张','王','李','赵','钱','孙','周','吴','郑','王'])[floor(random()*10)+1] ||

(array['伟','芳','娜','秀英','敏','静','磊','强','洋','勇'])[floor(random()*10)+1],

-- 生成随机部门

(array['人力资源部','财务部','技术部','市场部','销售部','运营部','研发部','客服部','采购部','行政部'])[floor(random()*10)+1],

-- 生成随机岗位

(array['经理','主管','工程师','专员','助理','分析师','顾问','代表','协调员','总监'])[floor(random()*10)+1],

-- 生成随机年龄(18-65岁)

floor(random()*(65-18+1))+18,

-- 生成随机手机号(1开头11位)

'1' || lpad(floor(random()*10000000000)::bigint::text, 10, '0')

FROM generate_series(1,10000);

查看测试数据:

cjc=# select * from cjc.t100;

employee_id |  name  | department | position | age |    phone    

-------------+--------+------------+----------+-----+-------------

1 | 赵勇   | 财务部     | 代表     |  39 | 11307775998

2 | 周娜   | 技术部     | 协调员   |  60 | 16313987906

3 | 赵芳   | 研发部     | 助理     |  45 | 14987637004

4 | 郑勇   | 运营部     | 协调员   |  44 | 15410717321

5 | 吴芳   | 技术部     | 代表     |  64 | 13225099067

6 | 钱洋   | 采购部     | 分析师   |  26 | 14959971395

7 | 钱娜   | 市场部     | 代表     |  23 | 17328770772

8 | 郑敏   | 采购部     | 协调员   |  54 | 14822293070

9 | 张强   | 市场部     | 顾问     |  31 | 11616783226

10 | 吴静   | 客服部     | 工程师   |  27 | 18070893302

......

模拟delete误删除

cjc=# delete from cjc.t100;DELETE 10000cjc=# select * from cjc.t100;employee_id | name | department | position | age | phone -------------+------+------------+----------+-----+-------(0 rows)
cjc=# select pg_switch_wal();pg_switch_wal ---------------0/C14D4C8(1 row)
cjc=# checkpoint;CHECKPOINT

将归档文件拷贝到/pg/other下,准备进行恢复。

[postgres@cjc-db-05 pg]$ cp /pg/arch/*/* /pg/other/

PDU恢复误删除的数据

工具获取:

作者ZhangChen,微信公众号《ZhangChen-PDU》,可以关注大佬的微信公众号,获取工具。

https://mp.weixin.qq.com/s/7YfjR611vfAbSbt4RUC_hg

上传工具

PDU2.5_for_Postgresql10-17社区版_20250809_x86.zip

[postgres@cjc-db-05 soft]$ mkdir PDU[postgres@cjc-db-05 soft]$ mv PDU2.5_for_Postgresql10-17社区版_20250809_x86.zip PDU2.5.zip[postgres@cjc-db-05 soft]$ mv PDU2.5.zip PDU
[postgres@cjc-db-05 PDU]$ unzip PDU2.5.zip [postgres@cjc-db-05 PDU]$ ls -lrth *-rw-r--r-- 1 postgres postgres 273 Aug  9 21:23 pdu.ini-rwxr-xr-x 1 postgres postgres 2.1M Aug 9 21:25 pdu10-rwxr-xr-x 1 postgres postgres 2.1M Aug 9 21:25 pdu11-rwxr-xr-x 1 postgres postgres 2.1M Aug 9 21:25 pdu12-rwxr-xr-x 1 postgres postgres 2.1M Aug 9 21:25 pdu13-rwxr-xr-x 1 postgres postgres 2.1M Aug 9 21:25 pdu14-rwxr-xr-x 1 postgres postgres 2.1M Aug 9 21:25 pdu15-rwxr-xr-x 1 postgres postgres 2.1M Aug 9 21:25 pdu16-rwxr-xr-x 1 postgres postgres 2.1M Aug 9 21:25 pdu17-rw-r--r-- 1 root root      17M Aug 30 05:34 PDU2.5.zip

修改pdu.ini 配置文件

[root@cjc-db-05 PDU]# vi pdu.ini #Postgresql 数据目录PGDATA=/pg/data#Postgresql 归档目录#ARCHIVE_DEST=/pg/archARCHIVE_DEST=/pg/other#dropScan 需要扫描的磁盘DISK_PATH=/dev/mapper/ol-root#dropScan 时跳跃的数据块数量,数值越小覆盖磁盘越全面,速度越慢BLOCK_INTERVAL=5

登录PDU:

[postgres@cjc-db-05 PDU]$ ./pdu17 ╔══════════════════════════════════════════════════════╗║  Copyright 2024-2025 ZhangChen. All rights reserved  ║║  PDU: PostgreSQL Data Unloader                       ║║  Version 2.5.0 (2025-08-09)                          ║╚══════════════════════════════════════════════════════╝  Current DB Supported Version:  ──────────────────────────  • PostgreSQL 17╔═══════════════════════════════════════════╗║              COMMUNITY VERSION            ║╠═══════════════════════════════════════════╣║ • Max 100000 Records Per Table (unload)   ║║ • Max 100000 Records Per Table (restore)  ║║ • Max 1 GB Per Table                      ║║ • Max 50 Columns Per Table                ║║ • Max 500 Object Per Schema               ║╚═══════════════════════════════════════════╝  Contact Me:  ───────────────────  • WeChat: x1987LJ2020929  • Email:  1109315180@qq.com  • Tel:    15251853831

查看PDU帮助信息

(随便输入一个不存在的命令,分号结尾)

PDU.public=# xxxxx;PDU数据拯救工具 | 命令帮助┌──────────────────────────────────────────────────────────────────────────────────────────────────┐  **基础操作**  b;                                      │ 初始化数据库元信息  <exit;>|<\q;>                           │ 退出工具  ----------------------------------------.--------------------------------  **数据库切换**  use <db>;                               │ 指定当前数据库(例: use logs;)  set <schema>;                           │ 指定当前模式(例: set recovery;)  ----------------------------------------.--------------------------------  **元数据展示**  \l;                                     │ 列出所有数据库  \dn;                                    │ 列出当前数据库所有模式  \dt;                                    │ 列出当前模式下的所有表  \d+ <table>;                            │ 查看表结构详情(例: \d+ users;)  \d <table>;                             │ 查看表列类型(例: \d users;)  ----------------------------------------.--------------------------------  **数据导出**  u|unload tab <table>;                   │ 导出表数据到CSV(例: unload tab orders;)  u|unload sch <schema>;                  │ 导出整个模式数据(例: unload sch public;)  u|unload ddl;                           │ 生成当前模式DDL语句文件  u|unload copy;                          │ 生成CSV的COPY语句脚本  ----------------------------------------.--------------------------------  **误操作数据恢复**  scan [t1|manual];                       │ 扫描误删表/从manual目录初始化元数据  restore del/upd [<TxID>|all];           │ 按事务号/时间区间恢复数据  add <filenode> <表名> <字段类型列表>;   │ 手动添加表信息(例: add 12345 t1 varchar,...)[!] 需将数据文件放入restore/datafile  restore db <库名> <路径>;               │ 初始化自定义数据库目录(例: restore db xmandb /home/...)  ----------------------------------------.--------------------------------  **Drop Table恢复**  dropscan/ds;                            │ 针对文件restore/tab.config中配置的表进行碎片扫描恢复  dropscan/ds repair;                     │ 针对此前扫描失败的TOAST表进行恢复  dropscan/ds clean;                      │ 删除restore/dropscan下的所有目录  dropscan/ds copy;                       │ 生成restore/dropscan下的所有表文件的COPY命令  ----------------------------------------.--------------------------------  **参数设置**  p|param startwal/endwal <WAL文件>;      │ 设置WAL扫描范围(默认归档目录首尾)  p|param starttime/endtime <时间>;       │ 设置时间扫描范围(例: 2025-01-01 00:00:00)  p|param resmode tx|time;                │ 设置恢复模式(事务号/时间区间)  p|param restype delete|update;          │ 设置恢复类型(删除/更新)  p|param exmode csv|sql;                 │ 设置导出格式(默认CSV)  p|param encoding utf8|gbk;              │ 设置字符编码(默认utf8)  reset <参数名>|all;                     │ 重置指定参数|所有参数  show;                                   │ 查看所有参数状态  t;                                      │ 查看当前支持的数据类型└──────────────────────────────────────────────────────────────────────────────────────────────────┘语法规则◈ 所有指令必须以`;`结尾

数据初始化

PDU.public=# b;开始初始化... -pg_database:</pg/data/global/1262>数据库:postgres       -pg_schema:</pg/data/base/5/2615>      -pg_class:</pg/data/base/5/1259> 共82行      -pg_attribute:</pg/data/base/5/1249> 共3126行      模式:        ▌ public 0张表数据库:cjc       -pg_schema:</pg/cjc/PG_17_202406281/16406/2615>      -pg_class:</pg/cjc/PG_17_202406281/16406/1259> 共83行      -pg_attribute:</pg/cjc/PG_17_202406281/16406/1249> 共3148行      模式:        ▌ public 0张表        ▌ cjc 1张表

切换数据库和模式:

PDU.public=# use cjc;┌────────────────────────────────────────┐│          模式             │  表数量    │├────────────────────────────────────────┤│    public                 │  0         ││    cjc                    │  1         │└────────────────────────────────────────┘cjc.public=# set cjc;  ┌──────────────────────────────────────────────────┐│               表名                  │  表大小    │├──────────────────────────────────────────────────┤│    t100                             │  0         │└──────────────────────────────────────────────────┘	仅显示表大小排名前 1 的表名

扫描被误删除的表

cjc.cjc=# scan t100;正在扫描表<t100>的删除记录...▌ 扫描归档目录   起始文件: 000000010000000000000001   终点文件: 00000001000000000000000C▌ 时间区间恢复模式 [按时间区间内全部显示]────────────────────────────────────────▌ 扫描结束,当前时间范围  开始: 2025-08-30 05:20:55.802215 CST  结束: 2025-08-30 18:40:36.241959 CST▌ 时间区间详情┌─────────────────────────────────────────────────────────┐ 开始时间: 2025-08-30 05:20:55.802215 CST 结束时间: 2025-08-30 18:40:36.241959 CST LSN: 0/0C0000F8 - 0/0C146710     建议startwal: 00000001000000000000000C     建议endwal: 00000001000000000000000C           -------------------.-------------------- ● 数据文件OID: 16417     ● Toast文件OID: 0 ● 该区间内删除的数据量: 10000 行└─────────────────────────────────────────────────────────┘  [!] 提示: 建议startwal仅表示该事务恢复时建议将startwal设置为该值            建议endwal表示该事务恢复时必须将startwal设置为该值,否则恢复可能会失败

restore误删除的数据

cjc.cjc=# restore del all;▌ 扫描归档目录   起始文件: 000000010000000000000001   终点文件: 00000001000000000000000C当前startwal设置和建议startwal之间差距较大导致恢复效率低下,是否确认执行?y/n  y▌ 事务恢复模式────────────────────────────────────────|-已解析数据条数: 10000▌ 解析完成┌───────────────────────────────────────────────────────────────┐ 表 t100 ● 恢复数据共计 10000 行 ● 成功: 10000      ● 失败: 0 ● 文件路径: restore/public/t100_del_2025-08-30 05:20:55.802215 CST_2025-08-30 18:40:36.241959 CST.csv└───────────────────────────────────────────────────────────────┘

查看恢复的数据,输出到csv文件

[postgres@cjc-db-05 public]$ pwd/pg/soft/PDU/restore/public
[postgres@cjc-db-05 public]$ cat t100_del_2025-08-30\ 05\:20\:55.802215\ CST_2025-08-30\ 18\:40\:36.241959\ CST.csv |wc -l10000
[postgres@cjc-db-05 public]$ more t100_del_2025-08-30\ 05\:20\:55.802215\ CST_2025-08-30\ 18\:40\:36.241959\ CST.csv 1 赵勇 财务部 代表 39113077759982 周娜 技术部 协调员 60163139879063 赵芳 研发部 助理 45149876370044 郑勇 运营部 协调员 44154107173215 吴芳 技术部 代表 64132250990676 钱洋 采购部 分析师 26149599713957 钱娜 市场部 代表 23173287707728 郑敏 采购部 协调员 54148222930709 张强 市场部 顾问 311161678322610 吴静 客服部 工程师 2718070893302......

重命名

mv t100_del_2025-08-30\ 05\:20\:55.802215\ CST_2025-08-30\ 18\:40\:36.241959\ CST.csv t100_del.csv

导入表数据

[postgres@cjc-db-05 scripts]$ psql -p 5432 -U cjc -W cjcPassword: psql (17.6)Type "help" for help.
cjc=# \c cjcPassword: You are now connected to database "cjc" as user "cjc".
cjc=# select * from t100; employee_id | name | department | position | age | phone -------------+------+------------+----------+-----+-------(0 rows)
cjc=# COPY cjc.t100 FROM '/pg/soft/PDU/restore/public/t100_del.csv';COPY 10000

恢复成功

cjc=# select count(*) from cjc.t100; count ------- 10000(1 row)
cjc=# select * from cjc.t100 limit 10; employee_id | name | department | position | age |    phone    -------------+------+------------+----------+-----+-------------           1 | 赵勇 | 财务部     | 代表     |  39 | 11307775998           2 | 周娜 | 技术部     | 协调员   |  60 | 16313987906           3 | 赵芳 | 研发部     | 助理     |  45 | 14987637004           4 | 郑勇 | 运营部     | 协调员   |  44 | 15410717321           5 | 吴芳 | 技术部     | 代表     |  64 | 13225099067           6 | 钱洋 | 采购部     | 分析师   |  26 | 14959971395           7 | 钱娜 | 市场部     | 代表     |  23 | 17328770772           8 | 郑敏 | 采购部     | 协调员   |  54 | 14822293070           9 | 张强 | 市场部     | 顾问     |  31 | 11616783226          10 | 吴静 | 客服部     | 工程师   |  27 | 18070893302(10 rows)

参考:

https://mp.weixin.qq.com/s/7YfjR611vfAbSbt4RUC_hg

欢迎关注我的公众号《IT小Chen》

Image
Image