RID 主键索引出现了局部不一致/损坏

我使用docker部署了一个高斯数据库,版本openGauss-6.0.3。就一台服务器部署的。
部署好之后,数据库正常运行开启运行,我们在上面创建了一个库
CREATE DATABASE ms_test2 WITH DBCOMPATIBILITY = ‘B’;
然后再创建的库中导入库表。
其中有一张表
CREATE TABLE td_tj_bhk (
RID bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘RID’,
VISIT_NO varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT ‘就诊流水号’,
INNER_ID bigint(20) DEFAULT NULL COMMENT ‘中心ID’,
EMP_ID bigint(20) NOT NULL COMMENT ‘体检人员信息ID’,
PER_CRPT_ID bigint(20) DEFAULT NULL COMMENT ‘用人单位信息ID’,
WORK_CRPT_ID bigint(20) DEFAULT NULL COMMENT ‘用工单位信息ID’,
NUM int(11) DEFAULT NULL COMMENT ‘序号’,
BHK_CODE varchar(50) COLLATE utf8_bin NOT NULL COMMENT ‘体检编号’,
LAST_BHK_CODE varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT ‘上次体检编号’,
BHK_PASSWORD varchar(48) COLLATE utf8_bin DEFAULT NULL COMMENT ‘档案查询密码’,
PERSON_NAME varchar(100) COLLATE utf8_bin NOT NULL COMMENT ‘姓名’,
PYX_NAME varchar(50) COLLATE utf8_bin NOT NULL COMMENT ‘拼音简写’,
AGE int(11) DEFAULT NULL COMMENT ‘年龄’,
MARITAL_STATUS varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT ‘婚姻状况’,
STREET_ZONE_CODE varchar(12) COLLATE utf8_bin DEFAULT NULL COMMENT ‘所属街道’,
NEIGH_ZONE_CODE varchar(12) CHARACTER SET utf8 DEFAULT NULL COMMENT ‘所属居委会’,
COMM_NAME varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT ‘社区名称’,
SEX varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT ‘性别’,
BIRTHDAY date DEFAULT NULL COMMENT ‘出生日期’,
COUNTRY varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT ‘国籍’,
USER_NATION varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT ‘民族’,
FAMILY_ADDR varchar(400) COLLATE utf8_bin DEFAULT NULL COMMENT ‘家庭地址’,
MOBILE_TEL varchar(128) COLLATE utf8_bin DEFAULT NULL COMMENT ‘联系电话’,
CUR_ADDR_ZONE_CODE varchar(12) COLLATE utf8_bin DEFAULT NULL COMMENT ‘现住址’,
BIRTH_ZONE_CODE varchar(12) COLLATE utf8_bin DEFAULT NULL COMMENT ‘出生地’,
PE_CODE varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT ‘社保编号’,
IDC_PHOTO varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT ‘身份证照片’,
DEGREE_EDU varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT ‘文化程度’,
BREAKFAST varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT ‘早餐’,
SETT_METHOD int(11) NOT NULL COMMENT ‘收费方式\n 1、后付费\n 2、预付费’,
POSITION varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT ‘职务’,
PE_MODE int(11) NOT NULL COMMENT ‘登记方式\n 1、现场登记\n 2、批量导入登记’,
PE_DATE datetime DEFAULT NULL COMMENT ‘体检日期’,
RCD_DATE datetime NOT NULL COMMENT ‘登记日期’,
RMK varchar(500) COLLATE utf8_bin DEFAULT NULL COMMENT ‘备注’,
MONI_TYPE int(11) DEFAULT NULL COMMENT ‘监测类型\n 1、常规监测\n 2、主动监测\n 3、其他监测’,
PHOTO varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT ‘照片’,
IF_PHOTO tinyint(1) NOT NULL COMMENT ‘是否完成拍照’,
CERT_TYPE varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT ‘发证类别’,
COM_TAG int(11) NOT NULL COMMENT ‘完成标记\n 0、未完成\n 1、科室完成\n 2、复检登记\n 3、体检完成’,
IF_APPO tinyint(1) NOT NULL COMMENT ‘是否预约登记’,
IF_RHK tinyint(1) NOT NULL COMMENT ‘是否复检登记’,
RHK_TYPE int(11) DEFAULT NULL COMMENT ‘复查类型 1:期间复查 2:最终复查’,
IF_DELAY tinyint(1) DEFAULT ‘0’ COMMENT ‘是否延期登记’,
IF_DELAY_BHK tinyint(1) DEFAULT NULL,
REC_TIME datetime DEFAULT NULL COMMENT ‘收表时间’,
IF_RECEIPT tinyint(1) NOT NULL COMMENT ‘是否收表’,
SETT_STATS int(11) NOT NULL COMMENT ‘结算状态\n 0、未结算\n 1、部分结算\n 2、已结算’,
IF_DGN_QUALIFIED tinyint(1) DEFAULT NULL COMMENT ‘是否主检合格’,
IF_OUT tinyint(1) DEFAULT NULL COMMENT ‘是否外出体检’,
RCD_USER_UID varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT ‘登记人员UID’,
RCD_USER varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT ‘登记人员’,
REC_USER_UID varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT ‘收表人员UID’,
REC_USER varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT ‘收表人员’,
INS_UNIT_UID varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT ‘检查机构UID’,
INS_UNIT_NAME varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT ‘检查机构名称’,
IF_OUT_MODIFY tinyint(1) DEFAULT ‘1’ COMMENT ‘外出数据是否可以上传后修改’,
IF_SCENE_COMPLETE tinyint(1) DEFAULT NULL COMMENT ‘是否现场完成’,
IF_VIP tinyint(1) DEFAULT ‘0’ COMMENT ‘是否贵宾’,
FINGER_PRINT longtext COLLATE utf8_bin COMMENT ‘指纹图片’,
BHK_SIGN varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT ‘体检人签名’,
IF_HIV tinyint(1) DEFAULT NULL COMMENT ‘是否HIV筛查’,
CREATE_TIME datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ‘创建时间’,
CREATE_USER bigint(50) NOT NULL DEFAULT ‘1’ COMMENT ‘创建人’,
MODIFY_TIME datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ‘修改时间’,
MODIFY_USER bigint(50) NOT NULL DEFAULT ‘1’ COMMENT ‘修改人’,
IF_DELETE tinyint(1) NOT NULL DEFAULT ‘0’ COMMENT ‘是否删除’,
DELETE_TIME datetime DEFAULT NULL COMMENT ‘删除时间’,
DELETE_USER bigint(50) DEFAULT NULL COMMENT ‘删除人’,
RMK2 varchar(500) COLLATE utf8_bin DEFAULT NULL COMMENT ‘备注2’,
RMK3 varchar(500) COLLATE utf8_bin DEFAULT NULL COMMENT ‘备注3’,
RMK4 varchar(500) COLLATE utf8_bin DEFAULT NULL COMMENT ‘备注4’,
RMK5 varchar(500) COLLATE utf8_bin DEFAULT NULL COMMENT ‘备注5’,
BILL_USER varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT ‘开单医生’,
BILL_USER_UID varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT ‘开单医生UID’,
CONVERT_TYPE int(11) DEFAULT ‘0’ COMMENT ‘体检类型转换 0:正常 1:职业放射转福利’,
EXAMINE_PURPOSE int(11) DEFAULT NULL COMMENT ‘检查目的1:定期监护 2:诊断’,
ACCOUNT_MANAGER_UID varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT ‘客户经理uid’,
ACCOUNT_MANAGER varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT ‘客户经理’,
ORDER_NO varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT ‘订单号’,
IF_PRINT_ORDER tinyint(1) DEFAULT ‘0’ COMMENT ‘是否打印流转单’,
ORD_PRINT_TIME datetime DEFAULT NULL COMMENT ‘流转单打印时间’,
ORD_PRINT_USER varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT ‘流转单打印人’,
ORD_PRINT_USER_UID varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT ‘流转单打印人UID’,
IF_MEDICAL_INSURANCE tinyint(1) DEFAULT ‘0’ COMMENT ‘是否医保登记’,
IF_GROUP tinyint(1) DEFAULT ‘0’ COMMENT ‘是否团检方案’,
GROUP_TOTAL decimal(11,2) DEFAULT ‘0.00’ COMMENT ‘团检总金额(团检范围内的金额)’,
IF_OFFICIALS tinyint(1) DEFAULT ‘0’ COMMENT ‘是否公务员’,
SIGN_DATE date DEFAULT NULL COMMENT ‘签到日期’,
IF_OUT_SUBMIT tinyint(1) DEFAULT ‘0’ COMMENT ‘是否外检提交’,
PRIMARY KEY (RID) USING BTREE,
KEY FK_FK_TD_TJ_BHK_1 (EMP_ID) USING BTREE,
KEY FK_FK_TD_TJ_BHK_2 (WORK_CRPT_ID) USING BTREE,
KEY FK_FK_TD_TJ_BHK_3 (PER_CRPT_ID) USING BTREE,
KEY INDEX_BHK_CODE (BHK_CODE) USING BTREE,
KEY INDEX_EMP_ID (EMP_ID) USING BTREE,
KEY IDX_MODIFY_DATE (MODIFY_TIME) USING BTREE,
KEY td_tj_bhk_RCD_DATE_IDX (RCD_DATE) USING BTREE,
KEY td_tj_bhk_INS_UNIT_UID_IDX (INS_UNIT_UID,PER_CRPT_ID,WORK_CRPT_ID) USING BTREE,
KEY INDEX_INS_UNIT_UID_CRPT_ID (INS_UNIT_UID,PER_CRPT_ID,WORK_CRPT_ID),
KEY idx_bhk_unit_date (INS_UNIT_UID,RCD_DATE,IF_DELETE),
CONSTRAINT FK_FK_TD_TJ_BHK_1 FOREIGN KEY (EMP_ID) REFERENCES td_tj_emp (RID),
CONSTRAINT FK_FK_TD_TJ_BHK_2 FOREIGN KEY (WORK_CRPT_ID) REFERENCES tb_tj_crpt (RID),
CONSTRAINT FK_FK_TD_TJ_BHK_3 FOREIGN KEY (PER_CRPT_ID) REFERENCES tb_tj_crpt (RID)
) ENGINE=InnoDB AUTO_INCREMENT=143642 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT=‘体检信息’;
我的程序是一个springboot的jar包,jar启动后运行,业务接口逻辑就是在td_tj_bhk表中新增修改数据。
现在有一个现象就是,td_tj_bhk表新增了一条数据,例如,rid=12701
查询 select * from td_tj_bhk where rid=12701 ,可以正常查询。
但是经过接口业务接口执行后,比如对td_tj_bhk进行一些update操作等。
然后再查询select * from td_tj_bhk where rid=12701就查不到。
使用select * from td_tj_bhk where rid='12701’就可以查到。
这样的现象还不是所有数据都这样,比如我某一天新增了400条数据,有那么随机的20条有这样的问题。

