当前位置: 首页 > news >正文

从 PostgreSQL 中挽救损坏的表

~/tmp-dir.dab4fd85-8b47-4d9a-b15c-18312ef61075 pg_dump -U postgres -h locathost www_p1 > wow_p1.sql

pg_dump:错误:转储表 “page_views” 的内容失败:PQgetResult() 失败。
pg_dump:详细信息:来自服务器的错误消息:ERROR: relation base/16384/16417 的块 31869 中的页面无效
pg_dump:详细信息:命令为:COPY public.page_views (page_view_id, visited_at, hostname, ip, method, endpoint, user_id, xhr) TO stdout;

警告:千万不要这样做…… 实际上任何时候都不要,尤其是在磁盘有故障的服务器上。这里是在磁盘正常但 Postgres 块损坏的服务器上进行的操作。

在我的专业工作和家庭实验室中,我花了大量时间尝试学习和实施 “正确” 或 “可靠” 的解决方案 —— 高可用部署、自动化和经过测试的备份、基础设施即代码等等。

但这次不是。

这是一种非常粗暴、毫无顾忌、绝对疯狂的做法,如果你在任何重要的环境中工作,你应该阅读本文并聘请专业人员。

由于一些不重要的原因,我一直在处理家庭实验室中 Postgres 服务器上的数据损坏问题。服务器有几次非正常关闭,导致磁盘数据损坏。因为没有什么比临时解决方案更持久的了,所以这台服务器没有备份。

对于大多数数据,我能够使用 pg_dump 转储模式和数据,并将其重新导入到新的 Postgres 服务器中(是的,新服务器现在已经配置了备份)。

pg_dump -U postgres -h localhost my_database > my_database.sql

但是,对于有损坏表的数据库,pg_dump 会因这个令人不安的错误而失败:

pg_dump -U postgres -h localhost www_p1 > www_p1.sql
pg_dump: error: 转储表“page_views”的内容失败:PQgetResult() 失败。
pg_dump: detail: 来自服务器的错误消息:ERROR: relation base/16384/16417 的块 31869 中的页面无效
pg_dump: detail: 命令为:COPY public.page_views (page_view_id, visited_at, hostname, ip, method, endpoint, user_id, xhr) TO stdout;

(…… 是的,那是我的个人网站的数据库。👀)令我有些惊讶的是,我找不到很多关于如何 “尽力” 从损坏的 Postgres 表中恢复数据的详细信息或策略,所以就有了这篇文章。

幸运的是,由于损坏是由 Postgres 非正常退出而不是物理磁盘故障引起的,它只影响了当时频繁写入的表。在这种情况下,就是 sessions 表和 page_views 表。sessions 表完全可以丢弃 —— 我在新服务器上重新创建了一个空表,然后就不管它了。

如果我丢失了 page_views 表,也不是世界末日,但表中记录了大约 650 万条历史页面浏览量,丢失它们还是挺可惜的。所以…… 让我们做些冒险的事情。

我的目标不是恢复整个表。如果是这个目标,我就会停下来聘请专业人员了。相反,我的目标是尽可能多地恢复表中的行。

pg_dump 失败的一个原因是它试图使用游标读取数据,当 Postgres 的基本假设被违反时(例如磁盘块中的坏数据、无效索引),游标读取会失败。

我的策略是在损坏的服务器上创建一个具有相同模式的第二个表,然后逐个遍历 page_views 表中的每一行,并将它们插入到干净的表中,跳过磁盘块中有坏数据的行。要感谢这个 Stack Overflow 答案给了我这个策略的大致启发。

