PostgreSQL时间线(timeline)和History File的用法

说明:

在pg中,当我们进行了基于时间点的还原(PITR)后,数据库会启用新的时间线并继续进行操作。

但是,当我们进行基于时间点的还原后如果发现又出现错误,想要继续还原数据库该如何操作呢?如何还原到原先旧的时间线呢?

我们可以使用recovery_target_timeline参数来指定数据库还原到某一个时间线上。如果你还不清楚这个参数该如何使用,或者说压根不知道时间线是啥,那么请继续往下看。

PostgreSQL 时间线:

每当我们在数据库中完成一个事务时,所做的操作都会记录到$PGDATA/pg_wal目录下的wal日志文件中。

wal日志文件一般都是下面这种格式:

000000010000000000000001

当一个wal日志被写满后,便会创建新的wal日志000000010000000000000002,以此类推。

该文件中前8位,即:00000001表示的便是数据库的时间线。

从控制文件中也可以看到:

-bash-4.1$-> pg_controldata |grep TimeLineID
Latest checkpoint's TimeLineID: 1
Latest checkpoint's PrevTimeLineID: 1

每当我们进行基于时间点的还原后,时间线便会加1,并创建一个名为NewTimelineID.history的新文件。这个文件是干什么用的我们后面会介绍。

recovery_target_timeline是一个参数,它可以帮助我们将集群带入历史记录中的任何时间线,只要有效的基本备份和所有存档日志都到位。

我们来看看下面的例子:

首先,重新初始化一个新的数据库集群。

-bash-4.1$-> ls pg_wal

000000010000000000000001 archive_status

然后创建一张表并插入数据。

bill=# create table timeline(tid int, remarks varchar(1000));
CREATE TABLE
bill=# insert into timeline values('1','This is timeline id 1');
INSERT 0 1
bill=# checkpoint;
CHECKPOINT
bill=# select pg_switch_wal();
pg_switch_wal
---------------
0/15D4B70
(1 row)

刚刚插入的数据便记录在000000010000000000000001的wal日志中。

当wal日志写到000000010000000000000005时,进行一次完整的备份,接着再产生一些新的wal日志。

-bash-4.1$ ls -rlt
total 147460
-rw------- 1 postgres postgres 16777216 Nov 22 13:03 000000010000000000000001
-rw------- 1 postgres postgres 16777216 Nov 22 13:03 000000010000000000000002
-rw------- 1 postgres postgres 16777216 Nov 22 13:03 000000010000000000000003
-rw------- 1 postgres postgres 16777216 Nov 22 13:05 000000010000000000000004
-rw------- 1 postgres postgres 16777216 Nov 22 13:05 000000010000000000000005
-rw------- 1 postgres postgres 337 Nov 22 13:05 000000010000000000000005.00000028.backup
-rw------- 1 postgres postgres 16777216 Nov 22 13:06 000000010000000000000006
-rw------- 1 postgres postgres 16777216 Nov 22 13:06 000000010000000000000007

可以看到,现在最新的wal日志是000000010000000000000008

接着插入一条新的数据。

bill=# insert into timeline values('1','This is timeline id 1 after basebackup');
INSERT 0 1
bill=# checkpoint;
CHECKPOINT
-bash-4.1$ pg_waldump 000000010000000000000008 | grep INSERT
rmgr: Heap len (rec/tot): 54/ 214, tx:
487, lsn: 0/08000110, prev 0/080000D8, desc: INSERT off 2 flags 0x00,
blkref #0: rel 1663/13530/16384 blk 0 FPW

然后再产生几个wal日志,现在的情况如下:

-bash-4.1$ ls -rlt
total 311308
-rw------- 1 16777216 Nov 22 13:03 000000010000000000000001
-rw------- 1 16777216 Nov 22 13:03 000000010000000000000002
-rw------- 1 16777216 Nov 22 13:03 000000010000000000000003
-rw------- 1 16777216 Nov 22 13:05 000000010000000000000004
-rw------- 1 16777216 Nov 22 13:05 000000010000000000000005
-rw------- 1 337 Nov 22 13:05 000000010000000000000005.00000028.backup
-rw------- 1 16777216 Nov 22 13:06 000000010000000000000006
-rw------- 1 16777216 Nov 22 13:06 000000010000000000000007
-rw------- 1 16777216 Nov 22 13:07 000000010000000000000008
-rw------- 1 16777216 Nov 22 13:07 000000010000000000000009
-rw------- 1 16777216 Nov 22 13:09 00000001000000000000000A

如下图所示:

此时,在我插入第二条数据前,我想要把数据还原到000000010000000000000007这个点。

因此我在postgresql.conf文件中将恢复目标lsn设置为“ 0/07000060”。

接着进行还原,当我们还原之后,数据库切换到了新的时间线。

除此之外还有哪些改变呢?

