摘要:Oracle数据库有时候不小心删除掉数据,想查询这些数据,或者恢复数据,就可以使用带有as of子句的select语句进行闪回查询。

PG粉有福了,下面介绍一种类似“闪回查询”插件 pg_dirtyread,可以读取未被vacuum的dead数据。

github主页:https://github.com/df7cb/pg_dirtyread

1.2 released:https://www.postgresql.org/message-id/20170923211004.uh27ncpjarkucrhd%40msg.credativ.de

一、我们一起看下官网的3个例子:

语法:

SELECT * FROM pg_dirtyread('tablename') AS t(col1 type1, col2 type2, ...);

样例1: 删除找回

 CREATE EXTENSION pg_dirtyread;
-- Create table and disable autovacuum
CREATE TABLE foo (bar bigint, baz text); ALTER TABLE foo SET (
autovacuum_enabled = false, toast.autovacuum_enabled = false
); --测试方便,先把自动vacuum关闭掉。 INSERT INTO foo VALUES (1, 'Test'), (2, 'New Test');
DELETE FROM foo WHERE bar = 1; SELECT * FROM pg_dirtyread('foo') as t(bar bigint, baz text);
bar │ baz
─────┼──────────
1 │ Test
2 │ New Test

可以看到, 被删除的记录(1, 'Test')已经可以查询到。

样例2:列被drop的情况

 CREATE TABLE ab(a text, b text);
INSERT INTO ab VALUES ('Hello', 'World'); ALTER TABLE ab DROP COLUMN b;
DELETE FROM ab; SELECT * FROM pg_dirtyread('ab') ab(a text, dropped_2 text);
a │ dropped_2
───────┼───────────
Hello │ World

可以看到,虽然b列被drop掉了,但是仍然可以读取到数据。

如何指定列:这里使用dropped_N来访问第N列,从1开始计数。

局限:由于PG删除了原始列的元数据信息,因此需要在表列名中指定正确的类型,这样才能进行少量的完整性检查。包括类型长度、类型对齐、类型修饰符,并且采取的是按值传递。

样例3:系统列

SELECT * FROM pg_dirtyread('foo')
AS t(tableoid oid, ctid tid, xmin xid, xmax xid, cmin cid, cmax cid, dead boolean,
bar bigint, baz text);
tableoid │ ctid │ xmin │ xmax │ cmin │ cmax │ dead │ bar │ baz
──────────┼───────┼──────┼──────┼──────┼──────┼──────┼─────┼───────────────────
41823 │ (0,1) │ 1484 │ 1485 │ 0 │ 0 │ t │ 1 │ Delete
41823 │ (0,2) │ 1484 │ 0 │ 0 │ 0 │ f │ 2 │ Insert
41823 │ (0,3) │ 1484 │ 1486 │ 0 │ 0 │ t │ 3 │ Update
41823 │ (0,4) │ 1484 │ 1488 │ 0 │ 0 │ f │ 4 │ Not deleted
41823 │ (0,5) │ 1484 │ 1489 │ 1 │ 1 │ f │ 5 │ Not updated
41823 │ (0,6) │ 1486 │ 0 │ 0 │ 0 │ f │ 3 │ Updated
41823 │ (0,7) │ 1489 │ 0 │ 1 │ 1 │ t │ 5 │ Not quite updated
41823 │ (0,8) │ 1490 │ 0 │ 2 │ 2 │ t │ 6 │ Not inserted

可以看到,xmax和ctid可以被恢复了。 oid只在11以及更早的版本中才能被恢复。

二、支持的版本

10和11已经支持,2.0以后的版本已经支持12和13,社区还是很活跃。

三、实现分析

核心代码有2部分:

1、dirtyread_tupconvert.c 主要实现了dirtyread_convert_tuples_by_name,通过列名进行元组转换,处理列原信息被清理以及存在表继承的情况,关键部分是数组:attrMap[],下标从1开始。

重点分析下dirtyread_do_convert_tuple

