如何在PostgreSQL故障切换后找回丢失的数据

1. 背景

PostgreSQL的HA方案一般都基于其原生的流复制技术,支持同步复制和异步复制模式。 同步复制模式虽然可以最大程度保证数据不丢失,但通常需要至少部署三台机器,确保有两台以上的备节点。 因此很多一主一备HA集群,都是使用异步复制。

在异步复制下,主库宕机,把备节点切换为新的主节点后,可能会丢失最近更新的少量数据。 如果这些丢失的数据对业务比较重要,那么,能不能从数据库里找回来呢?

下面就介绍找回这些数据的方法

2. 原理

基本过程

  1. 备库被提升为新主后会产生一个新的时间线,这个新时间线的起点我们称之为分叉点。
  2. 旧主故障修复后,在旧主上从分叉点位置开始解析WAL文件,将所有已提交事务产生的数据变更解析成SQL。 前提是旧主磁盘没有损坏,能够正常启动。不过,生产最常见的故障是物理机宕机,一般重启机器就可以恢复。
  3. 业务拿到这些SQL,人工确认后,回补数据。

为了能从WAL记录解析出完整的SQL,最好wal_level设置成logical,并且表上有主键。 此时,对于我们关注的增删改DML语句,WAL记录中包含了足够的信息,能够把数据变更还原成SQL。 详细如下:

  • INSERT
    WAL记录中包含了完整的tuple数据,结合系统表中表定义可以还原出SQL。

  • UPDATE
    WAL记录中包含了完整的更新后的tuple数据,对于更新前的tuple,视以下情况而定。

    • 表设置了replica identity full属性
      WAL记录中包含完整的更新前的tuple数据
    • 表包含replica identity key(或主键)且replica identity key的值发生了变更
      WAL记录中包含了更新前的tuple的replica identity key(或主键)的字段值
    • 其他
      WAL记录中不包含更新前的tuple数据
  • DELETE
    WAL记录中可能包含被删除的tuple信息,视以下情况而定。

    • 表设置了replica identity full属性
      WAL记录中包含完整的被删除的tuple数据
    • 表包含replica identity key(或主键)
      WAL记录中包含被删除的tuple的replica identity key(或主键)的字段值
    • 其他 WAL记录中不包含被删除的tuple数据

如果wal_level不是logical或表上没有主键,还可以从WAL中的历史FPI(FULL PAGE IANGE)中解析出变更前tuple。

因此,原理上,从WAL解析出SQL是完全可行的。并且也已经有开源工具可以支持这项工作了。

3. 工具

使用改版的walminer工具解析WAL文件。

  • WalMiner: walminer是PostgreSQL WAL的解析工具,可将WAL解析成原始SQL和undo SQL生成。这个分支对原始walminer进行了一些增强

walminer是一款很不错的工具,可以从WAL文件中解析出原始SQL和undo SQL。 但是当前原生的walminer要支持这一场景还存在一些问题,并且解析WAL文件的速度非常慢。

改版的walminer分支增加了基于LSN位置的解析功能,同时修复了一些BUG,解析WAL文件的速度也提升了大约10倍。 其中的部分修改后续希望能合到walminer主分支里。

3. 前提条件

  1. 分叉点之后的WAL日志文件未被清除
    正常是足够的。也可以设置合理的wal_keep_segments参数,在pg_wal目录多保留一些WAL。比如:
    wal_keep_segments=100
    
    如果配置了WAL归档,也可以使用归档目录中的WAL。
  2. WAL日志级别设置为logical
    wal_level=logical
    
  3. 表有主键或设置了replica identity key/replica identity full
  4. 分叉点之后表定义没有发生变更

注:以上条件的2和3如果不满足其实也可以支持,但是需要保留并解析分叉点的前一个checkpint以后的所有WAL。

4. 使用演示

4.1 环境准备

搭建好一主一备异步复制的HA集群

机器:

  • node1(主)
  • node2(备)

软件:

  • PostgreSQL 10

参数:

wal_level=logical

4.2 安装walminer插件

