【PGCCC】Postgres 故障排除:修复重复的主键行

news2025/4/26 0:45:38

如何从表中删除不需要的重复行。这些重复行之所以“不需要”,是因为同一个值在指定为主键的列中出现多次。自从 glibc 好心地改变了排序方式后,我们发现这个问题有所增加。当用户升级操作系统并修改底层 glibc 库时,这可能会导致无效索引。
唯一索引损坏的主要影响之一是允许添加本应由主键捕获的行。换句话说,对于一个在名为“id”的列上有主键的表,您可能会观察到如下情况:

-- Can you spot the problem?
SELECT id FROM mytable ORDER BY id LIMIT 5;
 id
----
  1
  2
  3
  3
  4
(5 rows)

在对问题一无所知的情况下,解决问题的第一步是什么?如果你说备份,那你答对了!每当你认为数据库有问题时,或者在尝试修复此类问题之前,都应该进行一次全新备份。
补充一下:我们可以简单地重新索引吗?不行——只要有重复的行,就无法创建(或重新创建)唯一索引。Postgres 会拒绝这样做,因为这违反了我们试图强制执行的唯一性。所以,我们必须从表中删除这些行。
这里有一个修复 Postgres 表中重复主键条目问题的巧妙方法。当然,你需要根据具体情况进行调整,但请慢慢操作,确保理解每个步骤。尤其是在生产环境中发生这种情况时(剧透:这种情况几乎总是在生产环境中发生)。

1. 调试辅助工具

我们要做的第一件事可能看起来有点奇怪:

-- Encourage not using indexes:
set enable_indexscan = 0;
set enable_bitmapscan = 0;
set enable_indexonlyscan = 0;

由于不良索引是此类不良行进入数据库的主要途径,因此我们不能信任它们。这些低级调试辅助工具会告诉 Postgres 规划器优先考虑其他数据获取方式。在我们的例子中,这意味着直接访问表,而不是在索引中查找

2. 进行快速健全性检查

-- Sanity check. This should return a number greater than 1. If not, stop.
set search_path = public;
select count(*) from mytable where id = 3;

在开始之前,我们需要确保拥有正确的表。search_path 是一种安全措施,就像在表上查找一样。由于 id 是表的主键,因此它应该为每个值返回 1 的计数。在我们的例子中,我们知道该表有多个 id 为“3”的条目,因此这主要是为了进行完整性检查,确保我们将要操作的患者是正确的。我们期望返回一个大于“1”的数字。对于有效的主键,返回的值应该只有 0 或 1。

3. 创建备份(始终)

-- Make a backup:
create table mytable_backup as select * from mytable;

在开始之前,我们需要将表中的所有现有行复制到新的备份表中。同样,这不会取代对整个数据库的完整备份(始终是步骤 0),但它是另一个很好的安全功能。

4.制作测试表

--  Test out the process on a subset of the data:
create table test_mytable as select * from mytable where id < 30;
create table test_mytable_duperows_20250317 (like mytable);

最好先在测试表上进行测试。在我们的例子中,使用的是实际表的较小版本。因为我们知道 ID 为 3 的行有问题,所以我们创建了一个包含这些行的新表。我们还创建了一个名为 test_mytable_20250317 的新空表,用于保存被移除的重复行。末尾的日期会告诉以后的查看者该表的创建时间。

5. 在副本会话中启动清理

从现在开始,我们将开始实际的清理工作。我们启动一个事务,然后将 session_replication_role 设置为 replica,这是一个高级(且危险)的命令,它会禁用所有触发器和规则。通常情况下,这不是一个好的做法,但我们还是希望这样做,以防万一存在外键阻止我们删除损坏的行。此外,我们这样做是SET LOCAL为了确保此设置在下次或时SET恢复正常。COMMITROLLBACK

begin;
set local session_replication_role = 'replica';

因为我们刚刚创建了这个测试表,所以我们知道它没有触发器并且没有通过外键链接到任何其他表,但我们希望使这个测试尽可能接近实际的表修改,因此我们保留 session_replication_role 修改。

6. 使用函数清理重复行

begin;
set local session_replication_role = 'replica';
with goodrows as (
  select min(ctid) from TEST_mytable group by id
)
,mydelete as (
  delete from TEST_mytable
  where not exists (select 1 from goodrows where min=ctid)
  returning *
)
insert into TEST_mytable_duperows_20250317 select * from mydelete;
reset session_replication_role;
commit;

因此,我们发出一个 begin 命令,设置 session_replication_role,运行一条 SQL 语句,重置 session_replication_role,最后提交。这条 SQL 语句执行起来比较繁重,所以我们来分解一下。

select min(ctid) from TEST_mytable group by id我们要做的第一件事就是想办法找出哪些行是重复的。由于id列应该是唯一的(所有主键列都是唯一的),我们知道任何出现超过一次的 ID 都需要一个决胜机制。Postgres 中的每一行都有一个名为“ctid”的隐藏列,它代表列元组标识符,本质上是一个指向实际物理行所在位置的指针。因此,它始终是唯一的。如果我们按 id 列分组,我们可以通过查找“最小”的 ctid 为每个唯一 ID 提取一个 ctid(使用 min() 、max() 或其他方法都没关系,只要我们只选择一个就行)。