恢复结束是指数据库打开进行写入的点。

创建了新的时间线的 history file文件,如00000002.history。

前一个时间线上的部分WAL文件已被新时间线的ID复制。

检查点记录写在新的时间线上。

日志中会记录下列信息:

LOG: starting point-in-time recovery to WAL location (LSN) "0/7000060"
LOG: restored log file "000000010000000000000005" from archive
LOG: redo starts at 0/5000028
LOG: consistent recovery state reached at 0/5000138
LOG: database system is ready to accept read only connections
LOG: restored log file "000000010000000000000006" from archive
LOG: restored log file "000000010000000000000007" from archive
LOG: recovery stopping after WAL location (LSN) "0/7000060"
LOG: pausing at the end of recovery
HINT: Execute pg_wal_replay_resume() to promote.

此时,PostgreSQL已在wal日志7处分支到新的时间线,并开始创建时间线ID为2的新wal日志。我们可以下wal日志目录下看到00000002.history文件。

该文件是可读文件,内容大致为:

1<parentTLI> 0/70000D8 <switchpoint> after LSN 0/7000060<reason>
parentTLI   ID of the parent timeline
switchpoint  XLogRecPtr of the WAL location where the switch happened
reason   human-readable explanation of why the timeline was changed

接下来,我向wal日志00000002000000000000000A (0/A000060)中插入新的数据。

bill=# insert into timeline values('2','This is timeline id 2 correct');

INSERT 0 1

以及另一个wal日志00000002000000000000000D(0/D000000)中插入另一条数据。

bill=# insert into timeline values('2','This is timeline id 2 wrong at 0/D000000');

INSERT 0 1

这个时候,我在00000002000000000000000D的wal日志中执行了错误的操作,想要回退到时间线2的00000002000000000000000C处,那么我要如何操作呢,如果像前面一样只指定lsn那么怎么保证不会回退到时间线1中呢?

这个时候我们便可以通过指定recovery_target_timeline来实现。

在postgresql.conf文件中添加:

recovery_target_timeline = '2'

recovery_target_lsn = '0/0C000060'

接着,启动数据库,可以看到日志中:

