在 Oracle 数据库中,​​死锁(Deadlock)​​ 是指两个或多个事务因互相持有对方需要的资源(如行锁、表锁)而无法继续执行的状态。此时,每个事务都在等待其他事务释放资源,形成“循环等待”,若没有外部干预,这些事务将永远无法完成。

锁类型:

1、共享锁(Share Locks,即S锁):加了共享锁的数据对象可以被其他事务读取,但不能修改。数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。

2、排它锁(Exclusive Locks,即X锁):当数据对象被加上排它锁时,其他的事务不能对它读取和修改。

3、独占锁:在共享锁的基础上,升级为独占锁。

4、更新锁:所有用户都可以读,但我将来可能会做更新操作,我已经获取了从共享锁(用来读)到排他锁(用来更新)的资格。一个事物只能有一个更新锁获此资格。

一、死锁的典型场景示例​​

以下是一个常见的死锁场景:

  1. ​事务 A​​ 执行:

    UPDATE accounts SET balance = balance - 100 WHERE user_id = 1; -- 行锁 R1
    UPDATE accounts SET balance = balance + 100 WHERE user_id = 2; -- 请求行锁 R2
  2. ​事务 B​​ 同时执行:

    UPDATE accounts SET balance = balance - 200 WHERE user_id = 2; -- 行锁 R2
    UPDATE accounts SET balance = balance + 200 WHERE user_id = 1; -- 请求行锁 R1

此时:

  • 事务 A 持有 R1,请求 R2;

  • 事务 B 持有 R2,请求 R1;

    两者形成循环等待,Oracle 会检测到死锁并自动回滚其中一个事务(通常选择“代价较小”的事务,即持有较少锁或 undo 数据较少的事务)。

Oracle 提供了多种工具和视图来检测死锁:

1. ​​AWR 报告与 ASH(活动会话历史)​​

死锁事件会被记录在 AWR(自动工作负载仓库)报告中,通过分析 AWR_REPORT_HTML可定位死锁发生的时间、涉及的事务和 SQL 语句。

ASH 存储了最近 7 天的会话活动数据,可通过 DBA_HIST_ACTIVE_SESS_HISTORY查看死锁相关的等待事件(如 enq: TX - row lock contention)。

在 Oracle 数据库中,​​死锁(Deadlock)​​ 是指两个或多个事务因互相持有对方需要的资源(如行锁、表锁)而无法继续执行的状态。此时,每个事务都在等待其他事务释放资源,形成“循环等待”,若没有外部干预,这些事务将永远无法完成。

死锁的核心是​​资源的循环等待​​,通常由以下因素共同作用导致:

1. ​​事务的并发操作​​

多个事务同时修改或查询同一组资源(如表、行),且操作顺序不一致。例如:

  • 事务 A 持有资源 X,并请求资源 Y;

  • 事务 B 持有资源 Y,并请求资源 X;

    此时,A 和 B 互相等待对方释放资源,形成死锁。

2. ​​锁的类型与模式冲突​​

Oracle 中锁的类型(如行锁、表锁)和模式(如共享锁 S、排他锁 X)若不兼容,会导致资源无法被其他事务获取。例如:

  • 事务 A 对行 R 加排他锁(X 锁,用于 UPDATE/DELETE);

  • 事务 B 尝试对行 R 加共享锁(S 锁,用于 SELECT ... FOR UPDATE)或排他锁;

    此时,事务 B 会被阻塞,直到事务 A 提交或回滚。若此时事务 B 已持有其他资源,而该资源被事务 A 需要,则可能引发死锁。

3. ​​长事务或未及时提交/回滚​​

事务执行时间过长(如批量操作、复杂查询),导致锁持有时间增加,其他事务等待时间变长,增加了循环等待的概率。

4. ​​索引缺失或全表扫描​​

若 SQL 语句未使用索引(如 UPDATE/DELETE 操作基于非索引列),Oracle 可能升级为表级锁(如 TM 锁),导致更大范围的资源竞争。例如:

  • 事务 A 对表 T 的某行执行 UPDATE(未使用索引),Oracle 可能升级为对表 T 加共享锁(S 锁);

  • 事务 B 尝试对表 T 加排他锁(X 锁,如执行 DROP TABLE),会被阻塞;

    若此时事务 A 还需要其他资源(如另一个表的锁),而该资源被事务 B 持有,则可能死锁。

5. ​​不合理的资源访问顺序​​

