oracle 死锁和锁等待的区别,锁等待

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了oracle 死锁和锁等待的区别,锁等待相关的知识,希望对你有一定的参考价值。

楼主您好
所谓的锁等待:就是一个事务a对一个数据表进行ddl或是dml操作时,系统就会对该表加上表级的排它锁,此时其他的事务对该表进行操作的时候会等待a提交或是回滚后,才可以继续b的操作

所谓的死锁:当两个或多个用户相互等待锁定的数据时就会发生死锁,这时这些用户被卡在不能继续处理业务,oracle可以自动检测死锁并解决他们,通过回滚一个死锁中的语句,释放锁定的数据,回滚的话会遇到ora-00060
deadlock detected while waiting for resource

模拟锁等待:

两个事务a和b,分别创建t1,t2,并且初始化一条数据,

a 更改t1的数据,此时并不提交,这时候b更改相同的一列,此时b一直处于等待的状态

我们可以查询锁等待的内容:

wait_lock.sql

select
(select username from v$session where sid = a.sid) username,
a.sid,
(select serial# from v$session where sid = a.sid) serial#,
a.type,
a.id1,
a.id2,
a.lmode,
a.request,
a.block,
b.sid blocking_sid
from v$lock a,
( select * from v$lock
where request > 0
and type > 'MR'
) b
where a.id1 = b.id1(+)
and a.id2 = b.id2(+)
and a.lmode > 0
and a.type > 'MR'
order by username,a.sid,serial#,a.type

此时可以查询到锁等待的现象,最后一列不为空的就是等待的事件

此时我们可以跟a用户提示让其提交事务或是回滚,也可以直接杀死

alter system kill session 'sid,serial#';

保持现状不动,在a事务更改t2表此时在a事务会产生

SQL> update t1 set id=1000 where id=1;
update t1 set id=1000 where id=1
*
第 1 行出现错误:
ORA-00060: 等待资源时检测到死锁

此时oracle已经帮我解决了这个死锁问题

死锁的产生需要四个必须的条件:

1 ,mutual execution(互斥)资源不能被共享,只能由一个进程使用

2,hold and wait(请求并持续)已经得到资源的进程可以再次申请新的资源

3,no pre-emption(不可剥夺)已经分配的资源不能被相应的进程强制剥夺

4,circular wait(循环等待条件)系统中若干进程组成环路,该环路中的每个进程都在等待相邻进程正占用的资源

定位死锁:

系统级别的定位

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死锁来自于那个程序

语句级别的定位

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));

进程级别的定位

Select s.username,l.object_id,l.session_id,s.serial#,l.oracle_usrename,l.os_user_name,l.process from v$locked_object l,v$session s where l.session_id=s.sid;

处理死锁的一般策略

1,鸵鸟算法忽略该问题

2,定位死锁并且恢复

3,仔细对资源进行动态分配,避免死锁

4,破坏死锁中的一个条件

如果oracle解决不了的死锁,我们需要定位到进程级别,找到对应的sid和serial#

alter system kill 'sid,serail#'

失败的话,找到对应的进程强制关闭

Select p.spid from v$session s, v$process p where s.sid=xx and s.paddr=p.addr

ps -ef | grep spid

kill -9 xx

查询oracle的死锁

lock.sql

SELECT bs.username "Blocking User", bs.username "DB User",
ws.username "Waiting User", bs.SID "SID", ws.SID "WSID",
bs.serial# "Serial#", bs.sql_address "address",
bs.sql_hash_value "Sql hash", bs.program "Blocking App",
ws.program "Waiting App", bs.machine "Blocking Machine",
ws.machine "Waiting Machine", bs.osuser "Blocking OS User",
ws.osuser "Waiting OS User", bs.serial# "Serial#",
ws.serial# "WSerial#",
DECODE (wk.TYPE,
'MR', 'Media Recovery',
'RT', 'Redo Thread',
'UN', 'USER Name',
'TX', 'Transaction',
'TM', 'DML',
'UL', 'PL/SQL USER LOCK',
'DX', 'Distributed Xaction',
'CF', 'Control FILE',
'IS', 'Instance State',
'FS', 'FILE SET',
'IR', 'Instance Recovery',
'ST', 'Disk SPACE Transaction',
'TS', 'Temp Segment',
'IV', 'Library Cache Invalidation',
'LS', 'LOG START OR Switch',
'RW', 'ROW Wait',
'SQ', 'Sequence Number',
'TE', 'Extend TABLE',
'TT', 'Temp TABLE',
wk.TYPE
) lock_type,
DECODE (hk.lmode,
0, 'None',
1, 'NULL',
2, 'ROW-S (SS)',
3, 'ROW-X (SX)',
4, 'SHARE',
5, 'S/ROW-X (SSX)',
6, 'EXCLUSIVE',
TO_CHAR (hk.lmode)
) mode_held,
DECODE (wk.request,
0, 'None',
1, 'NULL',
2, 'ROW-S (SS)',
3, 'ROW-X (SX)',
4, 'SHARE',
5, 'S/ROW-X (SSX)',
6, 'EXCLUSIVE',
TO_CHAR (wk.request)
) mode_requested,
TO_CHAR (hk.id1) lock_id1, TO_CHAR (hk.id2) lock_id2,
DECODE
(hk.BLOCK,
0, 'NOT Blocking', /**//* Not blocking any other processes */
1, 'Blocking', /**//* This lock blocks other processes */
2, 'Global', /**//* This lock is global, so we can't tell */
TO_CHAR (hk.BLOCK)
) blocking_others
FROM v$lock hk, v$session bs, v$lock wk, v$session ws
WHERE hk.BLOCK = 1
AND hk.lmode != 0
AND hk.lmode != 1
AND wk.request != 0
AND wk.TYPE(+) = hk.TYPE
AND wk.id1(+) = hk.id1
AND wk.id2(+) = hk.id2
AND hk.SID = bs.SID(+)
AND wk.SID = ws.SID(+)
AND (bs.username IS NOT NULL)
AND (bs.username > 'SYSTEM')
AND (bs.username > 'SYS')
ORDER BY 1;