答案内容由AI生成,仅供参考:

:end_arrow: 结论

您的问题极有可能是由 openGauss 6.0.3 版本索引管理机制的不稳定性引起的主键索引局部失效或逻辑损坏,叠加 Spring Boot 应用中的类型隐式转换,导致部分主键查询“看似丢失”。

:white_check_mark: 推荐行动清单:

  1. 使用 EXPLAIN ANALYZE 确认 rid=12701rid='12701' 的执行路径差异
  2. 检查 pg_index.indisvalid,确认索引有效性
  3. 在测试环境尝试 REINDEX INDEX td_tj_bhk_pkey
  4. 规范代码中参数类型,杜绝整型与字符串混用
  5. 制定升级至 openGauss 7.0 LTS 的计划(预计 2026 Q1 发布)

:bell: 强烈建议:openGauss 6.0.3 不应作为长期生产系统使用,其社区已将重点转向 7.0+ 版本的兼容性与稳定性提升。

若您无法升级,建议向 openGauss Gitee Issue 提交最小化复现案例,请求内核团队介入分析索引一致性机制是否存在 Bug。

点击此处查看详细分析解答

关于 openGauss 6.0.3 中 RID 主键索引局部不一致/损坏问题的分析与建议

根据您描述的现象,在 openGauss 6.0.3 版本中,td_tj_bhk 表的主键索引(RID)存在“局部不一致”或“逻辑损坏”的可能性较高。该问题表现为:

  • 新插入数据初期可正常通过主键查询SELECT * FROM td_tj_bhk WHERE rid=12701)。
  • 执行若干更新操作后,原本能查到的记录变得无法通过数值型比较查出,但使用字符串形式却能查出WHERE rid='12701' 可查)。
  • 问题呈现非普遍性、随机性,仅影响部分新增数据。

