Oracle/人大金仓数据重复问题的定位与解析

105 阅读3分钟

问题描述:

使用人大金仓数据库时,导出的数据有重复,数据为分页查询聚合后导出。

排查

第一步:检查数据库层面的数据

SELECT * FROM test_data WHERE id = '重复记录的ID或其他条件';

数据库中实际上只有一条记录

第二步:分析程序代码

查看导出数据的核心代码:

Page<ExportData> page;
do {
    log.info("查询数据,pageNum={},pageSize={},currentTotal={}", pageNum, pageSize, dataList.size());
    page = PageHelper.startPage(pageNum++, pageSize, false);
    dataExportMapper.queryData(request);
    dataList.addAll(page.getResult());
} while (CollUtil.isNotEmpty(page.getResult()));

对应的SQL查询语句:

select * from (
        select TMP_PAGE.*, ROWNUM PAGEHELPER_ROW_ID
        from (
                select i.id from test_data i 
                left join test_file f on i.FILE_ID = f.ID
                where i.IS_DEL = '0'
                order by i.CREATE_TIME 
             ) TMP_PAGE
) where PAGEHELPER_ROW_ID > 0 and PAGEHELPER_ROW_ID <= 1000;

第三步:定位可能的问题点

分析后发现以下几个可能导致数据重复的嫌疑点:

  1. 表关联问题:左连接test_file表时,如果被关联表中有多条匹配记录,会导致主表数据重复
  2. 分页机制问题:分页查询时的排序条件不够唯一
  3. 数据库特性问题:人大金仓数据库的ROWNUM机制可能与其他数据库有所不同

解决方案

问题的根本原因在于:排序字段不唯一

在原来的SQL中,只按照CREATE_TIME进行排序,当存在多条记录具有相同创建时间时,数据库无法保证这些记录的返回顺序在多次查询中保持一致。

解决方案:在排序条件中追加一个或多个唯一字段(如主键ID)。

sql修改如下:

    select * from (
            select TMP_PAGE.*, ROWNUM PAGEHELPER_ROW_ID
            from (
                    select i.id from test_data i 
                    left join test_file f on i.FILE_ID = f.ID
                    where i.IS_DEL = '0'
                    order by i.CREATE_TIME , i.ID -- 增加主键ID确保排序唯一性
                 ) TMP_PAGE
    ) where PAGEHELPER_ROW_ID > 0 and PAGEHELPER_ROW_ID <= 1000;

原理分析

为什么排序字段不唯一会导致数据重复?

在人大金仓/Oracle数据库中,ROWNUM是在数据被检索后、排序前分配的。如果排序字段不唯一,数据库不保证每次查询时相同排序值的记录顺序一致。这就可能导致:

  • 第一次分页查询时,某条记录出现在第1页
  • 第二次分页查询时,由于排序不稳定,相同的记录可能"漂移"到第2页
  • 最终聚合时,同一条记录被多次加入结果集

经验总结

通过这次排查,我总结了数据重复问题的通用排查思路:

  1. 确认数据源头:首先检查数据库中是否存在真正的重复数据
  2. 检查关联查询:确认关联表是否导致主表数据重复
  3. 分析分页逻辑:检查排序条件是否足够唯一和稳定
  4. 考虑数据库隔离级别(针对MySQL):
    • 在RC(读已提交)隔离级别下,每次SELECT都会生成新的ReadView
    • 如果有其他事务提交新增数据,可能导致分页结果出现交集
    • 解决方法:确保排序条件明确,且新增数据排在结果集末尾
  5. 关注数据库差异:不同数据库的分页机制可能有细微差别

最后提醒

分页查询时,务必使用唯一或足够唯一的排序条件,这是一个看似简单却很容易踩坑的地方。特别是在处理数据导出、数据迁移等场景时,这个问题尤为关键。

希望我的经验能帮你少走弯路!如果你也遇到了类似问题,欢迎在评论区交流讨论。


本文基于人大金仓数据库实践经验总结,不同版本可能有所差异,请以实际情况为准。