产品战略专家梁宁确认出席AICon北京站,分享AI时代下的商业逻辑与产品需求 了解详情
写点什么

PostgreSQL 误删数据怎么办?

  • 2019-08-28
  • 本文字数:3623 字

    阅读完需:约 12 分钟

PostgreSQL 误删数据怎么办?

本文经授权转载自 PostgreSQL 中文社区。


PostgreSQL 并未有闪回和数据误删除保护的功能,但是在一些场景下也可以实现。


此方法仅仅针对 DML 操作有效,DDL 操作(drop/truncate 等会将数据页面删除)无法找回数据。由于 PG 是多版本实现机制,因此数据仍然都在,只是不可见而已,vacuum_defer_cleanup_age 可以防止最近这些事务的元祖被删除,即保留这些事务操作的元组,可以闪回到这些操作的任意时间点

开始准备数据

test=# select pg_current_wal_lsn();
pg_current_wal_lsn
0/2003B58(1 row)
test=# create table lzzhang(id int);
CREATE TABLE
test=# insert into lzzhang values(1);
INSERT 0 1
test=# insert into lzzhang values(2);
INSERT 0 1
test=# insert into lzzhang values(3);
INSERT 0 1
test=# insert into lzzhang values(4);
INSERT 0 1
test=# insert into lzzhang values(5);
INSERT 0 1
test=# insert into lzzhang values(6);
INSERT 0 1
test=# select xmin, xmax, cmin, cmax, * from lzzhang;xmin | xmax | cmin | cmax | id——+——+——+——+—-
588 | 0 | 0 | 0 | 1
589 | 0 | 0 | 0 | 2
590 | 0 | 0 | 0 | 3
591 | 0 | 0 | 0 | 4
592 | 0 | 0 | 0 | 5
593 | 0 | 0 | 0 | 6
(6 rows)
复制代码

回滚 Insert

分析 redo,根据时间找到自己的错误操作,并找到对应的事务号


./pg_waldump -b -s 0/2003B58 -p dj
rmgr: Transaction len (rec/tot): 34/ 34, tx: 589, lsn: 0/0201A660, prev 0/0201A620, desc: COMMIT 2019-03-26 10:55:05.685536 CST
rmgr: Heap len (rec/tot): 59/ 59, tx: 590, lsn: 0/0201A688, prev 0/0201A660, desc: INSERT off 3
blkref #0: rel 1663/16392/16393 fork main blk 0
rmgr: Transaction len (rec/tot): 34/ 34, tx: 590, lsn: 0/0201A6C8, prev 0/0201A688, desc: COMMIT 2019-03-26 10:55:07.749260 CST
rmgr: Heap len (rec/tot): 59/ 59, tx: 591, lsn: 0/0201A6F0, prev 0/0201A6C8, desc: INSERT off 4
blkref #0: rel 1663/16392/16393 fork main blk 0
rmgr: Transaction len (rec/tot): 34/ 34, tx: 591, lsn: 0/0201A730, prev 0/0201A6F0, desc: COMMIT 2019-03-26 10:55:09.893856 CST
rmgr: Heap len (rec/tot): 59/ 59, tx: 592, lsn: 0/0201A758, prev 0/0201A730, desc: INSERT off 5
blkref #0: rel 1663/16392/16393 fork main blk 0
rmgr: Transaction len (rec/tot): 34/ 34, tx: 592, lsn: 0/0201A798, prev 0/0201A758, desc: COMMIT 2019-03-26 10:55:11.917570 CST
rmgr: Heap len (rec/tot): 59/ 59, tx: 593, lsn: 0/0201A7C0, prev 0/0201A798, desc: INSERT off 6
复制代码


此示例中,我们回滚 5 和 6 的数据。根据 wal 信息找到事务号为 592。


关闭数据库[lzzhang@lzzhang-pc bin]$ ./pg_resetwal -D dj -x 592启动数据库
复制代码


查询


test=# select xmin, xmax, cmin, cmax, * from lzzhang;
xmin | xmax | cmin | cmax | id------+------+------+------+----588 | 0 | 0 | 0 | 1589 | 0 | 0 | 0 | 2590 | 0 | 0 | 0 | 3591 | 0 | 0 | 0 | 4
复制代码


5/6 的数据已经没有了。增长事务号,看看情况!!!


test=# select * from txid_current(); txid_current--------------592(1 row)
复制代码


test=# select * from txid_current();txid_current-------------- 593(1 row)
复制代码


test=# select * from txid_current();txid_current--------------594
复制代码


test=# select xmin, xmax, cmin, cmax, * from lzzhang;xmin | xmax | cmin | cmax | id------+------+------+------+----588 |    0 |    0 |    0 |  1 589 |    0 |    0 |    0 |  2590 |    0 |    0 |    0 |  3591 |    0 |    0 |    0 |  4592 |    0 |    0 |    0 |  5593 |    0 |    0 |    0 |  6
复制代码


