You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate查询分区表AsyncFcmNotification触发ResultSet耗尽异常如何解决

异常根因与修复方案

根本原因

  • 静态统计的循环终止阈值与动态变化的结果集不匹配:程序启动时一次性统计得到的asyncFcmNotificationsCount=105220是当时满足sent = false条件的记录总量,但循环每执行一轮,都会通过updateUnsendNotif把查出来的记录的sent状态从false更新为true,直接导致后续满足查询条件的真实数据量持续减少,初始的统计值会随着业务推进越来越大于实际剩余的待处理数据量。
  • offset深分页越界触发ResultSet异常:当累加的offset值超过当前实际满足查询条件的记录总条数时,SQL执行时的偏移量已经超出了结果集的总长度,数据库返回的结果集游标直接走到末尾,没有可映射的数据行,Hibernate在读取结果集映射实体时就会抛出ResultSet耗尽类的运行时异常。
  • 表分区配置放大了深分页的越界影响:由于表是基于timestamp时间列做的分区,查询没有命中有效分区裁剪时,数据库定位offset位置需要扫描所有相关分区的数据,当offset超过实际结果集长度时,全分区扫描完成后游标直接终止,没有返回有效数据,进一步触发了游标耗尽的报错。

修复方案

  • 替换静态终止判断逻辑:取消使用程序启动时计算的asyncFcmNotificationsCount作为循环终止条件,每轮查询完成后判断当前返回批次的实际长度,如果返回空集合,直接跳出循环,不再继续发起下一次查询。核心调整逻辑参考:
    do {
        try (HibernateSession session = SessionManager.openSession()) {
            List<AsyncFcmNotification> batchList = session.createQuery("from AsyncFcmNotification where sent = false order by id asc")
                    .setFirstResult(offset)
                    .setMaxResults(count)
                    .list();
            // 无有效数据时直接终止循环,避免offset越界
            if (batchList.isEmpty()) {
                break;
            }
            updateUnsendNotif(batchList, session);
            offset += 100;
        }
        logger.info(offset + " notif has updated");
    } while (true);
    
  • 优化分页模式:从传统offset深分页替换为键集/游标分页,每次查询带上上一轮最后一条记录的唯一主键值,查询条件调整为where sent = false and id > 上一轮批次的最大id,搭配固定的升序排序,既不会因为结果集动态变化出现越界,也能大幅提升分区表的查询效率,避免全分区扫描定位offset的性能损耗。
  • 补全查询排序规则:原查询没有指定固定排序规则,数据库返回的行顺序是随机不稳定的,即使没有出现越界,也可能出现分页重复查询、漏查的问题,必须在查询语句中增加明确的排序字段,保证每轮查询的结果顺序固定一致。

内容的提问来源于stack exchange,提问作者lia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.24 12:45:03