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

多@Transactional会引发PostgreSQL多数据库锁吗?如何验证?

验证Spring事务拆分后的多事务与锁优化效果

我有一个标注了@Transactional的Service类EventsService,原deleteSentEvents方法用于按状态批量删除事件,原实现如下:

@Service
@Transactional("transactionManager")
public class EventsService {

  @Autowired private EventsRepository eventsRepository;
  
  public int deleteSentEvents() {
    String state = "SENT";
    int deletionLimit = 10000;
    Pageable page = Pageable.ofSize(1000);
    Page<Events> eventsPage = null;
    do {
      eventsPage =
          eventsRepository.findEventsByState(state, page); 
      eventsRepository.deleteInBatch(eventsPage.getContent().stream().map(Events::getId).toList());
      deletedTotal += eventsPage.getNumberOfElements();
    } while (eventsPage.hasNext() && deletedTotal < deletionLimit);
    return deletedTotal;
  }
}

为避免长时间锁定大量行,我把删除逻辑移到了单独标注@Transactional的deleteBatch方法中,修改后的代码如下:

public int deleteSentEvents() {
    String state = "SENT";
    int deletionLimit = 10000;
    Pageable page = Pageable.ofSize(1000);
    Page<Events> eventsPage = null;
    do {
      eventsPage =
          eventsRepository.findEventsByState(state, page);
      deleteBatch(eventsPage);
      deletedTotal += eventsPage.getNumberOfElements();
    } while (eventsPage.hasNext() && deletedTotal < deletionLimit);
    return deletedTotal;
  }
  
 @Transactional("transactionManager")
  public void deleteBatch(Page<Events> eventsPage) { 
    eventsRepository.deleteInBatch(eventsPage.getContent().stream().map(Events::getId).toList());
  }

现在需要验证这个改动是否真的产生了多个事务,并且数据库锁的范围/时长确实变小。我尝试查询pg_catalog.pg_locks但没得到有效结果,求验证方法。


验证方法

一、确认多事务是否生成

  • 开启Spring事务日志:在配置文件(application.yml/application.properties)中添加日志配置,打印事务的创建和提交过程:
    logging:
      level:
        org.springframework.transaction.interceptor: TRACE
        org.springframework.jdbc.datasource.DataSourceTransactionManager: DEBUG
    
    执行删除方法时,日志会输出Creating new transaction、Committing transaction这类信息,每次调用deleteBatch都会触发一次完整的事务周期,以此确认多事务生效。
  • 查看PostgreSQL事务日志:修改PostgreSQL的postgresql.conf,开启事务和语句日志:
    log_statement = 'all'
    log_transaction = on
    
    重启数据库后执行删除操作,查看数据库日志,会看到每次deleteBatch对应独立的BEGIN和COMMIT语句,每个事务有唯一的ID。

二、验证锁的优化效果

  • 精准查询pg_locks:执行删除操作的同时,用以下SQL实时监控目标表的锁(替换events为你的表名):
    SELECT
      locktype,
      database,
      relation,
      pid,
      mode,
      granted,
      query,
      pg_stat_activity.query_start
    FROM pg_catalog.pg_locks
    JOIN pg_catalog.pg_class ON pg_locks.relation = pg_class.oid
    JOIN pg_catalog.pg_stat_activity ON pg_locks.pid = pg_stat_activity.pid
    WHERE pg_class.relname = 'events'
      AND pid != pg_backend_pid();
    
    对比修改前后:锁的持有时长(用当前时间减去query_start)、锁定的行数(结合DELETE语句的返回行数),修改后应该能看到锁的持有时间大幅缩短,每次锁定的行数控制在1000条以内。
  • 并发压测对比:模拟并发场景,比如同时发起多个读/写请求(比如查询SENT状态事件、更新事件状态),对比修改前后请求的阻塞时长和成功率。如果修改后阻塞时间明显减少,说明锁的影响范围变小。
  • 统计事务时长:用Spring AOP给deleteBatch方法加埋点,记录每次方法的执行时间;或者在数据库层面通过pg_stat_activity查看每个事务的xact_start和query_end,计算事务持续时间,和原实现的单事务时长做对比,修改后的单事务时长应该远小于原方案。

三、关键注意点

  • 原代码中deleteSentEvents没有@Transactional注解,修改后的deleteBatch用默认的REQUIRED传播行为,每次调用都会新建独立事务,这是多事务生效的前提。
  • 如果查询用的是PostgreSQL的快照隔离级别,读操作不会被行锁阻塞,可以通过并发写操作(比如更新同批次事件)来验证锁的影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 03:50:55