这些语句的执行最好是在plsql或是sqldeveloper如果是直接在数据库里面执行的需要格式化表,否则会很让你眼花的。
参考技术A 所谓的锁等待:就是一个事务a对一个数据表进行ddl或是dml操作时,系统就会对该表加上表级的排它锁,此时其他的事务对该表进行操作的时候会等待a提交或是回滚后,才可以继续b的操作
所谓的死锁:当两个或多个用户相互等待锁定的数据时就会发生死锁,这时这些用户被卡在不能继续处理业务,oracle可以自动检测死锁并解决他们,通过回滚一个死锁中的语句,释放锁定的数据

死锁的概念以及处理方式

死锁定义

在多线程编程中(两个或两个以上的线程)因为资源抢占而早场的线程无线等待的问题

在这里插入图片描述

线程和锁的关系(一对多)

  1. 一个线程可以拥有多把锁
  2. 一把锁只能被一个线程锁拥有

手写死锁代码

/**
 * Created with IntelliJ IDEA.
 * Description:死锁
 * User: starry
 * Date: 2021 -05 -09
 * Time: 10:00
 */
public class ThreadDemo36 {

    public static void main(String[] args) {
        //锁A(资源A)
        Object lockA = new Object();
        //锁B(资源B)
        Object lockB = new Object();
        //线程1
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lockA) {
                    System.out.println("线程1:得到了锁A");
                    try {
                        //休眠1s,让线程2先得到锁B
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程1:等待获取锁B");
                    //尝试获取锁B
                    synchronized (lockB) {
                        System.out.println("线程1:得到了锁B");
                    }
                }
            }
        },"t1");
        t1.start();

        //创建并启动线程2
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lockB) {
                    System.out.println("线程2:得到了锁B");
                    try {
                        //休眠1s,让线程1先得到锁A
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程2:等待获取锁A");
                    //尝试获取锁B
                    synchronized (lockA) {
                        System.out.println("线程2:获取到锁A");
                    }
                }
            }
        },"t2");
        t2.start();
    }

}

在这里插入图片描述
可以看到这时已经发生死锁了,程序一直执行不下去

检查测死锁工具

  • jconsole 工具
  • jvisualvm 工具
  • jmc 工具

具体使用请看我这篇文章:https://blog.csdn.net/starry1441/article/details/116742308

死锁造成的四个条件(同时满足)

  1. 互斥条件(一个资源只能被一个线程持有,当被一个线程持有之后就不能被其他线程持有)
  2. 请求拥有条件(一个线程持有了一个资源之后,又试图请求另一个资源)
  3. 不可剥夺条件(一个资源被一个线程拥有之后,如果这个线程不释放此资源,那么其他线程不能强制获得此资源)
  4. 环路等待条件(多个线程在获取资源时,形成了一个环形链条)

如何解决死锁

从以下两个条件如手,修改以下条件任意一个:

  1. 请求拥有条件
  2. 环路等待条件(最易实现)

我们将线程1 先获取 锁A和锁B,然后再让线程2 获得锁A和锁B,修改环路等待条件,我们来试一试吧
在这里插入图片描述

/**
 * Created with IntelliJ IDEA.
 * Description:解决死锁
 * User: starry
 * Date: 2021 -05 -09
 * Time: 10:00
 */
public class ThreadDemo37 {

    public static void main(String[] args) {
        //锁A(资源A)
        Object lockA = new Object();
        //锁B(资源B)
        Object lockB = new Object();
        //线程1
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lockA) {
                    System.out.println("线程1:得到了锁A");
                    try {
                        //休眠1s,让线程2先得到锁B
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程1:等待获取锁B");
                    //尝试获取锁B
                    synchronized (lockB) {
                        System.out.println("线程1:得到了锁B");
                    }
                }
            }
        },"t1");
        t1.start();

        //创建并启动线程2
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lockA) {
                    System.out.println("线程2:得到了锁B");
                    try {
                        //休眠1s,让线程1先得到锁A
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程2:等待获取锁A");
                    //尝试获取锁B
                    synchronized (lockB) {
                        System.out.println("线程2:获取到锁A");
                    }
                }
            }
        },"t2");
        t2.start();
    }

}

在这里插入图片描述
此时程序正常的结束了,线程1和线程2都分别获取到了锁A锁B

以上是关于oracle 死锁和锁等待的区别,锁等待的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 死锁与慢查询总结

对死锁的理解

死锁的概念以及处理方式

MySQL锁等待与死锁问题分析

MySQL锁等待与死锁问题分析

MySQL锁等待与死锁问题分析