MySQL如何避免POS应用并发提交时增量发票号重复录入问题
发票号并发重复问题的SQL锁解决方案
核心问题本质
你遇到的重复问题根源是「读取最后发票号→加1→插入新发票」三个操作是非原子操作,高并发下多个请求会读到相同的最后发票号,最终生成重复编号。
可以用SQL锁解决,推荐悲观锁方案(适配你的场景实现成本最低)
共享主机环境一般使用MySQL数据库,直接用自带的排他锁即可实现操作原子化,步骤如下:
- 先给发票号字段添加唯一索引,作为兜底安全规则,避免任何极端情况下出现重复发票号的脏数据
- 生成发票号的整个逻辑包裹在数据库事务中,查询最后发票号时加排他锁,保证同一时间只有一个请求能读取到最新编号,其他请求会阻塞等待当前操作完成
CodeIgniter 实现参考代码
// 开启CodeIgniter事务,会自动处理异常回滚 $this->db->trans_start(); // 加排他锁查询最新发票号,FOR UPDATE会锁定查询范围,其他事务需等待当前事务结束后才能读取 $query = $this->db->query("SELECT MAX(invoice_no) as last_no FROM 你的发票表名 FOR UPDATE"); $lastNo = $query->row()->last_no ?? 0; // 如果是第一张发票默认从1开始 $newInvoiceNo = $lastNo + 1; // 拼接所有表单提交的销售数据,插入发票记录 $insertData = [ 'invoice_no' => $newInvoiceNo, // 其余你需要保存的表单字段,比如客户信息、金额、商品明细等 ]; $this->db->insert('你的发票表名', $insertData); // 提交事务,释放锁 $this->db->trans_complete(); // 事务失败兜底逻辑,自动重试无需暴露给用户 if ($this->db->trans_status() === false) { // 冲突概率极低,这里可以自动重试1~2次,用户完全无感知,不会丢失表单数据 }
额外优化建议
如果你的业务允许发票号不要求完全连续(比如偶尔出现跳号不影响财务规则),可以直接将发票号字段设置为数据库自增主键,数据库会自动处理并发递增逻辑,不需要手动编写锁相关代码,是成本最低的解决方案。
内容的提问来源于stack exchange,提问作者Chinmoy
相关产品推荐
相关产品推荐