我们将存储该信息并使用它来帮助删除,因此我们通过WITH命令启动一个 cte,并将其命名为goodrows

delete from TEST_mytable
where not exists (select 1 from goodrows where min=ctid)
returning *

下一步是删除所有不来自我们刚刚创建的 goodrows 列表的重复行。因此,每个重复行都会有不同的 ctid,我们将删除每个 ctid 对应的所有 ctid,只保留一个。最后***RETURNING ****一步告诉 delete 函数返回所有被删除行的完整信息。

insert into test_mytable_duperows_20250317 select * from mydelete;

最后,我们将删除操作的输出存储到表中。这样,行虽然被删除了,但我们仍然拥有一个完整的被删除行列表,用于调试和取证。

此时,重复的行应该被删除,并放在“duperows”表中。最好检查一下此表和 test_mytable 表,以确保一切按预期工作。

7. 在实时表上运行该函数

准备就绪后,您可以重新运行相同的代码,但将测试表替换为实际表:

create table mytable_duperows_20250317 (like mytable);
begin;
set local session_replication_role = 'replica';
with goodrows as (
  select min(ctid) from mytable group by id
)
,mydelete as (
  delete from mytable
  where not exists (select 1 from goodrows where min=ctid)
  returning *
)
insert into mytable_duperows_20250317 select * from mydelete;
reset session_replication_role;
commit;

8. 重新索引

最后一步,我们要重建那些可疑的索引。即使重复的行已经删除,索引中仍然可能包含错误的信息。该REINDEX命令本质上是删除并重建,因此我们对表中可能存在的所有索引执行此操作:

reindex table mytable;

现在我们还可以使用、和全部设置为将 Postgres 恢复到enable_indexscan正常enable_bitmapscan计划enable_indexonlyscan设置。

以上就是所有步骤!
在这里插入图片描述

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

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

相关文章

DeepSeek+Cursor+Devbox+Sealos项目实战

黑马程序员DeepSeekCursorDevboxSealos带你零代码搞定实战项目开发部署视频教程&#xff0c;基于AI完成项目的设计、开发、测试、联调、部署全流程 原视频地址视频选的项目非常基础&#xff0c;基本就是过了个web开发流程&#xff0c;但我在实际跟着操作时&#xff0c;ai依然会…

996引擎-拓展变量:物品变量

996引擎-拓展变量:物品变量 测试代码参考资料对于Lua来说,只有能保存数据库的变量才有意义。 至于临时变量,不像TXT那么束手束脚,通常使用Lua变量就能完成。 SELECT * FROM dbo.TBL_ITEM_EX_ABIL WHERE FLD_MAKEINDEX = 28620 <

【踩坑记录】stm32 jlink程序烧录不进去

最近通过Jlink给STM32烧写程序时一直报错&#xff0c;但是换一个其他工程就可以烧录&#xff0c;对比了一下jink配置&#xff0c;发现是速率选太高了“SW Device”&#xff0c;将烧录速率调整到10MHz以下就可以了

‌RISC-V低功耗MCU动态时钟门控技术详解

我来分享一下RISC-V核低功耗MCU的动态时钟门控技术实现&#xff1a; 这款MCU通过硬件级时钟门控电路实现了模块级的功耗管理。当外设&#xff08;如UART、SPI&#xff09;处于闲置状态时&#xff0c;系统会自动切断其时钟信号&#xff0c;减少无效翻转功耗。同时支持多电压域协…

工厂模式:解耦对象创建与使用的设计模式

工厂模式&#xff1a;解耦对象创建与使用的设计模式 一、模式核心&#xff1a;封装对象创建逻辑&#xff0c;客户端无需关心具体实现 在软件开发中&#xff0c;当创建对象的逻辑复杂或频繁变化时&#xff0c;直接在客户端代码中 new 对象会导致耦合度高、难以维护。例如&…

Python爬虫学习:高校数据爬取与可视化

本项目实现了从中国教育在线&#xff08;eol.cn&#xff09;的公开 API 接口爬取高校相关数据&#xff0c;并对数据进行清洗、分析与可视化展示。主要包括以下功能&#xff1a; 爬取高校基础信息及访问量数据数据清洗与格式转换多维度数据分析与可视化&#xff0c;如高校数量分…

触觉智能RK3506核心板,工业应用之RK3506 RT-Linux实时性测试

在工业自动化、机械臂控制等高实时性场景中&#xff0c;系统响应速度与稳定性直接决定设备效能。触觉智能RK3506核心板基于瑞芯微三核Cortex-A7架构深度优化&#xff0c;搭载Linux 6.1内核并支持Linux-RT实时系统&#xff0c;提供实时性能的高性价比解决方案。 RK3506与RT-Linu…

基于SpringBoot的高校体育馆场地预约管理系统-项目分享

基于SpringBoot的高校体育馆场地预约管理系统-项目分享 项目介绍项目摘要目录总体功能图用户实体图赛事实体图项目预览用户个人中心医生信息管理用户管理场地信息管理登录 最后 项目介绍 使用者&#xff1a;管理员 开发技术&#xff1a;MySQLJavaSpringBootVue 项目摘要 随着…

