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

解决Hibernate更新时LockAcquisitionException锁等待超时问题

解决按分类软删产品时的Lock wait timeout问题

嘿,我之前也踩过类似的坑!你遇到的Lock wait timeout exceeded问题,核心原因就是你提到的——DAO层的HQL没加WHERE条件,直接更新了整个Product表,导致InnoDB锁了太多行,触发了超时。咱们一步步来搞定它:

问题到底出在哪?

当你执行不带WHERE子句的UPDATE Product SET deleteTimestamp = :deleteTime时,MySQL的InnoDB引擎会干两件坏事:

  • 它会尝试锁定Product表的所有行(如果你的category外键没建索引,甚至会直接升级成表锁);
  • 如果表数据量大,或者有其他并发操作在读写Product表,锁会被长时间占用,其他事务拿不到锁就超时了。

具体怎么修复?

1. 先把DAO层的HQL改对,只更新目标分类的产品

这是最关键的一步!必须给HQL加上分类过滤条件,只更新指定分类下的产品。假设你的Product实体里用productCategory.id关联分类ID,正确的HQL应该是:

UPDATE Product p 
SET p.deleteTimestamp = :deleteTime 
WHERE p.productCategory.id = :categoryID

如果你的Product直接关联了ProductCategory对象,也可以这么写:

UPDATE Product p 
SET p.deleteTimestamp = :deleteTime 
WHERE p.productCategory = :category

这样一来,只会锁定目标分类下的产品行,锁的范围一下子就缩小了,超时概率会大幅降低。

2. 给外键字段加索引,避免全表扫描

哪怕你加了WHERE条件,如果category_id外键没有索引,MySQL还是会全表扫描找目标行,这时候还是会锁很多不必要的行。所以一定要给Product表的category_id字段加索引:
用SQL直接建:

CREATE INDEX idx_product_category ON product(category_id);

或者在Hibernate实体里通过注解自动生成:

@Entity
public class Product implements java.io.Serializable {
    // ...其他字段
    @ManyToOne
    @JoinColumn(name = "category_id", nullable = false)
    @Index(name = "idx_product_category") // 加这个注解生成索引
    private ProductCategory productCategory;
    // ...
}

3. 缩短信务范围,别让锁拿太久

检查一下你的服务层和DAO层的事务边界:

  • 别在事务里做无关的事,比如打大量日志、调用外部API这些,尽量让事务只包含更新操作,缩短锁的持有时间;
  • 如果用Spring的话,调整@Transactional的参数,比如设置合理的timeout,避免事务挂太久。

4. 分类下产品太多?试试分批更新

如果某个分类下有几万甚至几十万条产品,哪怕加了条件,一次性更新还是可能锁太多行。这时候可以分批更新,每次只更100条左右:

@Override
public void softDeleteProductByCategory(int categoryID, Timestamp deleteTime, Session session) {
    int batchSize = 100;
    int updatedCount;
    do {
        Query updateQuery = session.createQuery("UPDATE Product p " +
                "SET p.deleteTimestamp = :deleteTime " +
                "WHERE p.productCategory.id = :categoryID " +
                "AND p.deleteTimestamp IS NULL"); // 确保只处理未软删的产品
        updateQuery.setParameter("deleteTime", deleteTime);
        updateQuery.setParameter("categoryID", categoryID);
        updateQuery.setMaxResults(batchSize);
        updatedCount = updateQuery.executeUpdate();
    } while (updatedCount > 0);
}

每次只锁一小部分行,给其他操作留出空间,就不容易超时了。

最后验证一下

改完之后,再调用/softDeleteProductByCategory接口,你会发现锁的范围小了,持有时间短了,再也不会出现锁等待超时的问题啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 03:54:32