
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_PATHsource /home/postgres/.bash_profile
下载安装介质
https://www.postgresql.org/ftp/source/v17.6/

解压
[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.gzcd /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/./configuremake && make installmv /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 ~]$ psqlpsql (17.6)Type "help" for help.
版本
postgres=# select version();version-------------------------------------------------------------------------------------------------------------PostgreSQL 17.6 on x86_64-pc-linux-gnu, compiled by gcc (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 = onarchive_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;CHECKPOINTpostgres=# 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/datafilerestore 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 CSTLSN: 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.csv1 赵勇 财务部 代表 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 | 113077759982 | 周娜 | 技术部 | 协调员 | 60 | 163139879063 | 赵芳 | 研发部 | 助理 | 45 | 149876370044 | 郑勇 | 运营部 | 协调员 | 44 | 154107173215 | 吴芳 | 技术部 | 代表 | 64 | 132250990676 | 钱洋 | 采购部 | 分析师 | 26 | 149599713957 | 钱娜 | 市场部 | 代表 | 23 | 173287707728 | 郑敏 | 采购部 | 协调员 | 54 | 148222930709 | 张强 | 市场部 | 顾问 | 31 | 1161678322610 | 吴静 | 客服部 | 工程师 | 27 | 18070893302(10 rows)
参考:
https://mp.weixin.qq.com/s/7YfjR611vfAbSbt4RUC_hg
欢迎关注我的公众号《IT小Chen》