CREATE OR REPLACE PROCEDURE pg_recover_proc()
LANGUAGE plpgsql AS $$
DECLAREcnt BIGINT := 0;
BEGIN-- 从 page_views 表中获取最大的 page_view_idcnt := (SELECT MAX(page_view_id) FROM page_views);-- 按 page_view_id 降序遍历 page_views 表LOOPBEGIN-- 将当前 page_view_id 的行插入到 page_views_recovery 表中INSERT INTO page_views_recoverySELECT * FROM page_views WHERE page_view_id = cnt and entrypoint is not null;-- 递减计数器cnt := cnt - 1;-- 当 cnt < 1 时退出循环EXIT WHEN cnt < 1;EXCEPTIONWHEN OTHERS THEN-- 处理异常(例如数据损坏)IF POSITION('block' in SQLERRM) > 0 OR POSITION('status of transaction' in SQLERRM) > 0 OR POSITION('memory alloc' in SQLERRM) > 0 OR POSITION('data is corrupt' in SQLERRM) > 0 OR POSITION('MultiXactId' in SQLERRM) > 0 THENRAISE WARNING 'PGR_SKIP: %', cnt;cnt := cnt - 1;CONTINUE;ELSERAISE;END IF;END;IF MOD(cnt, 500) = 0 THENRAISE WARNING 'PGR_COMMIT: %', cnt;COMMIT;END IF;END LOOP;
END;
$$;

这里有一些巧妙但又很糟糕的做法。在现代版本的 Postgres 中,存储过程可以通过重复调用 COMMIT 来定期提交正在进行的顶级事务。我在这里(滥用)这个功能,以便在过程运行过程中,如果失败了,已经恢复的行能够被刷新到新表中。

我对与损坏数据相关的错误消息进行了一些粗略的字符串分析,如果是这种情况就跳过当前行。另一个有趣的边界情况:有几次,我遇到了向恢复表中插入数据失败的情况,因为对损坏表的 SELECT 查询返回了 null 值,尽管从技术上讲这是不可能的。我告诉过你我们在这里违反了 Postgres 的一些基本假设。在一个不同的非空列上添加 is not null 有助于避免这种情况。

我最初编写的这个过程是为了持续循环并跳过由磁盘损坏引起的致命错误(错误处理程序中的各种粗糙的 POSITION 检查)。

然而,很快我就遇到了一个新错误:

SQL Error [57P03]FATAL: the database system is in recovery mode

原来,如果你一直故意迫使 Postgres 尝试从损坏的磁盘块中读取数据,最终它的内部数据结构会进入不一致状态,服务器进程会出于安全原因自动重启。

这显然是个问题,因为我们无法捕获这个情况并强制过程继续运行。所以我转而添加 IF 条件来手动跳过导致服务器进程崩溃的主键区域。(我告诉过你这很疯狂。)

每次服务器崩溃时,我都会导出到目前为止恢复的行,以防万一:

pg_dump -U postgres -h localhost --table page_views2 www_p1 > page_views2-1.sql

然后我会跳过一个新的主键区域,删除并重新创建恢复表,然后再试一次。为什么要删除并重新创建它呢?因为我发现当服务器进程崩溃时,它偶尔会向恢复表中写入坏数据,这显然是不行的:

pg_dump: error: 转储表“page_views_recovery”的内容失败:PQgetResult() 失败。
pg_dump: detail: 来自服务器的错误消息:ERROR: 无效的内存分配请求大小 18446744073709551613
pg_dump: detail: 命令为:COPY public.page_views_recovery (page_view_id, visited_at, hostname, ip, method, endpoint, user_id, xhr) TO stdout;

可以预见,手动做这些事情变得非常烦人,所以我做了任何一个优秀的 Linux 极客都会做的事情 —— 为它编写了一个脚本,你可以在这里找到它。要点如下:

./pg-recover.sh postgres localhost www_p1 page_views page_view_id entrypoint

在损坏的表中的 6,628,903 行数据中,我成功恢复了 6,444,118 行。正如人们所说 —— 如果它很愚蠢但却有效,那它仍然是愚蠢的,而你只是幸运罢了。

相关文章:

从 PostgreSQL 中挽救损坏的表