从以下位置下载改版walminer插件源码

  • WalMiner: walminer是PostgreSQL WAL的解析工具,可将WAL解析成原始SQL和undo SQL生成。这个分支对原始walminer进行了一些增强

在主备库分别安装walminer

cd walminer
make && make install

在主库创建walminer扩展

create extension walminer

4.3 创建测试表

create table tb1(id int primary key, c1 text);
insert into tb1 select id,'xxx' from generate_series(1,10000) id;

4.4 模拟业务负载

准备测试脚本

test.sql

\set id1 random(1,10000)
\set id2 random(1,10000)insert into tb1 values(:id1,'yyy') on conflict (id)do update set c1=excluded.c1;delete from tb1 where id=:id2;

在主库执行测试脚本模拟业务负载

pgbench -c 8 -j 8 -T 1000 -f test.sql

4.5 模拟主库宕机

在主库强杀PG进程

killall -9 postgres

4.6 备库提升为新主

在备库执行提升操作

pg_ctl promote

查看切换时的时间线分叉点

[postgres@host2 ~]$tail -1 /pgsql/data10/pg_wal/00000002.history
1	0/EF76440	no recovery target specified

4.7 在旧主库找回丢失的数据

启动旧主库后调用wal2sql()函数,找回分叉点以后旧主库上已提交事务执行的所有SQL。

postgres=# select xid,timestamptz,op_text from wal2sql(NULL,'0/EF76440') ;
NOTICE:  Get data dictionary from current database.
NOTICE:  Wal file "/pgsql/data10/pg_wal/00000001000000000000000F" is not match with datadictionary.
NOTICE:  Change Wal Segment To:/pgsql/data10/pg_wal/00000001000000000000000C
NOTICE:  Change Wal Segment To:/pgsql/data10/pg_wal/00000001000000000000000D
NOTICE:  Change Wal Segment To:/pgsql/data10/pg_wal/00000001000000000000000Exid   |          timestamptz          |                           op_text                           
--------+-------------------------------+-------------------------------------------------------------938883 | 2020-03-31 17:12:10.331487+08 | DELETE FROM "public"."tb1" WHERE "id"=7630;938884 | 2020-03-31 17:12:10.33149+08  | INSERT INTO "public"."tb1"("id", "c1") VALUES(5783, 'yyy');938885 | 2020-03-31 17:12:10.331521+08 | DELETE FROM "public"."tb1" WHERE "id"=3559;938886 | 2020-03-31 17:12:10.331586+08 | UPDATE "public"."tb1" SET "c1" = 'yyy' WHERE "id"=7585;938887 | 2020-03-31 17:12:10.331615+08 | UPDATE "public"."tb1" SET "c1" = 'yyy' WHERE "id"=973;938888 | 2020-03-31 17:12:10.331718+08 | INSERT INTO "public"."tb1"("id", "c1") VALUES(7930, 'yyy');938889 | 2020-03-31 17:12:10.33173+08  | UPDATE "public"."tb1" SET "c1" = 'yyy' WHERE "id"=1065;938890 | 2020-03-31 17:12:10.331741+08 | INSERT INTO "public"."tb1"("id", "c1") VALUES(2627, 'yyy');938891 | 2020-03-31 17:12:10.331766+08 | UPDATE "public"."tb1" SET "c1" = 'yyy' WHERE "id"=1012;938892 | 2020-03-31 17:12:10.33178+08  | INSERT INTO "public"."tb1"("id", "c1") VALUES(4740, 'yyy');938893 | 2020-03-31 17:12:10.331814+08 | DELETE FROM "public"."tb1" WHERE "id"=4275;938894 | 2020-03-31 17:12:10.331892+08 | UPDATE "public"."tb1" SET "c1" = 'yyy' WHERE "id"=8651;938895 | 2020-03-31 17:12:10.33194+08  | UPDATE "public"."tb1" SET "c1" = 'yyy' WHERE "id"=9313;938896 | 2020-03-31 17:12:10.331967+08 | DELETE FROM "public"."tb1" WHERE "id"=3251;938897 | 2020-03-31 17:12:10.332001+08 | DELETE FROM "public"."tb1" WHERE "id"=2968;938898 | 2020-03-31 17:12:10.332025+08 | INSERT INTO "public"."tb1"("id", "c1") VALUES(5331, 'yyy');938899 | 2020-03-31 17:12:10.332042+08 | UPDATE "public"."tb1" SET "c1" = 'yyy' WHERE "id"=3772;938900 | 2020-03-31 17:12:10.332048+08 | INSERT INTO "public"."tb1"("id", "c1") VALUES(94, 'yyy');
(18 rows)Time: 2043.380 ms (00:02.043)

