
ORA-00060死锁——两个会话互相等对方那行社保系统里两个人同时操作同一个参保人的数据是最常见场景。A在修改参保信息B在改缴费记录——两个事务交叉等同一行30秒后Oracle自动杀一个。这篇文章告诉你怎么查到是谁杀了谁、为什么。文章目录ORA-00060死锁——两个会话互相等对方那行一、死锁长什么样二、社保系统的死锁场景三、死锁后怎么查四、预防死锁的编码规范五、unbreakable 死锁——APPLICATION级死锁一、死锁长什么样ORA-00060: deadlock detected while waiting for resourceOracle 自动检测到两个会话互相等待对方持有的锁。解决方式是随机杀掉一个让另一个继续。被杀的那个收到ORA-00060事务回滚。在alert.log里能看到完整的死锁图Deadlock graph: ---------Blocker(s)-------- ---------Waiter(s)--------- Resource Name process session holds waits process session holds waits TX-000b0017-0000041e 129 201 X 96 155 X TX-00090017-000003fb 96 155 X 129 201 X翻译会话201持有TX锁A在等B会话155持有TX锁B在等A。死锁。二、社保系统的死锁场景场景一两个人改同一个人操作员AUPDATE PERSON_INFO SET salary8000 WHERE id_card460027660601003——锁住这一行操作员BUPDATE PAYMENT_HISTORY SET gzze018000 WHERE id_card460027660601003——想锁同一个人等A释放操作员A接着UPDATE PAYMENT_HISTORY SET ylbf01... WHERE id_card460027660601003——想锁B已经锁了的行等B释放死锁 → Oracle杀B根因两个操作员同时操作同一个人的不同表事务太长。场景二外键没有索引-- 删单位时Oracle要检查PERSON_INFO表里有没有这个单位的员工DELETEFROMunit_infoWHEREunit_idA2209;-- 没有索引 → 全表扫描PERSON_INFO → 锁住所有扫描过的行Oracle 在执行外键约束检查时会对子表的相关行加共享锁。如果子表大且外键列没有索引——Oracle会锁住整张表。此时另一个人来更新PERSON_INFO的任意一行——死锁。修法CREATEINDEXidx_jbxx_dwbhONPERSON_INFO(unit_id);三、死锁后怎么查-- 查看alert.log中的最近死锁有完整trace-- 路径$ORACLE_BASE/diag/rdbms/orcl/orcl/trace/alert_orcl.log-- 实时锁等待检查SELECTddl.holding_session,ddl.holding_lock,ddl.waiting_sessionFROMdba_locks ddlWHEREddl.blocking_othersBlocking;-- 查出锁的SQLSELECTs.sid,s.serial#, s.username, s.status, q.sql_textFROMv$sessions,v$sqlqWHEREs.sql_idq.sql_idANDs.sidIN(阻塞的SID列表);杀掉阻塞会话如果业务允许ALTERSYSTEMKILLSESSIONsid,serial#IMMEDIATE;四、预防死锁的编码规范1. 统一加锁顺序// 所有修改单个人数据的接口按这个顺序加锁// 1. PERSON_INFO 2. PAYMENT_HISTORY 3. PERSON_ACCOUNT如果所有人都先锁A再锁B——永远不会交叉。死锁的根本原因是加锁顺序不一致。2. 缩短事务// 坏打开连接→查三张表→算金额→写三张表→COMMIT可能5秒// 好查三张表→算金额→开事务→写三张表→COMMIT事务内1秒3. 外键列必须建索引-- 查哪些外键列没有索引SELECTTABLE_NAME,CONSTRAINT_NAME,COLUMN_NAMEFROMDBA_CONS_COLUMNSWHERECONSTRAINT_NAMEIN(SELECTCONSTRAINT_NAMEFROMDBA_CONSTRAINTSWHERECONSTRAINT_TYPERANDOWNER)ANDNOTEXISTS(SELECT1FROMDBA_IND_COLUMNSWHERETABLE_OWNERANDCOLUMN_NAMEDBA_CONS_COLUMNS.COLUMN_NAME);五、unbreakable 死锁——APPLICATION级死锁还有一种死锁不是Oracle层面的是业务设计造成的。社保系统有一个工作流互斥——一个人不能同时跑两个同类型业务。实现方式是启动业务时在互斥表插入一条记录INSERT INTO hcmutex ...完成时删除。两个操作员同时点发起参保——两条INSERT同时进互斥表——后一条等前一条提交——前一条提交后后一条插入成功——但互斥检查过了——一个人同时跑了两个同类型业务。这不是锁的问题——是互斥检查在业务层的时机不对。修法用SELECT ... FOR UPDATE NOWAIT代替INSERT做互斥。-- 启动业务前先锁住互斥行SELECT1FROMhcmutexWHEREywbmLC01ANDid_card...FORUPDATENOWAIT;-- 如果返回resource busy——该业务已在运行拒绝✅ 亮点死锁debug从alert.log的死锁图出发配合v$session定位SQL补上预防死锁的三条编码规范。扩展方向分布式锁Redis/ZK在Oracle之外的死锁场景、RAC环境下的跨节点死锁。