:magnifying_glass_tilted_left: 问题本质分析

:white_check_mark: 1. 主键索引不一致/损坏的可能性

您观察到的核心现象:

SELECT * FROM td_tj_bhk WHERE rid=12701 → 查询不到
SELECT * FROM td_tj_bhk WHERE rid='12701' → 可以查到

这提示了以下关键线索:

  • 在 SQL 中,12701 是数值常量,而 '12701' 是字符串。数据库会尝试进行类型隐式转换。
  • 如果索引(尤其是基于 bigint 的主键索引)中的数据存储或统计信息不一致,可能导致等值比较时优化器无法使用索引或命中错误路径。
  • 字符串比较可能会导致全表扫描(seq scan),从而绕过索引路径,进而“查到了被索引隐藏的数据”。

:backhand_index_pointing_right: 这是一种典型的索引与堆表数据不一致的表现形式,即:

索引未正确维护某个主键值的位置映射,导致通过索引访问失败;但该行数据在底层表(heap)中仍然存在。


:red_exclamation_mark: 2. openGauss 6.0.3 的已知索引/系统表相关缺陷

从上下文知识库中可以提取多项相关线索,强烈支持此问题为索引机制缺陷或优化不足导致的查询不一致

:play_button: 已知问题:系统表缺乏索引导致元数据查询性能极差

  • 问题来源:[Document Chunk #5], [KG Entity: Index], [KG Relationship: Index → System Tables]
  • 描述:用户发现连接 openGauss 时会执行一段复杂的 pg_typepg_namespace 联查,耗时高达 76秒以上
  • 原因:系统表如 pg_typepg_class 等缺乏必要的辅助索引,导致查询计划走全表扫描。
  • 影响范围:虽然不是直接关联业务表,但这说明 openGauss 6.0 系列出厂时存在 索引策略设计缺陷的潜在风险,不排除延伸至用户表索引管理。

:play_button: 数据迁移中出现唯一索引创建失败,导致重复数据

  • 来源:[Document Chunk #1], [KG Entity: Primary Key Constraint Violation], [Relationship: Primary Key Constraint Violation → Sp Dxryxxb]
  • 现象:在使用 DataKit 迁移工具从 PostgreSQL 到 openGauss 6.0.3 时,日志显示:
    ERROR: could not create unique index “sp_dxryxxb_pkey”
    
  • 后果:目标表出现主键冲突和数据重复
  • 说明:openGauss 在建表过程中可能未正确完成索引初始化或在异常中断时未回滚彻底,导致后续查询行为异常。

:play_button: openGauss JDBC 驱动存在 timestamp 二进制解析错误

  • 来源:[KG Entity: OpenGauss JDBC Driver 6.0.2], [Relationship: OffsetDateTime/LocalDateTime → Driver]
  • 问题:开启 binaryTransfer=true 时,驱动无法正确解析 timestamp 类型。
  • 关联性:表明 openGauss 在底层数据序列化、类型匹配等方面存在兼容性问题,尤其是在非标准用法下(如 Java 应用、Spring Boot)更易暴露。

:warning: 3. Spring Boot + openGauss 环境下的潜在问题放大效应

您使用的是 Spring Boot 框架配合 openGauss 作为数据源,以下是几个关键配置点可能加剧问题:

风险点 说明
JDBC 自动类型转换 JDBC 驱动可能将 bigint 参数以字符串形式传入,触发隐式转换,绕过索引。例如,传参 '12701' 而非 12701
HikariCP/连接池元数据缓存 连接池会缓存 PreparedStatement 的元信息,若底层表结构或索引状态变化却不刷新,可能造成错误执行计划
Hibernate First-Level Cache 或 Query Cache 若启用了二级缓存,缓存中的旧记录可能导致“看似查不到”的假象
SQL 字面量拼接或 ORM 框架生成的条件类型不一致 如字段是 bigint,但传参为字符串,可能引发执行路径差异

:toolbox: 排查与验证建议

请按以下步骤确认问题性质:

:white_check_mark: 步骤 1:确认是否真的“查不到”,还是执行计划差异导致

-- 显式使用 EXPLAIN 分析执行计划
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM td_tj_bhk WHERE rid = 12701;
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM td_tj_bhk WHERE rid = '12701';

对比二者:

  • 是否使用了索引扫描(Index Scan using td_tj_bhk_pkey)?
  • 是否有类型转换警告(如 btree_numeric_ops vs btree_int8_ops)?
  • 是否有 buffer hit 差异?

:backhand_index_pointing_right: 若前者走索引失败而后者走顺序扫描,则大概率是索引损坏或统计信息不准。


:white_check_mark: 步骤 2:检查主键索引状态

-- 查看索引定义
\d+ td_tj_bhk

-- 查看 pg_index 中关于该索引的状态
SELECT 
  i.indexrelid::regclass AS index_name,
  i.indisvalid,
  i.indisready,
  i.indnatts,
  t.relname AS table_name
FROM pg_index i
JOIN pg_class t ON t.oid = i.indrelid
WHERE t.relname = 'td_tj_bhk';

重点关注 indisvalid 是否为 t,若为 f 表示索引无效。


:white_check_mark: 步骤 3:重建主键索引(高风险操作,生产慎用)

-- 删除并重建索引(建议在低峰期)
ALTER TABLE td_tj_bhk DROP CONSTRAINT td_tj_bhk_pkey;
ALTER TABLE td_tj_bhk ADD CONSTRAINT td_tj_bhk_pkey PRIMARY KEY (rid);

或手动重建索引:

REINDEX INDEX td_tj_bhk_pkey;

:warning: 注意REINDEX 可能阻塞写入,请在维护窗口执行。


:white_check_mark: 步骤 4:验证索引一致性

使用如下语句测试是否存在“索引遗漏”:

-- 比对堆表全扫描 vs 索引扫描结果
SELECT rid FROM td_tj_bhk WHERE rid = 12701;  -- 应走索引
SELECT rid FROM ONLY td_tj_bhk WHERE ctid IN (
  SELECT ctid FROM td_tj_bhk WHERE rid = 12701
);

如果前者为空,后不为空,则明确索引未覆盖该元组


:hammer_and_wrench: 解决建议

方案 描述 优先级
1. 升级到 openGauss 7.0 或等待 7.0 LTS 稳定版 根据社区路线图,7.0 LTS 将全面兼容 PostgreSQL ON CONFLICT、增强索引管理能力,并修复大量 6.x 系列已知缺陷。6.0.x 系列为早期版本,存在较多稳定性隐患。 :star::star::star::star:
2. 定期重建索引(自动化脚本) 对频繁更新的大表定期执行 REINDEX TABLE td_tj_bhk;,尤其是在大批量 UPDATE/DELETE/INSERT 后。 :star::star::star:
3. 避免直接使用字符串与整型混用 在 Spring Boot 的 MyBatis/JPA 中确保参数类型与字段严格一致(如使用 Long 而非 String)。禁止 SQL 拼接数字为字符串。 :star::star::star::star:
4. 使用 MERGE INTO 替代复杂 UPDATES openGauss 6.0 支持 MERGE INTO,且比 INSERT ... ON DUPLICATE KEY UPDATE 更稳定。建议重构业务逻辑。 :star::star::star:
5. 验证 Docker 环境 I/O 稳定性 Docker 容器若运行在 overlay2 文件系统上,且磁盘 I/O 延迟高,可能导致 WAL 写入异常、索引写半途失败。建议监控日志中是否出现 could not fsync file 或 checkpoint 错误。 :star::star:

:end_arrow: 结论

您的问题极有可能是由 openGauss 6.0.3 版本索引管理机制的不稳定性引起的主键索引局部失效或逻辑损坏,叠加 Spring Boot 应用中的类型隐式转换,导致部分主键查询“看似丢失”。

:white_check_mark: 推荐行动清单:

  1. 使用 EXPLAIN ANALYZE 确认 rid=12701rid='12701' 的执行路径差异
  2. 检查 pg_index.indisvalid,确认索引有效性
  3. 在测试环境尝试 REINDEX INDEX td_tj_bhk_pkey
  4. 规范代码中参数类型,杜绝整型与字符串混用
  5. 制定升级至 openGauss 7.0 LTS 的计划(预计 2026 Q1 发布)

:bell: 强烈建议:openGauss 6.0.3 不应作为长期生产系统使用,其社区已将重点转向 7.0+ 版本的兼容性与稳定性提升。

若您无法升级,建议向 openGauss Gitee Issue 提交最小化复现案例,请求内核团队介入分析索引一致性机制是否存在 Bug。

只是通过JDBC执行的时候会有这个问题吗?gsql执行的时候有没有问题?JDBC使用的是哪个版本?

gsql没办法复现,数据的出现都是随机的。而且都在业务系统中。JDBC用的官方的,用的JDBC_6.0.2。一开始部署的6.0.2有这样的问题,就升级到6.0.3,还是有这样的问题。所以JDBC还是用的6.0.2的

出问题时,rid一般多大,应该没有超过int范围吧。

我遇到了相同的问题,不同的是我的主键是24长度的字符串。随机出现数据通过主键查询不到。未找到原因

没有超范围,rid才使用6位长的数字

那后来你们是怎么解决这个问题的?

只有在

出现的场景是update后立即去查询是吧

看着RID不是主键,是不是业务update的时候连RID一起修改了。

RID是主键,update的时候,并没有连RID一起修改。

update后,不管是立即查询,还是过很久查询,都不行。必须要重新刷新索引才可以

EXPLAIN ANALYZE SELECT * FROM td_tj_bhk WHERE rid = 12701;

EXPLAIN ANALYZE SELECT * FROM td_tj_bhk WHERE rid = ‘12701’;

执行看一下这两个执行计划有什么不同

换数据库 :sweat_smile: