
使用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
└───────────────────────────────────────────────────────────────┘
详细过程如下:
安装
环境说明:
OS:Oracle Linux Server 7.5
DB:PostgreSQL 17.6
创建用户,组,目录等
groupadd -g 1500 postgres
useradd -g 1500 -u 1500 postgres
passwd postgres
mkdir -p /pg/{app/17,data,log,conf,soft,arch}
chown postgres.postgres /pg -R
配置环境变量
su - postgres
vi /home/postgres/.bash_profile
export PGHOME=/pg/app/17/pgsql
export PATH=$PATH:$PGHOME/bin
export PGDATA=/pg/data
export PGLOG=/pg/log
export LD_LIBRARY_PATH=/pg/app/17/lib:$LD_LIBRARY_PATH
source /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.gz
cd /pg/soft/
tar -zxvf postgresql-17.6.tar.gz
mv 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.conf
listen_addresses = '*'
vi /pg/data/pg_hba.conf
# IPv4 local connections:
# host all all 127.0.0.1/32 trust
host all all 0.0.0.0/0 trust
启动:
[postgres@cjc-db-05 ~]$ pg_ctl -D /pg/data -l /pg/log/pg.log start
waiting for server to start.... done
server 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 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 = 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 smart
pg_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 0
drwx------ 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 cjc
Password:
psql (17.6)
Type "help" for help.
cjc=# \c cjc
Password:
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 10000
cjc=# 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/arch
ARCHIVE_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 -l
10000
[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 赵勇 财务部 代表 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
......
重命名
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 cjc
Password:
psql (17.6)
Type "help" for help.
cjc=# \c cjc
Password:
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》
1618

被折叠的 条评论
为什么被折叠?



