如何破解 MySQL 死锁?核心原则与实操方法

技术群内再度热议:"监控系统告警频发------数据库出现严重死锁警告,订单业务几近停滞。"

新入职的同事小王看到群内消息,心中不禁一沉:"这不是我刚接手的项目吗?我只是增加了几个简单的更新操作啊......"小王仔细查看群中反馈的监控截图与运行日志,在困惑之余更添几分忐忑与焦急,连忙向资深同事请教。老同事耐心解释道:"你这是不小心踩中了 MySQL 的死锁陷阱......"

何谓死锁?简而言之,死锁犹如两人相遇于独木桥:

甲自东向西,乙自西向东

两人行至桥中,互不相让

最终双双无法通过

在 MySQL 中,当两个或更多事务相互等待对方释放持有的锁时,便会形成死锁。

小王所遇的死锁场景:

sql

CREATE TABLE orders (

id BIGINT PRIMARY KEY,

user_id BIGINT,

amount DECIMAL(10,2),

status VARCHAR(20),

version INT

);

CREATE TABLE account_balance (

user_id BIGINT PRIMARY KEY,

balance DECIMAL(10,2),

frozen_amount DECIMAL(10,2)

);

死锁代码示例

java

// 小王的业务逻辑------同时更新订单与账户

@Service

public class OrderService {

@Transactional

public void processOrder(Long orderId, Long userId) {

// 先更新订单状态

updateOrderStatus(orderId, "PROCESSING");

// 再冻结账户金额

freezeUserBalance(userId, 100.00);

}

@Transactional

public void cancelOrder(Long orderId, Long userId) {

// 先解冻账户金额

unfreezeUserBalance(userId, 100.00);

// 后更新订单状态

updateOrderStatus(orderId, "CANCELLED");

}

}

并发执行时的死锁场景:

时间线:

  1. 线程A:开始执行 `processOrder(1, 100)`

  2. 获取 `orders` 表中 `id=1` 的行锁

准备获取 `account_balance` 表中 `user_id=100` 的行锁

  1. 线程B:开始执行 `cancelOrder(2, 100)`

  2. 获取 `account_balance` 表中 `user_id=100` 的行锁

准备获取 `orders` 表中 `id=2` 的行锁

  1. 死锁发生!

  2. 线程A等待线程B释放 `account_balance` 的锁

线程B等待线程A释放 `orders` 的锁

相互等待,形成死锁

死锁带来的影响

  1. 业务停滞:相关订单无法继续处理

  2. 用户体验下降:用户面临操作超时

  3. 系统资源浪费:连接池占满,CPU 空转消耗

  4. 数据不一致风险:部分事务被迫回滚

如何有效避免死锁?

  1. 统一操作顺序

java

// 优化后:始终先操作订单表,再操作账户表

@Service

public class OrderService {

@Transactional

public void processOrder(Long orderId, Long userId) {

updateOrderStatus(orderId, "PROCESSING");

freezeUserBalance(userId, 100.00);

}

@Transactional

public void cancelOrder(Long orderId, Long userId) {

updateOrderStatus(orderId, "CANCELLED"); // 先操作订单表

unfreezeUserBalance(userId, 100.00); // 再操作账户表

}

}

  1. 设置事务超时

java

@Transactional(timeout = 3) // 设定事务超时时间

public void processOrderWithTimeout(Long orderId, Long userId) {

// 业务逻辑

}

  1. 降低事务粒度

java

// 将大事务拆分为小事务

public void processOrderInSmallTransactions(Long orderId, Long userId) {

updateOrderStatus(orderId, "PROCESSING");

// 账户操作置于独立事务中

TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);

transactionTemplate.execute(status > {

return freezeUserBalance(userId, 100.00);

});

}

  1. 采用乐观锁机制

sql

UPDATE account_balance

SET balance = balance 100, version = version + 1

WHERE user_id = 100 AND version = {oldVersion};

  1. 死锁检测与重试策略

java

@Slf4j

@Service

public class OrderService {

private static final int MAX_RETRY = 3;

public void processOrderWithRetry(Long orderId, Long userId) {

int retryCount = 0;

while (retryCount < MAX_RETRY) {

try {

processOrder(orderId, userId);

return; // 执行成功则退出

} catch (DeadlockLoserDataAccessException e) {

retryCount++;

log.warn("检测到死锁,正在进行第{}次重试", retryCount);

if (retryCount == MAX_RETRY) {

throw e;

}

// 采用指数退避策略

try {

Thread.sleep(100 * (long) Math.pow(2, retryCount));

} catch (InterruptedException ie) {

Thread.currentThread().interrupt();

throw e;

}

}

}

}

}

死锁排查工具

  1. 查看死锁日志

sql

查看近期死锁信息

SHOW ENGINE INNODB STATUS;

开启死锁日志记录

SET GLOBAL innodb_print_all_deadlocks = ON;

  1. 监控锁状态

sql

查看当前锁信息

SELECT * FROM information_schema.INNODB_LOCKS;

SELECT * FROM information_schema.INNODB_LOCK_WAITS;

避免死锁的核心原则

  1. 保持一致的访问顺序:对多个资源按固定顺序进行访问

  2. 控制事务粒度:尽量使事务轻量化,缩短锁持有时间

  3. 合理设计索引:确保查询利用索引,减少锁竞争范围

  4. 设置事务超时:为事务配置合理的超时时间

  5. 实现重试机制:对可能引发死锁的操作引入重试逻辑

总结:防范优于补救。死锁虽是并发系统中的常见问题,但通过良好的架构设计与编码实践,完全能够规避其发生。在系统设计与编码阶段即充分考虑并发安全,方能保障系统持续稳定运行。

来源:小程序app开发|ui设计|软件外包|IT技术服务公司-木风未来科技-成都木风未来科技有限公司

相关推荐
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼4 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone4 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen4 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone4 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui