You need to enable JavaScript to run this app. CakePHP 2.10.22并发创建申请时事务下申请号读取异常问题
优惠活动
大模型
产品
解决方案
定价
更多

CakePHP 2.10.22并发创建申请时事务下申请号读取异常问题

问题本质成因

所有异常的核心根源是MySQL**可重复读(REPEATABLE READ)**隔离级别的快照读机制:

  • 事务启动时,数据库会为当前事务生成一份固定的数据快照(ReadView),整个事务生命周期内,所有无锁的普通SELECT查询都会基于这份快照返回结果,不会感知到其他并发事务已经提交的新数据。
  • 控制器在进入业务逻辑第一行就开启事务时,两个并发请求会在事务刚启动的阶段执行第一次无锁查询,拿到同一个旧申请号ABC001233,生成相同的ABC001234,触发唯一约束冲突。
  • 冲突后进入重试逻辑时,由于事务已经生成了固定快照,哪怕暂停2秒等待第一个事务提交,后续的重试查询依然读取事务启动时生成的旧快照,永远看不到新提交的ABC001234,重试流程必然失败。
  • 移除控制器事务后,所有查询走自动提交模式,每次查询都会生成新的快照,自然能读到最新的已提交数据,重试逻辑可以正常运行。
保留控制器事务的落地优化方案

1. 将无锁最大号查询替换为带排他锁的当前读

这是从根源上解决并发竞态的方案,同时解决快照读限制:

  • 普通SELECT是快照读,固定读取事务启动时的快照;带FOR UPDATE的查询是当前读,会强制读取最新已提交的行数据,同时对查询到的记录加行级排他锁。
  • 由于application_number字段本身带有唯一约束(对应唯一索引),查询过程会命中索引,仅对最大申请号对应的行加锁,不会出现锁全表的性能问题。
  • 锁机制会自动将并发请求串行化:第一个请求拿到锁、生成并写入新申请号后提交释放锁,第二个请求的查询才会解除阻塞,读取最新的最大申请号,从流程上就不会出现两个请求拿到同一个申请号的情况,甚至可以移除原有冗余的重试逻辑。

优化后的get_new_application_number实现代码:

function get_new_application_number(){
    $db = $this->getDataSource();
    // 带FOR UPDATE的当前读,绕开旧快照,同时加行锁串行化并发请求
    $sql = "SELECT application_number FROM {$this->table} 
            ORDER BY application_number DESC 
            LIMIT 1 
            FOR UPDATE";
    $row = $db->fetchRow($sql);
    // 空值兜底,适配表中无数据的初始场景
    $old_number = $row[0]['application_number'] ?? 'ABC000000';
    return $old_number + 1;
}

2. 修正重试逻辑的查询规则

如果因历史代码兼容要求必须保留原有重试逻辑,需要严格注意:重试过程中不能继续使用原有的无锁普通find查询,必须每次重试都调用上述带锁的当前读查询:

  • 带FOR UPDATE的当前读会自动刷新当前事务的快照,哪怕事务已经启动了较长时间,也能读到最新提交的数据,不会被旧快照限制。
  • 可将原固定暂停2秒的逻辑优化为更短的等待(比如0.5秒),因为数据库锁本身会自动处理并发排队,不需要过长的人为等待,提升整体性能。

优化后的重试逻辑片段:

catch(Exception $e){
    $save_exception = true;
    // 短时间等待,避免忙等消耗资源
    usleep(500000);
    // 关键操作:必须调用带FOR UPDATE的当前读查询,否则依然会读取旧快照
    $new_application_number = $this->get_new_application_number();
}

3. 事务范围优化注意事项

避免将不需要事务保护的逻辑(比如人为sleep、无意义的空转重试)长期放在事务内,否则会拉长锁的持有时间,降低系统并发能力。通过上述带锁查询的改造后,申请号生成环节会被自动串化为串行流程,整个事务的持有时间会被压缩到毫秒级,不会产生额外的性能损耗。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.12 10:24:07