多个事务以不同顺序访问相同资源。例如:

  • 事务 A 先更新表 T1 的行 R1,再更新表 T2 的行 R2;

  • 事务 B 先更新表 T2 的行 R2,再更新表 T1 的行 R1;

    若两者同时执行,可能因互相持有对方需要的行锁而死锁。

​​二、死锁的典型场景示例​​

以下是一个常见的死锁场景:

  1. ​事务 A​​ 执行:

    UPDATE accounts SET balance = balance - 100 WHERE user_id = 1; -- 行锁 R1
    UPDATE accounts SET balance = balance + 100 WHERE user_id = 2; -- 请求行锁 R2
  2. ​事务 B​​ 同时执行:

    UPDATE accounts SET balance = balance - 200 WHERE user_id = 2; -- 行锁 R2
    UPDATE accounts SET balance = balance + 200 WHERE user_id = 1; -- 请求行锁 R1

此时:

  • 事务 A 持有 R1,请求 R2;

  • 事务 B 持有 R2,请求 R1;

    两者形成循环等待,Oracle 会检测到死锁并自动回滚其中一个事务(通常选择“代价较小”的事务,即持有较少锁或 undo 数据较少的事务)。

查询死锁位置并释放死锁:

select sess.sid,sess.serial#,lo.oracle_username, lo.os_user_name,ao.object_name, lo.locked_mode     
from v$locked_object lo,dba_objects ao,v$session sess 
where ao.object_id = lo.object_id and lo.session_id = sess.sid; 

alter system kill session '738,1429';

​​三、死锁的检测方法​​

Oracle 提供了多种工具和视图来检测死锁:

模式编号

模式名称

描述

1

NULL

空模式。某些情况下可以持有,但不代表任何锁资源。通常可以忽略

2

ROW-S (SS)

行级共享锁 (Sub Share)。也称为共享锁

3

ROW-X (SX)

行级独占锁 (Sub Exclusive)

4

SHARE (S)

表级共享锁

5

S/ROW-X (SSX)

共享行独占锁 (Share Sub Exclusive)

6

EXCLUSIVE (X)

独占锁

1、​检查数据库确定是否存在死锁

select username, lockwait, status, machine, program
from v$session
where sid in (select session_id from v$locked_object);
 
Username:死锁语句所用的数据库用户;
Lockwait:死锁的状态,如果有内容表示被死锁。
Status:状态,active表示被死锁
Machine:死锁语句所在的机器。
Program:产生死锁的语句主要来自哪个应用程序。

2、​确定死锁后,可以检查是sql产生死锁等待

select sql_text
from v$sql
where hash_value in
      (select sql_hash_value from v$session where sid in (select session_id from v$locked_object));

3、查询未提交的事务sql

select s.sid,
       s.username,
       s.osuser,
       s.program,
       to_char(s.LOGON_TIME, 'yyyymmdd hh24:mi:ss')       as LOGON_TIME,
       to_char(t.START_DATE, 'yyyymmdd hh24:mi:ss')       as START_DATE,
       s.status,
       (select q.SQL_TEXT from v$sql q where q.LAST_ACTIVE_TIME = t.START_DATE
                                         and rownum <= 1) as SQL_TEXT
from v$session s,
     v$transaction t
where s.sADDR = t.SES_ADDR;

4、找不到对应用户,可以通过kill掉死锁的session进程

SELECT l.SESSION_ID, l.OS_USER_NAME, s.USERNAME, l.OBJECT_ID, l.ORACLE_USERNAME
FROM v$locked_object l,
     v$session s
WHERE l.SESSION_ID = s.SID;

5、根据SessionID查询锁表语句

select sql_text
from v$sql
where hash_value in
      (select sql_hash_value from v$session where sid in (208));

6、查看死锁

select sess.sid, sess.serial#, lo.oracle_username, lo.os_user_name, ao.object_name, lo.locked_mode, SESS.machine
from v$locked_object lo,
     dba_objects ao,
     v$session sess
where ao.object_id = lo.object_id
  and lo.session_id = sess.sid;

7、对v$locked_object被锁对象进行查询

 SELECT l.session_id sid,
             s.serial#,
             l.locked_mode,
             l.oracle_username,
             l.os_user_name,
             s.machine,
             s.terminal,
             o.object_name,
             s.logon_time
        FROM v$locked_object l, all_objects o, v$session s
       WHERE l.object_id = o.object_id AND l.session_id = s.sid
       ORDER BY sid, s.serial#;

8、杀死该进程

alter system kill session '139,6151';

---139是查询结果中的SID字段,6151是查询结果中的SERIA