当前位置: 首页 > 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…...

AMLP框架实战:基于MACE构建高精度机器学习势函数

1. 项目概述&#xff1a;当机器学习势函数遇上自动化管道在计算化学和材料科学领域&#xff0c;我们长久以来面临着一个核心矛盾&#xff1a;精度与效率的权衡。密度泛函理论&#xff08;DFT&#xff09;能提供接近实验的精度&#xff0c;但计算成本高昂&#xff0c;通常只能处…...

HFSS仿真结果怎么看?一文读懂S参数与电场图,让你的T型波导分析不再迷茫

HFSS仿真结果深度解析&#xff1a;从S参数到电场图的工程实践指南面对HFSS仿真生成的复杂数据图表&#xff0c;许多工程师常陷入"看得见数据却读不懂含义"的困境。本文将带您穿透数据表象&#xff0c;掌握T型波导性能分析的核心方法论。1. S参数&#xff1a;波导性能…...

手把手教你为WCH CH582移植CherryUSB主机栈(基于RT-Thread,含中断优化)

基于RT-Thread的WCH CH582 USB主机协议栈深度移植指南在嵌入式开发领域&#xff0c;USB主机功能的实现往往意味着设备能够直接连接各类USB外设&#xff0c;从简单的键盘鼠标到复杂的存储设备。对于使用WCH CH582这类RISC-V内核MCU的开发者而言&#xff0c;原厂SDK提供的USB主机…...

ARM架构CONSTRAINED UNPREDICTABLE行为解析与应对

1. ARM架构中的CONSTRAINED UNPREDICTABLE行为解析在处理器架构设计中&#xff0c;UNPREDICTABLE行为通常指架构规范未明确定义的执行结果&#xff0c;可能导致不可预期的系统状态。ARM架构通过引入CONSTRAINED UNPREDICTABLE机制&#xff0c;将这类行为限制在特定范围内&#…...

3步深度解锁:网络设备权限管理工具的实战手册

3步深度解锁&#xff1a;网络设备权限管理工具的实战手册 【免费下载链接】zteOnu A tool that can open ZTE onu device factory mode 项目地址: https://gitcode.com/gh_mirrors/zt/zteOnu 你是否曾面对功能受限的网络设备感到束手无策&#xff1f;当默认配置锁死了硬…...

ARM PMU性能监控单元原理与实践指南

1. ARM PMU性能监控单元概述性能监控单元&#xff08;PMU&#xff09;是现代ARM处理器中用于硬件级性能分析的核心组件。它通过一组可编程的硬件计数器&#xff0c;实现对处理器内部各种关键事件的精确测量。这些事件涵盖了从指令执行、缓存访问到内存子系统行为等处理器活动的…...

FT231XQ USB串口桥接板设计解析与实战应用指南

1. 项目概述&#xff1a;从FT232R到FT231XQ的USB串口桥接板演进在嵌入式开发和硬件调试的日常工作中&#xff0c;一个可靠、小巧且功能清晰的USB转串口&#xff08;UART&#xff09;桥接板&#xff08;Breakout Board&#xff0c; 简称BoB&#xff09;几乎是工程师手边的标配工…...

终极歌词同步神器LRCGET:5分钟为你的音乐库添加完美歌词

终极歌词同步神器LRCGET&#xff1a;5分钟为你的音乐库添加完美歌词 【免费下载链接】lrcget Utility for mass-downloading LRC synced lyrics for your offline music library. 项目地址: https://gitcode.com/gh_mirrors/lr/lrcget 你是否厌倦了在听歌时手动搜索歌词…...

猫抓浏览器扩展终极指南:5分钟掌握全网视频资源下载技巧

猫抓浏览器扩展终极指南&#xff1a;5分钟掌握全网视频资源下载技巧 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 你是否经常遇到心仪的视频无法…...

UE5 Cesium项目里,如何把默认的飞行Pawn换成建筑漫游Pawn?保姆级迁移教程

UE5 Cesium项目建筑漫游Pawn迁移实战&#xff1a;从飞行模式到精细化浏览的完整指南当你在UE5中结合Cesium插件构建数字孪生场景时&#xff0c;DynamicPawn提供的全球飞行体验令人印象深刻。但当视角聚焦到单体建筑或室内空间时&#xff0c;那种仿佛操控无人机般的操作方式就显…...