第453集死锁从基础到架构实战 | 字数总计: 5.5k | 阅读时长: 23分钟 | 阅读量:
死锁从基础到架构实战 1. 概述 1.1 死锁的重要性 死锁(Deadlock) 是数据库并发控制中的核心问题,当多个事务相互等待对方释放资源时,就会发生死锁。理解死锁机制、掌握死锁的检测、预防和解决方法是数据库运维和架构设计的必备技能。
死锁的价值 :
系统稳定性 :死锁会导致事务无法完成,影响系统可用性
性能优化 :合理处理死锁可以提升系统性能
数据一致性 :死锁处理不当可能导致数据不一致
架构设计 :理解死锁有助于设计更好的并发架构
1.2 死锁的定义 死锁 :两个或多个事务在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
死锁的四个必要条件 (Coffman条件):
互斥条件 :资源不能被多个事务同时使用
请求与保持条件 :事务持有资源的同时请求其他资源
不剥夺条件 :已获得的资源不能被强制释放
循环等待条件 :存在事务资源的循环等待链
1.3 本文内容结构 本文将从以下几个方面全面解析死锁:
死锁基础 :死锁概念、产生条件、死锁类型
MySQL死锁 :死锁检测、日志分析、解决方案
Oracle死锁 :死锁检测、处理机制、优化建议
PostgreSQL死锁 :死锁检测、处理方式、最佳实践
SQL Server死锁 :死锁检测、图形化分析、解决方案
其他数据库 :MongoDB、Redis等数据库的死锁处理
死锁预防 :设计原则、最佳实践
死锁排查 :排查工具、分析方法
架构实战 :高并发场景下的死锁处理
2. 死锁基础 2.1 死锁的概念 2.1.1 什么是死锁 死锁定义 : 死锁是指两个或多个事务在执行过程中,因争夺资源而造成的一种互相等待的现象。如果没有外力干预,这些事务将永远无法继续执行。
死锁示例 :
1 2 3 4 5 6 7 8 9 事务A: 1. 锁定资源X 2. 请求资源Y(等待) 事务B: 1. 锁定资源Y 2. 请求资源X(等待) 结果:事务A等待事务B释放Y,事务B等待事务A释放X → 死锁
2.1.2 死锁 vs 锁等待 锁等待(Lock Wait) :
一个事务等待另一个事务释放锁
是正常的并发控制机制
等待的事务最终会获得锁
死锁(Deadlock) :
多个事务相互等待
是异常情况
需要数据库自动检测并解决
2.2 死锁产生的必要条件 2.2.1 Coffman条件 1. 互斥条件(Mutual Exclusion) :
资源不能被多个事务同时使用
一个资源在同一时刻只能被一个事务持有
2. 请求与保持条件(Hold and Wait) :
事务在持有资源的同时请求其他资源
不会释放已持有的资源
3. 不剥夺条件(No Preemption) :
已获得的资源不能被强制释放
只能由持有资源的事务主动释放
4. 循环等待条件(Circular Wait) :
存在事务资源的循环等待链
T1等待T2,T2等待T3,…,Tn等待T1
说明 :
四个条件必须同时满足才会发生死锁
只要破坏其中一个条件,就可以避免死锁
2.3 死锁的类型 2.3.1 按资源类型分类 表级死锁 :
行级死锁 :
页级死锁 :
2.3.2 按等待关系分类 直接死锁 :
间接死锁 :
多个事务形成循环等待
T1等待T2,T2等待T3,T3等待T1
2.4 死锁的影响 2.4.1 对系统的影响 性能影响 :
死锁检测和处理消耗CPU资源
被回滚的事务需要重试
影响系统吞吐量
可用性影响 :
数据一致性 :
死锁回滚可能导致数据不一致
需要应用层处理回滚逻辑
3. MySQL死锁 3.1 MySQL死锁检测 3.1.1 死锁检测机制 MySQL死锁检测 :
InnoDB引擎自动检测死锁
使用等待图(Wait-for Graph)算法
检测到死锁后自动回滚一个事务
检测频率 :
3.1.2 查看死锁信息 查看最近死锁信息 :
1 2 SHOW ENGINE INNODB STATUS\G
查看死锁日志 :
1 2 3 4 5 SHOW VARIABLES LIKE 'innodb_print_all_deadlocks' ;SET GLOBAL innodb_print_all_deadlocks = ON ;
查看错误日志 :
1 2 tail -f /var/log/mysql/error.log | grep -i deadlock
3.2 MySQL死锁日志分析 3.2.1 死锁日志格式 典型死锁日志 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 LATEST DETECTED DEADLOCK ------------------------ 2024-01-15 10:30:45 0x7f8b8c0b9700 *** (1) TRANSACTION: TRANSACTION 12345, ACTIVE 5 sec starting index read mysql tables in use 1, locked 1 LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s) MySQL thread id 10, OS thread handle 140123456789, query id 100 localhost root updating UPDATE users SET name = 'Alice' WHERE id = 1 *** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 1 page no 3 n bits 72 index PRIMARY of table `test`.`users` trx id 12345 lock_mode X locks rec but not gap waiting Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0 0: len 4; hex 00000001; asc ;; 1: len 6; hex 000000000303; asc ;; 2: len 7; hex 82000001010110; asc ;; *** (2) TRANSACTION: TRANSACTION 12346, ACTIVE 3 sec starting index read mysql tables in use 1, locked 1 3 lock struct(s), heap size 1136, 2 row lock(s) MySQL thread id 11, OS thread handle 140123456790, query id 101 localhost root updating UPDATE users SET name = 'Bob' WHERE id = 2 *** (2) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 1 page no 3 n bits 72 index PRIMARY of table `test`.`users` trx id 12346 lock_mode X locks rec but not gap waiting Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0 0: len 4; hex 00000002; asc ;; 1: len 6; hex 000000000304; asc ;; 2: len 7; hex 82000001020120; asc ;; *** WE ROLL BACK TRANSACTION (2)
3.2.2 日志解读 关键信息 :
TRANSACTION:事务ID
LOCK WAIT:锁等待信息
WAITING FOR THIS LOCK:等待的锁
WE ROLL BACK TRANSACTION:回滚的事务
锁类型 :
X locks:排他锁
S locks:共享锁
rec but not gap:记录锁,非间隙锁
gap:间隙锁
next-key:Next-Key锁
3.3 MySQL死锁场景 3.3.1 场景1:相同资源不同顺序 1 2 3 4 5 6 7 8 9 10 11 BEGIN ;UPDATE users SET name = 'A' WHERE id = 1 ;UPDATE users SET name = 'A' WHERE id = 2 ;COMMIT ;BEGIN ;UPDATE users SET name = 'B' WHERE id = 2 ;UPDATE users SET name = 'B' WHERE id = 1 ;COMMIT ;
死锁原因 :
事务A锁定id=1,请求id=2
事务B锁定id=2,请求id=1
形成循环等待
解决方案 :
统一锁定顺序(都先锁id=1,再锁id=2)
使用应用层锁
3.3.2 场景2:Gap锁死锁 1 2 3 4 5 6 7 8 9 BEGIN ;SELECT * FROM users WHERE id BETWEEN 10 AND 20 FOR UPDATE ;INSERT INTO users (id, name) VALUES (15 , 'New' );BEGIN ;SELECT * FROM users WHERE id BETWEEN 10 AND 20 FOR UPDATE ;INSERT INTO users (id, name) VALUES (16 , 'New' );
死锁原因 :
解决方案 :
3.3.3 场景3:外键死锁 1 2 3 4 5 6 7 8 9 BEGIN ;INSERT INTO orders (user_id, amount) VALUES (1 , 100 );UPDATE users SET balance = balance - 100 WHERE id = 1 ;BEGIN ;UPDATE users SET balance = balance + 50 WHERE id = 1 ;INSERT INTO orders (user_id, amount) VALUES (1 , 50 );
死锁原因 :
解决方案 :
3.4 MySQL死锁解决方案 3.4.1 应用层处理 重试机制 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void updateWithRetry (int maxRetries) { int retries = 0 ; while (retries < maxRetries) { try { updateData(); return ; } catch (DeadlockLoserDataAccessException e) { retries++; if (retries >= maxRetries) { throw e; } Thread.sleep((long )(Math.random() * 100 )); } } }
统一锁定顺序 :
1 2 3 4 5 6 7 List<Integer> ids = Arrays.asList(2 , 1 , 3 ); ids.sort(Integer::compareTo); for (Integer id : ids) { updateById(id); }
3.4.2 数据库层优化 减少事务时间 :
1 2 3 4 5 BEGIN ;UPDATE ...;COMMIT ;
使用合适的隔离级别 :
1 2 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
优化索引 :
1 2 CREATE UNIQUE INDEX idx_user_id ON users(id);
4. Oracle死锁 4.1 Oracle死锁检测 4.1.1 死锁检测机制 Oracle死锁检测 :
自动检测死锁
使用等待图算法
检测到死锁后回滚一个事务
检测频率 :
4.1.2 查看死锁信息 查看死锁信息 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 SELECT * FROM v$lock WHERE block > 0 ;SELECT s.sid, s.serial#, s.username, s.program, l.type, l.id1, l.id2, l.lmode, l.request, l.block FROM v$session s, v$lock lWHERE s.sid = l.sidAND l.block > 0 ;
查看死锁历史 :
1 2 3 4 SELECT * FROM v$diag_alert_ext WHERE message_text LIKE '%deadlock%' ORDER BY originating_timestamp DESC ;
4.2 Oracle死锁场景 4.2.1 场景1:表锁死锁 1 2 3 4 5 6 7 LOCK TABLE users IN EXCLUSIVE MODE; LOCK TABLE users IN EXCLUSIVE MODE;
解决方案 :
4.2.2 场景2:外键死锁 1 2 3 4 5 6 7 UPDATE parent SET name = 'A' WHERE id = 1 ;UPDATE child SET name = 'A' WHERE parent_id = 1 ;UPDATE child SET name = 'B' WHERE parent_id = 1 ;UPDATE parent SET name = 'B' WHERE id = 1 ;
解决方案 :
4.3 Oracle死锁处理 4.3.1 手动处理死锁 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 SELECT s.sid, s.serial#, s.username, s.status FROM v$session s, v$lock l1, v$lock l2WHERE s.sid = l1.sidAND l1.id1 = l2.id1AND l1.id2 = l2.id2AND l1.request > 0 AND l2.lmode > 0 ;ALTER SYSTEM KILL SESSION 'sid,serial#' ;
4.3.2 预防死锁 统一锁定顺序 :
1 2 SELECT * FROM users WHERE id IN (2 , 1 , 3 ) ORDER BY id FOR UPDATE ;
减少锁定时间 :
5. PostgreSQL死锁 5.1 PostgreSQL死锁检测 5.1.1 死锁检测机制 PostgreSQL死锁检测 :
自动检测死锁
使用等待图算法
检测到死锁后回滚一个事务
检测频率 :
默认每1秒检测一次
可通过deadlock_timeout参数调整
5.1.2 查看死锁信息 查看死锁配置 :
1 2 3 4 5 SHOW deadlock_timeout;SET deadlock_timeout = '1s' ;
查看死锁日志 :
1 2 tail -f /var/log/postgresql/postgresql.log | grep -i deadlock
查看当前锁 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 SELECT locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted FROM pg_locksWHERE NOT granted;
5.2 PostgreSQL死锁场景 5.2.1 场景1:行锁死锁 1 2 3 4 5 6 7 8 9 10 11 BEGIN ;UPDATE users SET name = 'A' WHERE id = 1 ;UPDATE users SET name = 'A' WHERE id = 2 ;COMMIT ;BEGIN ;UPDATE users SET name = 'B' WHERE id = 2 ;UPDATE users SET name = 'B' WHERE id = 1 ;COMMIT ;
5.2.2 场景2:表锁死锁 1 2 3 4 5 6 7 8 9 10 11 BEGIN ;LOCK TABLE users IN ACCESS EXCLUSIVE MODE; COMMIT ;BEGIN ;LOCK TABLE users IN ACCESS EXCLUSIVE MODE; COMMIT ;
5.3 PostgreSQL死锁处理 5.3.1 应用层处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import psycopg2from psycopg2 import OperationalErrordef execute_with_retry (conn, query, max_retries=3 ): retries = 0 while retries < max_retries: try : cursor = conn.cursor() cursor.execute(query) conn.commit() return cursor.fetchall() except OperationalError as e: if 'deadlock' in str (e).lower(): retries += 1 if retries >= max_retries: raise time.sleep(0.1 * retries) else : raise
5.3.2 数据库层优化 使用合适的锁模式 :
1 2 3 4 5 SELECT * FROM users WHERE id = 1 FOR UPDATE ;SELECT * FROM users WHERE id = 1 FOR SHARE;
6. SQL Server死锁 6.1 SQL Server死锁检测 6.1.1 死锁检测机制 SQL Server死锁检测 :
自动检测死锁
使用等待图算法
选择代价最小的事务回滚
死锁优先级 :
1 2 3 4 SET DEADLOCK_PRIORITY LOW; SET DEADLOCK_PRIORITY NORMAL; SET DEADLOCK_PRIORITY HIGH;
6.1.2 查看死锁信息 启用死锁跟踪 :
1 2 3 DBCC TRACEON(1222 , -1 ); DBCC TRACEON(1204 , -1 );
查看死锁图 :
1 2 3 4 5 6 7 8 9 CREATE EVENT SESSION DeadlockGraph ON SERVERADD EVENT sqlserver.xml_deadlock_reportADD TARGET package0.event_file( SET filename = N'C:\temp\deadlock.xel' ) WITH (MAX_DISPATCH_LATENCY = 5 SECONDS);ALTER EVENT SESSION DeadlockGraph ON SERVER STATE = START ;
查看死锁信息 :
1 2 3 4 5 6 7 8 9 SELECT request_session_id, resource_type, resource_database_id, resource_associated_entity_id, request_mode, request_status FROM sys.dm_tran_locks;
6.2 SQL Server死锁场景 6.2.1 场景1:索引死锁 1 2 3 4 5 6 7 8 9 10 11 BEGIN TRANSACTION;UPDATE users SET name = 'A' WHERE id = 1 ;UPDATE users SET name = 'A' WHERE id = 2 ;COMMIT ;BEGIN TRANSACTION;UPDATE users SET name = 'B' WHERE id = 2 ;UPDATE users SET name = 'B' WHERE id = 1 ;COMMIT ;
6.2.2 场景2:锁升级死锁 1 2 3 4 5 6 7 8 9 BEGIN TRANSACTION;UPDATE users SET status = 1 WHERE id BETWEEN 1 AND 10000 ;BEGIN TRANSACTION;ALTER TABLE users ADD COLUMN new_col INT ;
6.3 SQL Server死锁处理 6.3.1 使用死锁优先级 1 2 3 4 5 6 SET DEADLOCK_PRIORITY HIGH;BEGIN TRANSACTION;COMMIT ;
6.3.2 使用NOLOCK提示(谨慎使用) 1 2 SELECT * FROM users WITH (NOLOCK) WHERE id = 1 ;
注意 :NOLOCK可能导致脏读,不推荐使用。
7. 其他数据库死锁 7.1 MongoDB死锁 7.1.1 MongoDB锁机制 MongoDB锁 :
全局写锁(早期版本)
文档级锁(WiredTiger引擎)
集合级锁(MMAPv1引擎)
7.1.2 MongoDB死锁场景 1 2 3 4 5 6 7 8 9 10 11 session.startTransaction (); db.users .updateOne ({id : 1 }, {$set : {name : 'A' }}); db.orders .insertOne ({user_id : 1 , amount : 100 }); session.commitTransaction (); session.startTransaction (); db.orders .insertOne ({user_id : 1 , amount : 50 }); db.users .updateOne ({id : 1 }, {$set : {name : 'B' }}); session.commitTransaction ();
解决方案 :
7.2 Redis死锁 7.2.1 Redis锁机制 Redis锁 :
单线程模型,不存在传统死锁
但可能出现业务层面的死锁
7.2.2 Redis分布式锁死锁 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 String lockKey = "lock:resource:1" ;String lockValue = UUID.randomUUID().toString();Boolean acquired = redisTemplate.opsForValue() .setIfAbsent(lockKey, lockValue, 30 , TimeUnit.SECONDS); try { if (acquired) { } } finally { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " + "return redis.call('del', KEYS[1]) else return 0 end" ; redisTemplate.execute(new DefaultRedisScript <>(script, Long.class), Collections.singletonList(lockKey), lockValue); }
死锁预防 :
7.3 其他数据库 7.3.1 DB2死锁 1 2 3 4 5 SELECT * FROM SYSIBMADM.SNAPDB;SELECT * FROM SYSIBMADM.LOCKS_HELD;
1 2 SELECT * FROM sysmaster:syslocks;
8. 死锁预防 8.1 设计原则 8.1.1 统一锁定顺序 原则 :
实现方式 :
1 2 3 4 5 6 7 List<Integer> ids = getIdsToUpdate(); ids.sort(Integer::compareTo); for (Integer id : ids) { updateById(id); }
8.1.2 减少锁定时间 原则 :
实现方式 :
1 2 3 4 5 6 7 8 9 10 11 try { connection.setAutoCommit(false ); updateData(); connection.commit(); } catch (SQLException e) { connection.rollback(); } finally { connection.setAutoCommit(true ); }
8.1.3 使用合适的隔离级别 隔离级别选择 :
隔离级别
死锁风险
性能
数据一致性
READ UNCOMMITTED
低
高
差
READ COMMITTED
中
中
中
REPEATABLE READ
高
低
好
SERIALIZABLE
最高
最低
最好
建议 :
大多数场景使用READ COMMITTED
需要强一致性时使用REPEATABLE READ
避免使用SERIALIZABLE(除非必要)
8.2 最佳实践 8.2.1 应用层实践 1. 使用应用层锁 :
1 2 3 4 5 6 7 8 9 10 11 12 public void updateWithLock (String resourceId) { String lockKey = "lock:" + resourceId; if (distributedLock.tryLock(lockKey, 10 , TimeUnit.SECONDS)) { try { updateResource(resourceId); } finally { distributedLock.unlock(lockKey); } } }
2. 实现重试机制 :
1 2 3 4 5 6 @Retryable(value = {DeadlockLoserDataAccessException.class}, maxAttempts = 3, backoff = @Backoff(delay = 100)) public void updateWithRetry () { }
3. 批量操作优化 :
1 2 3 4 5 6 7 8 9 10 public void batchUpdate (List<Integer> ids) { int batchSize = 100 ; for (int i = 0 ; i < ids.size(); i += batchSize) { List<Integer> batch = ids.subList(i, Math.min(i + batchSize, ids.size())); updateBatch(batch); } }
8.2.2 数据库层实践 1. 优化索引 :
1 2 3 4 5 CREATE UNIQUE INDEX idx_user_id ON users(id);CREATE INDEX idx_status ON users(status);
2. 分区表 :
1 2 3 4 5 6 7 8 9 10 CREATE TABLE orders ( id INT , user_id INT , amount DECIMAL (10 ,2 ) ) PARTITION BY RANGE (user_id) ( PARTITION p0 VALUES LESS THAN (1000 ), PARTITION p1 VALUES LESS THAN (2000 ), PARTITION p2 VALUES LESS THAN MAXVALUE );
3. 读写分离 :
1 2 3 4 5 SELECT * FROM users WHERE id = 1 ; UPDATE users SET name = 'A' WHERE id = 1 ;
9. 死锁排查 9.1 排查工具 9.1.1 MySQL排查工具 pt-deadlock-logger :
1 2 3 4 5 6 7 yum install percona-toolkit pt-deadlock-logger --user=root --password=xxx \ --host=localhost --create-dest-table \ --dest D=test ,t=deadlocks
查看死锁统计 :
1 2 3 4 5 6 SELECT COUNT (* ) as deadlock_count, MIN (ts) as first_deadlock, MAX (ts) as last_deadlock FROM deadlocks;
9.1.2 Oracle排查工具 AWR报告 :
1 2 @?/ rdbms/ admin/ awrrpt.sql
ASH报告 :
1 2 @?/ rdbms/ admin/ ashrpt.sql
9.1.3 PostgreSQL排查工具 pg_stat_statements :
1 2 3 4 5 6 7 CREATE EXTENSION pg_stat_statements;SELECT * FROM pg_stat_statements ORDER BY total_time DESC LIMIT 10 ;
9.2 排查方法 9.2.1 死锁日志分析 分析步骤 :
收集死锁日志
识别死锁事务
分析锁定顺序
找出根本原因
制定解决方案
分析工具 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import refrom collections import defaultdictdef analyze_deadlock_log (log_file ): """分析死锁日志""" deadlocks = [] current_deadlock = {} with open (log_file, 'r' ) as f: for line in f: if 'LATEST DETECTED DEADLOCK' in line: if current_deadlock: deadlocks.append(current_deadlock) current_deadlock = {} elif 'TRANSACTION' in line: pass elif 'WAITING FOR' in line: pass return deadlocks
9.2.2 实时监控 监控脚本 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #!/bin/bash while true ; do mysql -u root -p -e "SHOW ENGINE INNODB STATUS\G" | \ grep -A 50 "LATEST DETECTED DEADLOCK" > /tmp/deadlock.log if [ -s /tmp/deadlock.log ]; then echo "发现死锁!" cat /tmp/deadlock.log send_alert "发现数据库死锁" fi sleep 10 done
10. 架构实战 10.1 高并发场景设计 10.1.1 分布式锁方案 Redis分布式锁 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Component public class DistributedLock { @Autowired private RedisTemplate<String, String> redisTemplate; public boolean tryLock (String key, String value, long expireTime) { Boolean result = redisTemplate.opsForValue() .setIfAbsent(key, value, expireTime, TimeUnit.SECONDS); return Boolean.TRUE.equals(result); } public void unlock (String key, String value) { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " + "return redis.call('del', KEYS[1]) else return 0 end" ; redisTemplate.execute( new DefaultRedisScript <>(script, Long.class), Collections.singletonList(key), value ); } }
Zookeeper分布式锁 :
1 2 3 4 5 6 7 8 9 10 11 12 13 public class ZookeeperLock { private CuratorFramework client; public InterProcessMutex acquireLock (String path) { InterProcessMutex lock = new InterProcessMutex (client, path); try { lock.acquire(10 , TimeUnit.SECONDS); return lock; } catch (Exception e) { throw new RuntimeException ("获取锁失败" , e); } } }
10.1.2 数据库连接池优化 HikariCP配置 :
1 2 3 4 5 6 7 8 9 spring.datasource.hikari.maximum-pool-size =20 spring.datasource.hikari.minimum-idle =5 spring.datasource.hikari.connection-timeout =30000 spring.datasource.hikari.idle-timeout =600000 spring.datasource.hikari.max-lifetime =1800000 spring.datasource.hikari.leak-detection-threshold =60000
10.1.3 事务管理优化 使用声明式事务 :
1 2 3 4 5 6 7 8 9 10 11 12 @Service @Transactional( isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, timeout = 5, rollbackFor = Exception.class ) public class UserService { public void updateUser (User user) { } }
10.2 实战案例 10.2.1 案例1:订单系统死锁 场景 :
问题 :
多个订单同时操作同一用户账户
锁定顺序不一致导致死锁
解决方案 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Service public class OrderService { @Autowired private DistributedLock distributedLock; public void createOrder (Order order) { String lockKey = "lock:user:" + order.getUserId(); String lockValue = UUID.randomUUID().toString(); if (distributedLock.tryLock(lockKey, lockValue, 10 )) { try { reduceStock(order.getProductId(), order.getQuantity()); reduceBalance(order.getUserId(), order.getAmount()); saveOrder(order); } finally { distributedLock.unlock(lockKey, lockValue); } } else { throw new BusinessException ("系统繁忙,请稍后重试" ); } } }
10.2.2 案例2:账户转账死锁 场景 :
账户A向账户B转账
账户B向账户A转账
同时发生导致死锁
解决方案 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Service public class TransferService { public void transfer (int fromAccountId, int toAccountId, BigDecimal amount) { int firstLock = Math.min(fromAccountId, toAccountId); int secondLock = Math.max(fromAccountId, toAccountId); lockAccount(firstLock); try { lockAccount(secondLock); try { deductBalance(fromAccountId, amount); addBalance(toAccountId, amount); } finally { unlockAccount(secondLock); } } finally { unlockAccount(firstLock); } } }
11. 总结 11.1 核心要点
死锁基础 :四个必要条件、死锁类型
MySQL死锁 :检测机制、日志分析、解决方案
Oracle死锁 :检测机制、处理方式
PostgreSQL死锁 :检测机制、最佳实践
SQL Server死锁 :死锁图、优先级设置
其他数据库 :MongoDB、Redis等
死锁预防 :统一锁定顺序、减少锁定时间
死锁排查 :工具和方法
架构实战 :高并发场景下的死锁处理
11.2 架构师建议
设计原则 :
预防措施 :
监控告警 :
11.3 最佳实践
统一标准 :所有事务按照相同顺序获取锁
快速提交 :尽快释放锁,减少事务时间
合理隔离 :根据业务需求选择合适的隔离级别
监控告警 :实时监控死锁,及时处理
相关文章 :