如何防止数据库数据被覆盖 解决高并发下字段增量计数错误问题
你遇到的是典型的丢失更新问题,本质是「读-改-写」的非原子操作在高并发场景下的时序冲突,多个事务同时读到相同的旧计数值,各自加1后回写,最终只累加了一次。以下是满足要求的解决方案:
1. 数据库层原子增量更新(最符合「无需先读取数值」的要求)
直接通过HQL/JPQL执行更新语句,把增量逻辑放在数据库层面原子执行,完全跳过应用层读取计数的步骤:
// 假设实体类为FileRecord,计数字段为typeCount,匹配条件为文件类型fileType String updateHql = "update FileRecord set typeCount = typeCount + 1 where fileType = :targetFileType"; int effectRows = session.createQuery(updateHql) .setParameter("targetFileType", 你的业务文件类型参数) .executeUpdate();
Oracle的单条UPDATE语句本身具备原子性,执行时会自动对操作行加锁,并发请求会排队串行执行,完全不会出现累加丢失的问题,性能也远高于先读后写的方案。
2. 乐观锁方案(适合并发冲突概率较低的场景)
如果业务逻辑必须先读取实体做其他校验/操作,可以给实体加@Version乐观锁注解避免丢失更新:
@Entity public class FileRecord { // 其他业务字段 @Version private Integer version; // 乐观锁版本字段,无需手动维护 private Integer typeCount; private String fileType; }
并发更新时如果版本号不匹配,Hibernate会抛出OptimisticLockException,捕获异常后重试更新即可保证计数准确。
3. 悲观锁方案(适合并发冲突概率高的场景)
如果业务必须先读实体,且冲突率很高,可以在读取时加行级排他锁,避免其他事务同时修改该行:
// 读取时直接加行锁,生成SELECT ... FOR UPDATE语句 FileRecord record = session.get(FileRecord.class, 记录主键ID, LockMode.PESSIMISTIC_WRITE); record.setTypeCount(record.getTypeCount() + 1); session.saveOrUpdate(record);
加锁后其他事务必须等当前事务提交后才能读取该行数据,也能避免丢失更新问题。
内容的提问来源于stack exchange,提问作者Hiba Rida
相关产品推荐
相关产品推荐