上面wal2sql()的输出结果是按事务在WAL中提交的顺序排序的。可以把这些SQL导到文件里提供给业务修单。

4.8 恢复旧主

可以通过pg_rewind快速回退旧主多出的数据,然后作为新主的备库重建复制关系,恢复HA。

5. 小结

借助改版的walminer,可以方便快速地在PostgreSQL故障切换后找回丢失的数据。

walminer除了能生成正向SQL,还可以生成逆向的undo SQL,也就是我们熟知的闪回功能。 undo SQL的生成方法和使用限制可以参考开源项目文档。

然而,在作为闪回功能使用时,walminer还有需要进一步改进的地方,最明显的就是解析速度。 因为从WAL记录中完整解析undo SQL需要开启replica identity full,而很多系统可能不会为每个表都打开replica identity full设置。 在没有replica identity full的前提下,生成undo SQL就必须要依赖历史FPI。

虽然改版的walminer已经在解析速度上提升了很多倍,但是如果面对几十GB的WAL文件,解析并收集历史所有FPI,资源和时间消耗仍然是个不小的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/863671.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

简单shell

目录 预备知识 fork 进程等待 wait waitpid 环境变量 概念 分类 常见的环境变量及其用途 环境变量的查看与设置 exec系列 函数解释 命名理解 简单shell 预备知识 fork fork 是 Linux 和许多其他类 Unix 系统中的一个重要系统调用,它用于创建一个新的…

双指针-旋转链表

目录 一、问题描述 二、解题思路 三、代码实现 四、刷题链接 一、问题描述 二、解题思路 1.先确定链表长度为len 2.注意当K>len时,如果每个节点都往右移动len个位置,等价于不移动,所以需要求KK%len。 3.所有元素右移K个位置&#xf…

uniapp运行到小程序Vue.use注册全局组件不起作用

真想吐槽一下小程序,uniapp运行到小程序使用Vue.use注册全局组件根本不起作用,也不报错,这只是其中一个问题,其他还有很多问题,比如vue中正常使用的没问题的语法,运行到小程序就不行,又是包太大…

【Python机器学习】自动化特征选择——单变量统计

添加更多特征会使所有的模型变得更加复杂,从而增大过拟合的可能性。 在添加新特征或处理一般的高位数据集时,最好将特征的数量减少到只包含最有用的那些特征,并删除其余特征,这样会得到泛化能力更好、更简单的模型。 对于如何判…

生成式人工智能和机器人技术是否即将取得最后的突破?

了解生成式人工智能与机器人技术的融合如何彻底改变从医疗保健到娱乐等行业 想象一下这样一个世界,机器人可以谱写交响乐、画出杰作、写出小说。这种创造力与自动化的迷人融合,由 生成式人工智能,不再是梦想;它正在以重大方式重塑…

1Panel开源面板项目GitHub Star数量突破20,000!

截至2024年6月25日9:00,FIT2CLOUD飞致云旗下开源项目——1Panel开源Linux服务器运维管理面板GitHub Star数超过20,000个! 继Halo和JumpServer之后,1Panel成为飞致云旗下第三个GitHub Star数量超过20,000个的开源项目,也是飞致云旗…

Python 类

文章目录 定义类与对象成员方法构造方法魔术方法私有成员继承复写父类成员调用父类成员 多态 定义 class 类名:成员变量成员方法变量类名()# 创建对象 变量.成员变量# 使用成员变量 变量.成员方法类与对象 类相当于设计图纸,规定了各种属性与行为。 对象也就是按照…

(单机架设教程)3D剑踪

