如何破解 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技术服务公司-木风未来科技-成都木风未来科技有限公司

相关推荐
ab_dg_dp5 小时前
Android bugreportz 源码分析
android
小吴学不废Java5 小时前
MySQL慢查询日志分析
android·adb
TechMix5 小时前
【用法总结】抓取main_log、events_log、kernel_log日志的方法
android
峥嵘life6 小时前
Android16 EDLA 认证测试CTS过程介绍
android·学习
唔666 小时前
下面给出 **Fuel 2.x** 的 **“开箱即用”** 封装类,**同时支持**:
android
有位神秘人6 小时前
android中compose系列之总纲
android
Jomurphys6 小时前
测试 - 概览
android
飞鹰@四海7 小时前
AutoGLM 旧安卓一键变 AI 手机:安装与使用指南
android·人工智能·智能手机
敲上瘾7 小时前
MySQL主从集群解析:从原理到Docker实战部署
android·数据库·分布式·mysql·docker·数据库架构