HeapTuple
dirtyread_do_convert_tuple(HeapTuple tuple, TupleConversionMap *map, TransactionId oldest_xmin)
{ /*
* Extract all the values of the old tuple, offsetting the arrays so that
* invalues[0] is left NULL and invalues[1] is the first source attribute;
* this exactly matches the numbering convention in attrMap.
*/
heap_deform_tuple(tuple, map->indesc, invalues + 1, inisnull + 1); //+1是因为是从下标1开始,从旧的元组中把数据的值获取到 /*
* Transpose into proper fields of the new tuple. 这部分是重点,在这里完成转换
*/
for (i = 0; i < outnatts; i++)
{
int j = attrMap; if (j == DeadFakeAttributeNumber)
//场景1:明确是dead,直接调用内核的函数HeapTupleIsSurelyDead即可,
//定义在tqual.c中,其它场景可以使用HeapTupleSatisfiesVacuum、HeapTupleSatisfiesMVCC等等,这里明确是dead,所以使用HeapTupleIsSurelyDead
{
outvalues = HeapTupleIsSurelyDead(tuple
, oldest_xmin);
outisnull = false;
}
else if (j < 0) //场景2:系统列,交给函数heap_getsysattr来处理。
outvalues = heap_getsysattr(tuple, j, map->indesc, &outisnull);
else
{ //场景3:最常见的场景,直接获取即可。
outvalues = invalues[j];
outisnull = inisnull[j];
}
} return heap_form_tuple(map->outdesc, outvalues, outisnull); //重新包装为tuple格式
}

2、pg_dirtyread.c 面向客户的接口在这里实现。

重点分析下 Datum pg_dirtyread(PG_FUNCTION_ARGS)

第1部分

   if (SRF_IS_FIRSTCALL()),这部分比较套路化
{
superuser校验
PG_GETARG_OID获取表的oid
heap_open打开表
get_call_result_type计算结果校验,不支持复合类型
BlessTupleDesc(tupdesc) 拿到表结构
usr_ctx->map = dirtyread_convert_tuples_by_name(usr_ctx->reltupdesc,
funcctx->tuple_desc, "Error converting tuple descriptors!"); //关键的一步,这里使用dirtyread_convert_tuples_by_name函数,。
heap_beginscan(usr_ctx->rel, SnapshotAny...),开始启动表扫描,这里使用了SnapshotAny
}

第2部分,不断的获取每一行,然后对每一行进行转换,直到扫描结束。

 if ((tuplein = heap_getnext(usr_ctx->scan, ForwardScanDirection)) != NULL)
{
if (usr_ctx->map != NULL)
{
tuplein = dirtyread_do_convert_tuple(tuplein, usr_ctx->map, usr_ctx->oldest_xmin);
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuplein));
}
else
SRF_RETURN_NEXT(funcctx, heap_copy_tuple_as_datum(tuplein, usr_ctx->reltupdesc));
}
else
{
heap_endscan(usr_ctx->scan); //结束扫描
heap_close(usr_ctx->rel, AccessShareLock); //关闭表
SRF_RETURN_DONE(funcctx);
}

整体上实现并不是很复杂,理解了这些后,就可以在此基础上增加自己的功能了。 而PG的魅力就在于此--架构的开放性,可以让开发者迅速地开发自己的“小程序”出来。

点击关注,第一时间了解华为云新鲜技术~

技术实践丨PostgreSQL插件之pg_dirtyread "闪回查询"的更多相关文章

  1. 【转】FlashBack总结之闪回查询与闪回表

    本文主要介绍利用UNDO表空间的闪回技术,主要包括:闪回表,闪回版本查询,闪回事务查询,闪回查询.这些闪回技术实现从回滚段中读取表中一定时间内操作过的数据,可用来进行数据比对,或者修正意外提交造成的错 ...

  2. 闪回查询(SELECT AS OF)

    使用Flashback Query的场景包括如下: 摘自官档 Recovering lost data or undoing incorrect, committed changes. For exa ...

  3. Oracle的回收站和闪回查询机制(二)

    上一篇中讲诉了Oracle中一些闪回查询(Flashback Query),这是利用回滚段信息来恢复一个或一些表到以前的一个时间点(一个快照).要注意的是,Flashback Query仅仅是查询以前 ...

  4. Oracle的回收站和闪回查询机制(一)

    实际工作中,我们经常会遇到一些情况,误删除某些表或某些表的某些记录,这时候就需要我们将这些记录重新插入进去.如何才能解决这个问题呢? Oracle的Flashback query(闪回查询)为我们解决 ...

  5. KingbaseES 的闪回查询

    KingbaseES V008R006C006B0013版本新增支持闪回查询,闪回版本查询.闪回表到指定时间点.旧版本已支持闪回回收站技术. 闪回技术(闪回查询和闪回表到指定时间点)可以通过时间戳和C ...

  6. 【练习】flushback基于时间的闪回查询

    1.创建table t1 :: SCOTT@ORA11GR2>create table t1 as select * from scott.emp; Table created. :: SCOT ...

  7. Oracle Flashback Technologies - 闪回查询

    Oracle Flashback Technologies - 闪回查询 查看表中,某行数据的修改记录 #创建一个表,并插入和修改数据 SQL> create table y3(id )); T ...

  8. oracle闪回查询

    一.引言 程序中用到需要同步oracle更新和删除数据,于是考虑利用oracle的闪回查询机制来实现. 利用该机制首先需要oracle启用撤销表空间自动管理回滚信息,并根据实际情况设置对数据保存的有效 ...

  9. Oracle 中利用闪回查询确定某表在某时间点之后的修改内容,并恢复至该时间点

    Oracle 中利用闪回查询确定某表在某时间点之后的修改内容: 1.查看 DELETE 及 UPDATE 操作修改的数据: SQL> SELECT * FROM tab AS OF TIMEST ...

  10. Flashback Query、Flashback Table(快速闪回查询、快速闪回表)

    Flashback Query闪回查询 flashback query是基于undo表空间的闪回,与之相关的参数如下: SQL> show parameter undo NAME         ...

随机推荐

  1. 【虹科干货】Redis Enterprise vs ElastiCache——如何选择缓存解决方案?

    使用Redis 或 Amazon ElastiCache 来作为缓存加速已经是业界主流的解决方案,二者各有什么优势?又有哪些区别呢? 为了提高 Web 应用程序和数据驱动服务的性能与效率,使用 Red ...

  2. LVS负载均衡概念+三种模式

    1.LVS负载均衡概念剖解: 1)LVS技术全称:Linux virtual Server ,Linux虚拟机服务器集群,1998由章文嵩博士设计.并且开源的,是中国最早的开源软件项目之一.   2) ...

  3. 市二模&中考游记

    市二模 过于久远,记不清了.当时由于不想写游记浪费备考时间所以没写. 然后就是我现在真的记不住,算了不写了( 大概是 140+150+135.5+89+68 吧. 中考 内容以各类奇闻逸事为主( \( ...

  4. 手算base64

    base64人类群星闪耀时 CSP2021考了-- 什么鬼-- 不得不大骂一声--您有毒吧 base64是什么 Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基 ...

  5. 如何使用markdown

    关于如何使用markdown写博客 markdown的语法 代码的插入 电脑Table建上面上面的键输入三个点``` 然后输入语言+回车 c语言中第一个程序 #include<stdio.h&g ...

  6. Codeforces Round 823 (Div. 2)C

    C. Minimum Notation 思路:我们可以进行的操作时将一个位置的数删除然后在任意位置处添加一个比当前数大1并且小于9的数,所以我们的操作只会让一个数变大,我们统计一个最大值的后缀,贪心的 ...

  7. 每天5分钟复习OpenStack(八)存储虚拟化

    KVM存储虚拟化是通过存储池(Storage Pool)和卷(Volume)来管理的.Storage Pool 是宿主机上可以看到的一片存储空间,可以是多种类型,Volume 是在 Storage P ...

  8. 殷浩详解DD系列

    第五讲:https://blog.csdn.net/Taobaojishu/article/details/115911833 内部有1-4讲链接

  9. Wampserver搭建DVWA和sqli-labs问题总结

    Wampserver 搭建 DVWA 和 sqli-labs 问题总结 遇到问题解决的思路方法 百度,博客去搜索相关的问题,人工智能 chatgpt 查看官方文档,查看注释. 本次解决方法就是在文档的 ...

  10. Calendar日历类型常见方法(必看!!)

    Hi i,m JinXiang 前言 本篇文章主要介绍Calendar日历类型的几种常见方法以及部分理论知识 欢迎点赞  收藏 留言评论 私信必回哟 博主收将持续更新学习记录获,友友们有任何问题可以在 ...