~/tmp-dir.dab4fd85-8b47-4d9a-b15c-18312ef61075 pg_dump -U postgres -h locathost www_p1 > wow_p1.sqlpg_dump&#xff1a;错误&#xff1a;转储表 “page_views” 的内容失败&#xff1a;PQgetResult() 失败。pg_dump&#xff1a;详细信息&#xff1a;来自服务器的错误…...

【Vue3 入门到实战】1. 创建Vue3工程

目录 ​编辑 1. 学习目标 2. 环境准备与初始化 3. 项目文件结构 4. 写一个简单的效果 5. 总结 1. 学习目标 (1) 掌握如何创建vue3项目。 (2) 了解项目中的文件的作用。 (3) 编辑App.vue文件&#xff0c;并写一个简单的效果。 2. 环境准备与初始化 (1) 安装 Node.js 和 …...

rtthread学习笔记系列(10/11) -- 系统定时器

文章目录 10. 系统定时器10.1 跳跃表[定时器跳表 (Skip List) 算法](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/timer/timer?id定时器跳表-skip-list-算法) 10.2 硬件定时器10.2.1 初始化&&删除10.2.2 sta…...

mock服务-通过json定义接口自动实现mock服务

go-mock介绍 不管在前端还是后端开发过程中&#xff0c;当我们需要联调其他服务的接口&#xff0c;而这个服务还没法提供调用时&#xff0c;那我们就要用到mock服务&#xff0c;自己按接口文档定义一个临时接口返回指定数据&#xff0c;以供本地开发联调测试。 怎么快速启动一…...

像JSONDecodeError: Extra data: line 2 column 1 (char 134)这样的问题怎么解决

问题介绍 今天处理返回的 JSON 的时候&#xff0c;出现了下面这样的问题&#xff1a; 处理这种问题的时候&#xff0c;首先你要看一下当前的字符串格式是啥样的&#xff0c;比如我查看后发现是下面这样的&#xff1a; 会发现这个字符串中间没有逗号&#xff0c;也就是此时的J…...

C#版 软件开发6大原则与23种设计模式

开发原则和设计模式一直是软件开发中的圣经, 但是这仅仅适用于中大型的项目开发, 在小型项目的开发中, 这些规则会降低你的开发效率, 使你的工程变得繁杂. 所以只有适合你的才是最好的. 设计模式六大原则1. 单一职责原则&#xff08;Single Responsibility Principle&#xff0…...

java8 springboot 集成javaFx 实现一个客户端程序

1. 先创建一个springboot 程序(此步骤不做流程展示) 2. 更改springboot的版本依赖和导入所需依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.7</versio…...

MySQL(高级特性篇) 06 章——索引的数据结构

一、为什么使用索引 索引是存储引擎用于快速找到数据记录的一种数据结构&#xff0c;就好比一本教科书的目录部分&#xff0c;通过目录找到对应文章的页码&#xff0c;便可快速定位到需要的文章。MySQL中也是一样的道理&#xff0c;进行数据查找时&#xff0c;首先查看查询条件…...

PanWeidb-使用BenchmarkSQL对磐维数据库进行压测

本文提供PanweiDb使用BenchmarkSQL进行性能测试的方法和测试数据报告。 BenchmarkSQL,一个JDBC基准测试工具,内嵌了TPC-C测试脚本,支持很多数据库,如PostgreSQL、Oracle和Mysql等。 TPC-C是专门针对联机交易处理系统(OLTP系统)的规范,一般情况下我们也把这类系统称为业…...

AR 在高校实验室安全教育中的应用

AR应用APP可以内置实验室安全功能介绍&#xff0c;学习并考试&#xff08;为满足教育部关于实验室人员准入条件&#xff09;&#xff0c;AR主模块。其中AR主模块应该包括图形标识码的扫描&#xff0c;生成相应模型&#xff0c;或者火灾、逃生等应急处置的路线及动画演示。考试采…...

微信小程序实现个人中心页面