华为云获取IAM用户Token的方式及适用分析

&#x1f9e0; 一、为什么要获取 IAM 用户 Token&#xff1f; 我们用一个生活中的比喻来解释&#x1f447;&#xff1a; &#x1f3e2; 比喻场景&#xff1a; 你要去一个 高级写字楼&#xff08;华为云物联网平台&#xff09; 办事&#xff08;调用接口管理设备&#xff09;&…

如何利用快照与备份快速恢复服务器的数据

在服务器上利用**快照&#xff08;Snapshot&#xff09;**和**备份&#xff08;Backup&#xff09;**快速恢复数据&#xff0c;可显著减少停机时间并确保业务连续性。以下是具体操作步骤和最佳实践&#xff1a; --- ### **1. 快照&#xff08;Snapshot&#xff09;恢复** **适…

Git 详细使用说明文档(适合小白)

Git 详细使用说明文档&#xff08;适合小白&#xff09; 1. 什么是 Git&#xff1f; Git 是一个版本控制系统&#xff0c;帮助你管理和跟踪代码的变更。无论是个人项目还是团队协作&#xff0c;Git 都能帮助你记录代码的历史版本&#xff0c;方便回溯和协作。 2. 安装 Git …

Graph Database Self-Managed Neo4j 知识图谱存储实践1:安装和基础知识学习

Neo4j 是一个原生图数据库&#xff0c;这意味着它在存储层实现了真正的图模型。它不是在其他技术之上使用“图抽象”&#xff0c;而是以您在白板上绘制想法的相同方式在Neo4j中存储数据。 自2007年以来&#xff0c;Neo4j已经发展成为一个丰富的工具、应用程序和库的生态系统。…

一天学完Servlet!!!(万字总结)

文章目录 前言Servlet打印Hello ServletServlet生命周期 HttpServletRequest对象常用api方法请求乱码问题请求转发request域对象 HttpServletResponse对象响应数据响应乱码问题请求重定向请求转发与重定向区别 Cookie对象Cookie的创建与获取Cookie设置到期时间Cookie注意点Cook…

E3650工具链生态再增强,IAR全面支持芯驰科技新一代旗舰智控MCU

近日&#xff0c;全球嵌入式软件开发解决方案领导者IAR与全场景智能车芯引领者芯驰科技正式宣布&#xff0c;IAR Embedded Workbench for Arm已全面支持芯驰E3650&#xff0c;为这一旗舰智控MCU提供开发和调试一站式服务&#xff0c;进一步丰富芯驰E3系列智控芯片工具链生态&am…

MSSQL-数据库还原报错-‘32(另一个程序正在使用此文件,进程无法访问。)‘

这里是引用 标题: Microsoft SQL Server Management Studio 还原 对于 服务器“<<服务器名称>>”失败。 (Microsoft.SqlServer.SmoExtended) 有关帮助信息&#xff0c;请单击: http://go.microsoft.com/fwlink?ProdNameMicrosoftSQLServer&ProdVer12.0.2000.8…

卷积神经网络:视觉炼金术士的数学魔法

引言&#xff1a;当数学遇见视觉炼金术 在人工智能的奇幻世界里&#xff0c;卷积神经网络&#xff08;CNN&#xff09;犹如掌握视觉奥秘的炼金术士&#xff0c;将原始像素的"铅块"淬炼成认知的"黄金"。这种融合数学严谨性与生物灵感的算法架构&#xff0c…

立马耀:通过阿里云 Serverless Spark 和 Milvus 构建高效向量检索系统,驱动个性化推荐业务

作者&#xff1a;厦门立马耀网络科技有限公司大数据开发工程师 陈宏毅 背景介绍 行业 蝉选是蝉妈妈出品的达人选品服务平台。蝉选秉持“陪伴达人赚到钱”的品牌使命&#xff0c;致力于洞悉达人变现需求和痛点&#xff0c;提供达人选高佣、稳变现、速响应的选品服务。 业务特…

专业热度低,25西电光电工程学院(考研录取情况)

1、光电工程学院各个方向 2、光电工程学院近三年复试分数线对比 学长、学姐分析 由表可看出&#xff1a; 1、光学工程25年相较于24年下降20分&#xff0c; 2、光电信息与工程&#xff08;专硕&#xff09;25年相较于24年上升15分 3、25vs24推免/统招人数对比 学长、学姐分析…

java—11 Redis

目录 一、Redis概述 二、Redis类型及编码 三、Redis对象的编码 1. 类型&编码的对应关系 2. string类型常用命令 &#xff08;1&#xff09;string类型内部实现——int编码 &#xff08;2&#xff09;string类型内部实现——embstr编码 ​编辑 &#xff08;3&#x…

热门算法面试题第19天|Leetcode39. 组合总和40.组合总和II131.分割回文串

39. 组合总和 力扣题目链接(opens new window) 给定一个无重复元素的数组 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的数字可以无限制重复被选取。 说明&#xff1a; 所有数字&#xff08;包括 ta…