由于 5/6 仍然在数据库中,所以 5/6 又可见了。

回滚 delete

删除数据


test=# select pg_current_wal_lsn();pg_current_wal_lsn--------------------0/3000190(1 row)

复制代码


test=# delete from lzzhang where id = 5 or id = 6;DELETE 2
复制代码


找到事物号(595)


rmgr: Heap        len (rec/tot):     59/   299, tx:        595, lsn: 0/030001B8, prev 0/03000180, desc: DELETE off 5 KEYS_UPDATED , blkref #0: rel 1663/16392/16393 blk 0 FPW
复制代码


rmgr: Heap        len (rec/tot):     54/    54, tx:        595, lsn: 0/030002E8, prev 0/030001B8, desc: DELETE off 6 KEYS_UPDATED , blkref #0: rel 1663/16392/16393 blk 0
复制代码


rmgr: Transaction len (rec/tot):     34/    34, tx:        595, lsn: 0/03000320, prev 0/030002E8, desc: COMMIT 2019-03-26 11:00:23.410557 CST
复制代码


回滚数据


关闭数据库[lzzhang@lzzhang-pc bin]$ ./pg_resetwal -D dj -x 595启动数据库
复制代码


查看数据


test=# select xmin, xmax, cmin, cmax, * from lzzhang;xmin | xmax | cmin | cmax | id------+------+------+------+----588 |    0 |    0 |    0 |  1589 |    0 |    0 |    0 |  2590 |    0 |    0 |    0 |  3591 |    0 |    0 |    0 |  4592 |  595 |    0 |    0 |  5593 |  595 |    0 |    0 |  6
复制代码


提升事务号,5/6 又被删除


test=# select * from txid_current();txid_current--------------595(1 row)
复制代码


test=# select * from txid_current();txid_current--------------596(1 row)
复制代码


test=# select xmin, xmax, cmin, cmax, * from lzzhang;xmin | xmax | cmin | cmax | id------+------+------+------+----588 |    0 |    0 |    0 |  1589 |    0 |    0 |    0 |  2590 |    0 |    0 |    0 |  3591 |    0 |    0 |    0 |  4(4 rows)
复制代码

回滚 update

回滚 update 的操作类似,就不在讲述。

回滚 drop table

回滚后表可见,但是数据已经没有了 test=# select * from zz;ERROR: could not open file “base/16392/16396”: 没有那个文件或目录根据你的需要回滚/闪回指定操作,然后使用 txid_current()提升事物号,并不影响数据库整体的可用性。

确定数据仍然存在

安装 pageinspect 插件

create extension pageinspect

检查数据是否存在

查看目前表中的内容


test=# select xmin, xmax, cmin, cmax, * from lzzhang;xmin | xmax | cmin | cmax | id——+——+——+——+—–588 | 0 | 0 | 0 | 1589 | 0 | 0 | 0 | 2590 | 0 | 0 | 0 | 3597 | 0 | 0 | 0 | 100
复制代码


查看表的 page 内容(无效/不可见的元组都可以查看到)


test=# select * from heap_page_items(get_raw_page(‘lzzhang’, ‘main’, 0));lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid | t_data—-+——–+———-+——–+——–+——–+———-+——–+————-+————+——–+——–+——-+————1 | 8160 | 1 | 28 | 588 | 0 | 0 | (0,1) | 1 | 2304 | 24 | | | \x010000002 | 8128 | 1 | 28 | 589 | 0 | 0 | (0,2) | 1 | 2304 | 24 | | | \x020000003 | 8096 | 1 | 28 | 590 | 0 | 0 | (0,3) | 1 | 2304 | 24 | | | \x030000004 | 8064 | 1 | 28 | 591 | 597 | 0 | (0,7) | 16385 | 1280 | 24 | | | \x040000005 | 8032 | 1 | 28 | 592 | 595 | 0 | (0,5) | 8193 | 1280 | 24 | | | \x050000006 | 8000 | 1 | 28 | 593 | 595 | 0 | (0,6) | 8193 | 1280 | 24 | | | \x060000007 | 7968 | 1 | 28 | 597 | 0 | 0 | (0,7) | 32769 | 10496 | 24 | | | \x64000000
复制代码


vacuum 后查看 page 数据 vacuum lzzhang;