前言 今天给大家带来一款单机游戏的架设:3D剑踪 如今市面上的资源参差不齐,大部分的都不能运行,本人亲自测试,运行视频如下: 3D剑踪 搭建教程 此游戏架设不需要虚拟机, 我们先解压 “3D剑踪.zip” &…

【Python】pycharm常用快捷键操作

目录 一.pycharm自定义修改快捷键 二.pycharm默认常用快捷键 一.pycharm自定义修改快捷键 在file-setting-keymap中可以修改快捷键,建议刚开始没特殊需求就不用修改,先熟悉系统默认的常用快捷键,但是以下情况可以考虑修改: 之前使用其他I…

因果解耦表征 | (香港理工ICLR24)联合学习个性化因果不变表示以应对异构联邦客户端

原文:Learning Personalized Causally Invariant Representations for Heterogeneous Federated Clients 地址:https://openreview.net/forum?id8FHWkY0SwF 代码:未知 出版:ICLR 2024 机构: 香港理工大学、香港科技大学 解读&…

JAVA期末速成库(12)第十三章

一、习题介绍 第十三章 Check Point:P501 13.3,13.17,13.28,13.29 Programming Exercise:13.1,13.6,13.11 二、习题及答案 Check Point: 13.3 True or false? a. An abst…

Nature Climate Change | 中国科学院地理资源所吴朝阳课题组发表生物多样性调控植被物候的研究成果!

本文首发于“生态学者”微信公众号! 植被春季物候对气候变化的响应通常是通过测量其温度敏感性(ST,温度每升高1度,植被提前展叶的天数)来量化。ST是植被在当地历史气候环境的选择压力下演化形成的最优策略,…

第一百三十四节 Java数据类型教程 - Java int数据类型

Java数据类型教程 - Java int数据类型 int数据类型是32位有符号Java原语数据类型。 int数据类型的变量需要32位内存。 其有效范围为-2,147,483,648至2,147,483,647(-231至231 - 1)。 此范围中的所有整数称为整数字面量。 例如,10&#xf…

Eclipse 悬浮提示:提升编程效率的利器

Eclipse 悬浮提示:提升编程效率的利器 引言 对于广大开发者而言,Eclipse 是一款功能强大的集成开发环境(IDE)。它不仅支持多种编程语言,还提供了丰富的插件和工具,以帮助开发者提高编程效率和代码质量。在本文中,我们将重点探讨 Eclipse 中的一个实用功能——悬浮提示…

刷算法Leetcode---7(二叉树篇)(前中后序遍历)

前言 本文是跟着代码随想录的栈与队列顺序进行刷题并编写的 代码随想录 好久没刷算法了,最近又开始继续刷,果然还是要坚持。 二叉树的题目比之前多了好多,就多分几次写啦~ 这是力扣刷算法的其他文章链接:刷算法Leetcode文章汇总 …

PyTorch读写模型(state_dict、torch.save、torch.load)

1. state_dict 在PyTorch中,state_dict 是一个简单的python的字典对象,将每一层与它的对应参数建立映射关系。(如model的每一层的weights及bias等) 首先,我们来定义一个MLP模型: import torch.nn as nnclass MLP(nn.Module):de…

494. 目标和 Medium

给你一个非负整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 或 - ,然后串联起所有整数,可以构造一个 表达式 : 例如,nums [2, 1] ,可以在 2 之前添加 ,在 1 之前添加 - ,然…

使用Calendar.add进行日期计算

使用Calendar.add进行日期计算 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将深入探讨在Java中如何使用Calendar.add方法进行日期计算。Calendar类是…

如何在Ubuntu20上离线安装joern(包括sbt和scala)

在Ubuntu 20上离线安装Joern,由于Joern通常需要通过互联网从其官方源或GitHub等地方下载,但在离线环境中,我们需要通过一些额外的步骤来准备和安装。(本人水平有限,希望得到大家的指正) 我们首先要做的就是…

在QGIS中调用天地图

2019年 1月 1日起,天地图 API及服务接口调用需要获得开发授权,之前使用 QGIS等 GIS软件无法继续调用天地图,这就需要申请一个许可。 一、注册并申请 Key 具体申请可以登录如下地址:https://www.tianditu.gov.cn打开上述网址后点…