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

在这里插入图片描述
使用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/

image.png

解压

[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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值