test=# select * from heap_page_items(get_raw_page(‘lzzhang’, ‘main’, 0));lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid | t_data—-+——–+———-+——–+——–+——–+———-+——–+————-+————+——–+——–+——-+————1 | 8160 | 1 | 28 | 588 | 0 | 0 | (0,1) | 1 | 2304 | 24 | | | \x010000002 | 8128 | 1 | 28 | 589 | 0 | 0 | (0,2) | 1 | 2304 | 24 | | | \x020000003 | 8096 | 1 | 28 | 590 | 0 | 0 | (0,3) | 1 | 2304 | 24 | | | \x030000004 | 7 | 2 | 0 | | | | | | | | | |5 | 0 | 0 | 0 | | | | | | | | | |6 | 0 | 0 | 0 | | | | | | | | | |7 | 8064 | 1 | 28 | 597 | 0 | 0 | (0,7) | 32769 | 10496 | 24 | | | \x64000000(7 rows)
复制代码


4/5/6 已经被 vacuum 掉,通过 waldump 可以找到对应的事务号的元祖已经不在,所有无法找回。


作者简介:


张连壮,多年 PostgreSQL 数据库内核研发经验,高可用/数据复制方面经验较为丰富,目前主要从事分布式数据库 Citus 相关工作,CitusDB 中国【站主】专注于 Citus 技术分享的全信息平台。


原文链接:


https://mp.weixin.qq.com/s/FbA8haWVOwRcw4CKNP0Vig


2019-08-28 11:336090

评论

发布
暂无评论
发现更多内容

让前端小姐姐愉快地开发表单

蛋先生DX

vue.js 表单 动态表单 6月日更

你应该知道的数仓安全

华为云开发者联盟

数据加密 数仓安全 透明加密 SQL函数加密

【Vue2.x 源码学习】第十四篇 - 生成 ast 语法树 - 模板解析

Brave

源码 vue2 6月日更

Pandas高级教程之:Dataframe的重排和旋转

程序那些事

Python 数据分析 pandas 程序那些事

Apache Drill 1.19.0 发布集锦

MPP Apache Drill 联邦查询和分析

EasyRecovery用法进阶--高阶设置使用技巧

淋雨

数据恢复 EasyRecovery 文件恢复 硬盘恢复

并发王者课-黄金3:雨露均沾-不要让你的线程在竞争中被“饿死”

MetaThoughts

Java 多线程 并发 并发王者课

算法之异位词字符处理

Skysper

算法

Java中生成随机数的4种方式!

王磊

Java 随机数

云图说|华为云自研云数据库GaussDB NoSQL,兼容多款NoSQL接口的数据库服务

华为云开发者联盟

nosql Influxdb Cassandra mongo GaussDB NoSQL

[译] Android 的 Java 9,10,11,12 的支持

Antway

6月日更

ONES CTO 冯斌 | 升级项目管理体系,加速金融行业数字化转型

万事ONES

项目管理 研发管理 数字经济 ONES

【译】JavaScript 代码整洁之道-概述篇

KooFE

JavaScript 大前端 6月日更 整洁代码

缓存的世界Redis(一)

卢卡多多

redis 缓存 6月日更

网络安全逐渐成为程序员的必备技能

学神来啦

Linux 程序员 安全 渗透

Java 字符串 split 踩坑记

mghio

6月日更

【LeetCode】山脉数组的峰顶索引Java题解

Albert

算法 LeetCode 6月日更

EMQ映云科技边缘计算里程碑—Kuiper加入LF Edge基金会

EMQ映云科技

开源 云端 边缘流式数据 emq LF Edge

【21-7】Grafana如何配置Thanos 查询历史数据

耳东@Erdong

Grafana Prometheus Thanos query 6月日更

你有一份Rx编程秘籍请签收

vivo互联网技术

响应式编程 大前端 函数编程 RXJS

ARTS 打卡第11周

steve_lee

详解Vue八大生命周期钩子函数

华为云开发者联盟

Vue 对象 函数 Vue实例 八大生命周期

极光开发者周刊【No.0611】

极光JIGUANG

react源码解析12.状态更新流程

全栈潇晨

React

推荐学Java——应该了解的前端内容

逆锋起笔

Java 大前端 后端 JAVA开发

Go 语言学习路线来啦

roseduan

学习 Go 语言

【Flutter 专题】127 图解基础 Image 小组件

阿策小和尚

Flutter 小菜 0 基础学习 Flutter Android 小菜鸟 6月日更

浪潮云说丨云应用容灾:四大关键能力,护航业务连续性

云计算

docker搭建Nginx和Tomcat负载均衡

叫我阿柒啊

nginx Docker 负载均衡

ONES CTO 冯斌 | 大型软件研发团队如何实践高效项目管理?

万事ONES

团队管理 ONES Project 研发团队

什么是Python中的套接字编程?

华为云开发者联盟

Python 编程 socket 网络 套接字

PostgreSQL 误删数据怎么办?_数据库_张连壮_InfoQ精选文章