文章目录 1. 官方文档教程2. 编写静态页面3. 关于作者其它项目视频教程介绍 1. 官方文档教程 https://developers.weixin.qq.com/miniprogram/dev/framework/ 2. 编写静态页面 mine.wxml布局文件 <!--index.wxml--> <navigation-bar title"个人中心" ba…...

Spring Boot中的配置文件有哪些类型

在 Spring Boot 中&#xff0c;配置文件用于管理应用程序的设置和参数&#xff0c;通常存放在项目的 src/main/resources 目录下。Spring Boot 支持多种类型的配置文件&#xff0c;并通过这些文件来控制应用的行为和环境配置。 1. application.properties application.proper…...

Spring Boot 项目启动后自动加载系统配置的多种实现方式

Spring Boot 项目启动后自动加载系统配置的多种实现方式 在 Spring Boot 项目中&#xff0c;可以通过以下几种方式实现 在项目启动完成后自动加载系统配置缓存操作 的需求&#xff1a; 1. 使用 CommandLineRunner CommandLineRunner 是一个接口&#xff0c;可以用来在 Spring…...

如何在 CentOS 中生成 CSR

在本教程中&#xff0c;我们将向您展示如何在CentOS 7和6中生成CSR。您可以直接从服务器生成 CSR。 只需按照以下步骤操作&#xff1a; 第 1 步&#xff1a;使用安全外壳 &#xff08;SSH&#xff09; 登录您的服务器 步骤 2&#xff1a;创建私钥和 CSR 文件 在提示符处键入以…...

qml XmlListModel详解

1、概述 XmlListModel是QtQuick用于从XML数据创建只读模型的组件。它可以作为各种view元素的数据源&#xff0c;比如ListView、GridView、PathView等&#xff1b;也可以作为其他和model交互的元素的数据源。通过XmlRole定义角色&#xff0c;如name、age和height&#xff0c;并…...

C++并发编程之跨应用程序与驱动程序的单生产者单消费者队列

设计一个单生产者单消费者队列&#xff08;SPSC队列&#xff09;&#xff0c;不使用C STL库或操作系统原子操作函数&#xff0c;并且将其放入跨进程共享内存中以便在Ring3&#xff08;用户模式&#xff09;和Ring0&#xff08;内核模式&#xff09;之间传递数据&#xff0c;是一…...

PostgreSQL技术内幕22:vacuum full 和 vacuum

文章目录 0.简介1.概念及使用方式2.工作原理2.1 主要功能2.2 清理流程2.3 防止事务id环绕说明 3.使用建议 0.简介 在之前介绍MVCC文章中介绍过常见的MVCC实现的两种方式&#xff0c;一种是将旧数据放到回滚段&#xff0c;一种是直接生成一条新数据&#xff08;对于删除是不删除…...

【网络】:网络编程套接字

目录 源IP地址和目的IP地址 源MAC地址和目的MAC地址 源端口号和目的端口号 端口号 VS 进程ID TCP协议和UDP协议 网络字节序 字符串IP和整数IP相互转换 查看当前网络的状态 socket编程接口 socket常见API 创建套接字&#xff08;socket&#xff09; 绑定端口号&…...

java基础概念55-不可变集合

一、定义 不可变集合&#xff1a;不可以被修改的集合&#xff0c;只能查询。&#xff08;长度、内容均不可被修改&#xff09; 二、创建不可变集合 【注意】&#xff1a; 此方法是在JDK9中引入的。 2-1、list不可变集合 示例&#xff1a; import java.util.List;public cla…...

深入理解 C++ 函数重载

在 C 中, 函数重载是一个非常强大的特性, 允许多个函数使用相同的名称, 但具有不同的参数类型. 重载解析决定了在给定的调用中, 编译器应选择哪个版本的重载函数. 本文将深入探讨 C 重载解析的工作原理, 帮助你在实际编程中更好地理解这一机制. 重载(Overload) vs 重写(Overri…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗&#xff1f;了解下一期 Elasticsearch Engineer 培训的时间吧&#xff01; Elasticsearch 拥有众多新功能&#xff0c;助你为自己…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...