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

PostgreSQL不同线程提交数据后短时间内无法查询到数据的问题排查

问题描述与疑问

我们有一个基于Spring Boot和Apache Camel的微服务,从Kafka Topic消费消息,完成复杂映射后将数据存入PostgreSQL的多张业务表中。

在Kafka补丁维护期间,发现同一消息可能被同时重复投递,这不符合预期。为解决该问题,我们创建了key_locking表来阻止处理重复消息的线程:

CREATE TABLE IF NOT EXISTS key_locking (key VARCHAR(50) PRIMARY KEY);

设计思路是:每个处理相同Kafka消息的线程尝试向key_locking表执行INSERT并立即DELETE对应key,成功的线程继续处理,失败的线程则阻塞等待“获胜线程”提交或回滚事务。若获胜线程提交事务,业务表中已存入对应数据;提交完成后,阻塞线程可执行INSERT和DELETE操作,之后检查业务表中是否已存在该消息的数据,若存在则将重复消息存入异常表。

对应的实现代码如下:

private boolean checkDuplicateByLocking(String key) {
  try {
    log.info("Inserting key={} into key_locking table{}", key);
    jdbcTemplate.update("INSERT INTO key_locking (key) VALUES (?)", key);
    log.info("Inserted key={} into key_locking", key);
    
    jdbcTemplate.update("DELETE FROM key_locking WHERE key=?", key);
    log.info("Deleted key={} from key_locking", key);
    
    var duplicateCheckResultJdbc = jdbcTemplate.queryForObject(
        String.format("SELECT count(*) FROM some_table WHERE key = '%s'", key), Long.class) > 0;
    var duplicateCheckResult = eventExtRepository.findByHdrId(key).isPresent();
    log.info("Duplicate check result for jdbc template is = " + duplicateCheckResultJdbc);
    log.info("Duplicate check result for repository is = " + duplicateCheckResult);
    
    return duplicateCheckResultJdbc;
  } catch (Exception e) {
    log.info("Unable to lock key", e);
    return true;
  }
}

我们编写了集成测试故意注入重复消息并观察日志:
大多数情况下流程符合预期,一个线程会在Inserting key=xyz into key_locking table处阻塞,直到获胜线程输出Finished processing message with key=xyz后,才会出现Inserted key=xyz into key_locking日志。但失败场景中,日志显示duplicateCheckResultJdbc为false,而duplicateCheckResult为true,调换jdbcTemplate和repository的调用顺序结果一致,两次检查的日志间隔仅1-2毫秒。

核心疑问:在READ_COMMITTED事务隔离级别下,其他线程提交的数据应全局可见,为何会出现不同线程提交数据后,另一线程无法立即查询到已提交的数据的情况?


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 17:42:46