LOG: database system was interrupted; last known up at 2020-11-22 13:05:01 IST
LOG: restored log file "<span style="color: rgb(255, 0, 0);" data-mce-style="color: #ff0000;">00000002.history</span>" from archive
cp: cannot stat `/u02/archivelogs/00000003.history': No such file or directory
LOG: starting point-in-time recovery to WAL location (LSN) "0/C000060"
LOG: restored log file "00000002.history" from archive
LOG: restored log file "<span style="color: rgb(255, 0, 0);" data-mce-style="color: #ff0000;">000000010000000000000005</span>" from archive
LOG: redo starts at 0/5000028
LOG: consistent recovery state reached at 0/5000138
LOG: database system is ready to accept read only connections
LOG: restored log file "000000010000000000000006" from archive
LOG: restored log file "000000020000000000000007" from archive
LOG: restored log file "000000020000000000000008" from archive
LOG: restored log file "000000020000000000000009" from archive
LOG: restored log file "00000002000000000000000A" from archive
LOG: restored log file "00000002000000000000000B" from archive
LOG: restored log file "<span style="color: rgb(255, 0, 0);" data-mce-style="color: #ff0000;">00000002000000000000000C</span>" from archive
LOG: recovery stopping after WAL location (LSN) "<span style="color: rgb(255, 0, 0);" data-mce-style="color: #ff0000;">0/C000060</span>"
LOG: pausing at the end of recovery
HINT: Execute pg_wal_replay_resume() to promote.
..
LOG: redo done at 0/C000060
LOG: last completed transaction was at log time 2020-11-22 13:15:29.696929+05:30

然后查询该表验证:

bill=# select * from timeline;
 tid |  remarks
-----+-------------------------------
 1 | This is timeline id 1
 2 | This is timeline id 2 correct
(2 rows)

此时可以看到新建了00000003.history文件,该文件内容如下:

-bash-4.1$ cat 00000003.history
1 0/70000D8 after LSN 0/7000060
2 0/C0000D8 after LSN 0/C000060

我们不难发现:

history file这个文件中记录的就是这个时间线是从哪个WAL位置开始生成的。

补充:PostgreSQL promote过程 和 一主多备 时间线 无缝对接 详解

PostgreSQL的physical standby数据库的promote过程,数据库会在pg_xlog目录产生3个文件。

例如将备库1 promote,它将在pg_xlog目录产生如下文件:

A.partial  (xlog)
NEWTL_A (xlog)
NEWTL.history (history file)

例如备库1当前已接收到的XLOG位置是 00000001000000000000002D 文件中的某个位置 0/2D15D7D0,现在promote它 。

将会在pg_xlog目录中产生3个文件:

00000001000000000000002D.partial
00000002000000000000002D
  (00000001000000000000002D.partial 的内容会拷贝到 00000002000000000000002D)
00000002.history
   1  0/2D15D7D0  no recovery target specified

假设还有一个备库叫备库2,备库2如何能顺利的对接到已激活的备库1呢?

有个前提条件

备库2在TL1这条时间线上,还没有接收到00000001000000000000002D 这个文件。

把00000002.history拷贝到备库2的pg_xlog。

备库2会在应用完00000001000000000000002C后请求下一个时间线的 00000002000000000000002D 文件。

这样就能完美对接。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • Postgresql锁机制详解(表锁和行锁)

    表锁 LOCK [ TABLE ] [ ONLY ] name [ * ] [, ...] [ IN lockmode MODE ] [ NOWAIT ] lockmode包括以下几种: ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE| SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE LOCK TABLE命令用于获取一个表锁,获取过程将阻塞一直

  • Postgresql - 查看锁表信息的实现

    查看表锁信息,是DBA常用的脚本之一. 实验环境: CentOS 7 PG 10.4 先通过A窗口执行 mytest=# begin; BEGIN mytest=# update t1 set col1 = 'a' where id =1 ; UPDATE 1 mytest=# 打开B窗口执行 mytest=# begin; BEGIN mytest=# update t1 set col1 = 'b' where id =2; UPDATE 1 mytest=# update t1 set c

  • 基于postgresql行级锁for update测试

    创建表: CREATE TABLE db_user ( id character varying(50) NOT NULL, age integer, name character varying(100), roleid character varying, CONSTRAINT db_user_pkey PRIMARY KEY (id) ) 随便插入几条数据即可. 一.不加锁演示 1.打开一个postgreSQL的SQL Shell或pgAdmin的SQL编辑器窗口,执行: begin; s

  • 查看postgresql数据库用户系统权限、对象权限的方法

    PostgreSQL简介 PostgreSQL是一种特性非常齐全的自由软件的对象-关系型数据库管理系统(ORDBMS),是以加州大学计算机系开发的POSTGRES,4.2版本为基础的对象关系型数据库管理系统.POSTGRES的许多领先概念只是在比较迟的时候才出现在商业网站数据库中.PostgreSQL支持大部分的SQL标准并且提供了很多其他现代特性,如复杂查询.外键.触发器.视图.事务完整性.多版本并发控制等.同样,PostgreSQL也可以用许多方法扩展,例如通过增加新的数据类型.函数.操作符

  • postgresql synchronous_commit参数的用法介绍

    synchronous_commit 指定在命令返回"success"指示给客户端之前,一个事务是否需要等待 WAL 记录被写入磁盘. 合法的值是{local,remote_write,remote_apply,on,off} 默认的并且安全的设置是on. 不同于fsync,将这个参数设置为off不会产生数据库不一致性的风险:一个操作系统或数据库崩溃可能会造成一些最近据说已提交的事务丢失,但数据库状态是一致的,就像这些事务已经被干净地中止.因此,当性能比完全确保事务的持久性更重要时,关

  • 基于postgresql数据库锁表问题的解决

    查询是否锁表了 select oid from pg_class where relname='可能锁表了的表' select pid from pg_locks where relation='上面查出的oid' 如果查询到了结果,表示该表被锁 则需要释放锁定 select pg_cancel_backend(上面查到的pid) 补充:PostgreSQL 解决锁表.死锁问题 1.-- 查询ACTIVITY的状态等信息 SELECT T .PID, T.STATE, T.QUERY, T.WA

  • Postgresql创建新增、删除与修改触发器的方法

    新增触发器 第一步:创建函数 为待模糊查询的表创建函数 CREATE OR REPLACE FUNCTION fuzzy_query_func() RETURNS TRIGGER AS $$ BEGIN INSERT INTO fuzzy_query(id,name,address,table_name) VALUES (new.id, NEW.name,NEW.address,TG_TABLE_NAME); RETURN NEW; END; $$ LANGUAGE plpgsql; 第二步:创

  • PostgreSQL+Pgpool实现HA主备切换的操作

    PostgreSQL流复制实现HA主备切换 环境说明和主机规划 操作系统 主机名 主机 角色 端口 CentOS 7 master 10.0.0.11 PG-Master 54321 CentOS 7 slave 10.0.0.12 PG-Slave 54321 CentOS 7 pool 10.0.0.13 pgpool 54321 基础环境配置(所有主机操作) 配置HOSTS echo -e "10.0.0.11 master\n10.0.0.12 slave\n10.0.0.13 pool

  • PostgreSQL时间线(timeline)和History File的用法

    说明: 在pg中,当我们进行了基于时间点的还原(PITR)后,数据库会启用新的时间线并继续进行操作. 但是,当我们进行基于时间点的还原后如果发现又出现错误,想要继续还原数据库该如何操作呢?如何还原到原先旧的时间线呢? 我们可以使用recovery_target_timeline参数来指定数据库还原到某一个时间线上.如果你还不清楚这个参数该如何使用,或者说压根不知道时间线是啥,那么请继续往下看. PostgreSQL 时间线: 每当我们在数据库中完成一个事务时,所做的操作都会记录到$PGDATA/

  • Element Timeline时间线的实现

    目录 组件-时间线 ⾃定义节点样式 ⾃定义时间戳 组件-时间线 基础用法 <div class="block"> <div class="radio"> 排序: <el-radio-group v-model="reverse"> <el-radio :label="true">倒序</el-radio> <el-radio :label="false

  • WPF实现动画效果(三)之时间线(TimeLine)

    WPF动画效果系列 WPF实现动画效果(一)之基本概念 WPF实现动画效果(二)之From/To/By 动画 WPF实现动画效果(三)之时间线(TimeLine) WPF实现动画效果(四)之缓动函数 WPF实现动画效果(五)之关键帧动画 WPF实现动画效果(六)之路径动画 WPF实现动画效果(七)之演示图板 正文 时间线(TimeLine)表示时间段. 它提供的属性可以让控制该时间段的长度.开始时间.重复次数.该时间段内时间进度的快慢等等.在WPF中内置了如下几种TimeLine: Animat

  • 微信小程序实现Timeline时间线效果

    微信小程序实现类似elementUI的Timeline时间线效果,自适应页面与文本 wxml代码: <view class="box">   <view wx:for="{{list}}" wx:key="index" class="one">     <view class="onedot"></view>     <view wx:if="

  • vue时间线组件的使用方法

    本文实例为大家分享了vue时间线组件的具体实现代码,供大家参考,具体内容如下 效果 vue-时间线组件(时间轴组件)代码 <template> <ul class="timeline-wrapper"> <li class="timeline-item" v-for="t in timelineList" :key="t.id"> <div class="timeline-b

  • Android中使用itemdecoration实现时间线效果

    代码如下: // 时间线装饰器 public class TimeLineDecoration extends RecyclerView.ItemDecoration { private Paint mPaint; public TimeLineDecoration() { mPaint = new Paint(); mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(Color.BLUE); mPaint.setStrokeWidth(5);

  • Docker启动PostgreSQL时创建多个数据库的解决方案

    1 前言 在文章<Docker启动PostgreSQL并推荐几款连接工具>中我们介绍如何通过Docker来启动PostgreSQL,但只有一个数据库,如果想要创建多个数据库在同一个Docker容器上怎么办呢? 2 两种方案 一种方案是把shell/sql脚本放入/docker-entrypoint-initdb.d/目录中,让容器启动的时候自动执行创建:另一种是通过shell脚本指定创建,本质是一样的.这里只介绍第一种. 把shell脚本或sql脚本放入指定目录,就会自动执行,两种脚本都可以.

  • Android如何实现时间线效果(下)

    目录 1.前言 2.分析 2.1提出功能 2.2需求分析 2.3方案设想 3.编码 3.1第三版 3.2第四版 3.3最终版 1.前言 上回Android如何实现时间线效果 说到,小庄吭哧吭哧的撸完了需求,虽然功能上可以应付过去了,但他总觉得什么地方还可以再优化一下,可以搞一个较为通用的组件,顺便还能锻炼一下自己的编码设计能力,岂不美哉!一起看看他今天又要搞什么幺蛾子呗? 2.分析 2.1提出功能 这回小庄自己当上产品了,他上上下下的看了几遍这个时间线,亲自提出了几点不满可以优化的地方: 最好可

  • Android如何实现时间线效果

    目录 1.背景 2.分析 2.1功能分析 2.2细节分析 2.3方案设想 3.编码 3.1第一版 3.2第二版 4.结语 1.背景 这天下班前,老板找到小庄:"有个页面要优化,小需求,你跟进一下." 小庄:"好的老板!"他看了看时间,忐忑地翻出原型,看到了这样一个页面: 需要优化页面的原型图: 思索片刻后,小庄熟练地打开了某搜索引擎,没有找到合适的轮子,小庄知道软件开发的第一步必须是先进行需求分析和设计,而不是撸起袖子一把梭.于是他决定先分析下功能并整理思路. 2.

  • Python几种绘制时间线图的方法

    目录 Matplotlib 制作 Plotly 绘制 Excel 绘制 Matplotlib 制作 Matplotlib 作为 Python 家族最为重要的可视化工具,其基本的 API 以及绘制流程还是需要掌握的.尤其是该库的灵活程度以及作为众多工具的基础,重要性不言而喻 下面我们来看下该如何绘制一个时间线图表 导入库以及设置 XY 轴数据 import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] p

随机推荐