SEian.G

苦练七十二变,笑对八十一难

  • 博客访问: 24109
  • 博文数量: 47
  • 用 户 组: 普通用户
  • 注册时间: 2016-03-12 13:43
  • 认证徽章:
个人简介

冰冻三尺非一日之寒,滴水穿石非一日之功。

ITPUB论坛APP

ITPUB论坛APP



APP发帖 享双倍积分

文章分类

全部博文(47)

文章存档

2017年(45)

2016年(1)

微信关注

IT168企业级官微



微信号:IT168qiye



系统架构师大会



微信号:SACC2013

订阅
热词专题

分类: Oracle


死锁存在的两个必要条件,一个是多任务工作的并发,另一个是共享资源的独占性需求。只要一个系统(广义系统)中存在这两个前提,我们就认为可能出现死锁的情况。

 

死锁描述的是一种状态。当两个或两个以上的任务单元在执行过程中,因为请求资源出现等待,因资源永远不能获得而相互等待的状态。如果没有外力的作用,死锁状态是会一直持续下去。死锁是伴随着多任务、并行操作产生的,在单任务情况下,一个任务单元可以使用并且独占所有资源,不存在资源等待的情况,所以也没有死锁的情况。在进入多任务系统环境下,多个任务之间存在资源共享和独占的需求,才可能出现死锁。

 

死锁最简单的例子:任务AB,资源12。任务A独占了资源1,任务B独占了资源2。此时,任务A要资源2,向任务B提出请求并等待。任务B要求资源1,并且也等待。AB两者均不释放所占有的资源,就造成了死锁。

 

Oracle的锁机制是建立在行锁一级,在插入、更新行一级信息的时候,会加入独占锁内容。死锁是数据库经常发生的问题,数据库一般不会无缘无故产生死锁,死锁通常都是由于我们应用程序的设计本身造成的。下面通过实验模拟一个死锁现象:两个session分别更新两条记录,在一个事务里再尝试更新对方记录

 

创建一张测试表tb_1

SCOTT@seiang11g>create table tb_1 as select * from emp;

Table created.

 

SCOTT@seiang11g>select * from tb_1;

 

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO

---------- ---------- --------- ---------- --------- ---------- ---------- ----------

      7369 SMITH      CLERK           7902 17-DEC-80        800                    20

      7499 ALLEN      SALESMAN        7698 20-FEB-81       1600        300         30

      7521 WARD       SALESMAN        7698 22-FEB-81       1250        500         30

      7566 JONES      MANAGER         7839 02-APR-81       2975                    20

      7654 MARTIN     SALESMAN        7698 28-SEP-81       1250       1400         30

      7698 BLAKE      MANAGER         7839 01-MAY-81       2850                    30

      7782 CLARK      MANAGER         7839 09-JUN-81       2450                    10

      7788 SCOTT      ANALYST         7566 19-APR-87       3000                    20

      7839 KING       PRESIDENT            17-NOV-81       5000                    10

      7844 TURNER     SALESMAN        7698 08-SEP-81       1500          0         30

      7876 ADAMS      CLERK           7788 23-MAY-87       1100                    20

      7900 JAMES      CLERK           7698 03-DEC-81        950                    30

      7902 FORD       ANALYST         7566 03-DEC-81       3000                    20

      7934 MILLER     CLERK           7782 23-JAN-82       1300                    10

 

分别在两个不同的会话进行更新操作

Session1

SCOTT@seiang11g>update tb_1 set ename='WJQ' where empno=7369;

1 row updated.

 

 

Session2

SCOTT@seiang11g>update tb_1 set ename='SEIANG' where empno=7499;

1 row updated.

 

Session1

再执行empno=7499字段的更新,此时语句已经hang住,需要等待会话2发出commit或rollback动作,表现为停住操作,不断的轮询资源

 

SCOTT@seiang11g>update tb_1 set ename='SEIANG_WJQ' where empno=7499;

update tb_1 set ename='SEIANG_WJQ' where empno=7499

       *

ERROR at line 1:

ORA-00060: deadlock detected while waiting for resource

 

注意:Oracle此处的处理:只是驳回了一方的请求,并没有回滚该请求,也没有将另一方的hange状态解除。

 

Session2

一旦执行更新,Session1就会马上报错ORA-00060,当前session被hang住

SCOTT@seiang11g>update tb_1 set ename='WJQORA' where empno=7369;

 

思考:
显然,这个过程中是Oracle内部的防护机制起了作用,防止了系统中死锁的发生。在Oracle中,存在某种轮询的机制,随时检查系统中出现的多会话被hange住的情况,一旦发生,就将一个session被hange住的请求退回,抛出ORA-00060错误。

在两个session互锁的情况下,Oracle死锁检测程序可以起作用。那么,如果死锁结构复杂的时候,是不是Oracle的检测机制会失效。在使用4-5个session进行检查,虽然检测起效的时间有长有短,但最后都是将死锁的状态加以解除。

有一点需要注意,Oracle解决死锁的方式只是将请求拒绝,并不是将事务回滚。所以,在解决死锁之后,其他被hange住的session依然还是被hange住。所以,从应用程序和PL/SQL的角度,如果接受到了ORA-00060错误,应该做的工作就是回滚当前事务,解决整体的资源争用现象。

 

 

告警日志中的相关信息:

[oracle@seiang11g trace]$ tail -f alert_seiang11g.log

Tue Sep 19 06:06:01 2017

Archived Log entry 20 added for thread 1 sequence 24 ID 0xc94cf059 dest 1:

Tue Sep 19 14:51:24 2017

ORA-00060: Deadlock detected. More info in file /u01/app/oracle/diag/rdbms/oradb11g/seiang11g/trace/seiang11g_ora_12360.trc.

 

分析Trace日志文件中记录的相关信息,可以找到相关的死锁语句:

[oracle@seiang11g ~]$ more /u01/app/oracle/diag/rdbms/oradb11g/seiang11g/trace/seiang11g_ora_12360.trc

Trace file /u01/app/oracle/diag/rdbms/oradb11g/seiang11g/trace/seiang11g_ora_12360.trc

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

With the Partitioning, OLAP, Data Mining and Real Application Testing options

ORACLE_HOME = /u01/app/oracle/product/11.2.0/dbhome_1

System name:    Linux

Node name:      seiang11g.comsys.com

Release:        3.10.0-514.el7.x86_64

Version:        #1 SMP Tue Nov 22 16:42:41 UTC 2016

Machine:        x86_64

VM name:        VMWare Version: 6

Instance name: seiang11g

Redo thread mounted by this instance: 1

Oracle process number: 19

Unix process pid: 12360, image: oracle@seiang11g.comsys.com (TNS V1-V3)

 

 

*** 2017-09-19 14:51:24.355

*** SESSION ID:(52.3879) 2017-09-19 14:51:24.355

*** CLIENT ID:() 2017-09-19 14:51:24.355

*** SERVICE NAME:(SYS$USERS) 2017-09-19 14:51:24.355

*** MODULE NAME:(SQL*Plus) 2017-09-19 14:51:24.355

*** ACTION NAME:() 2017-09-19 14:51:24.355

 

*** 2017-09-19 14:51:24.355

DEADLOCK DETECTED ( ORA-00060 )

 

[Transaction Deadlock]

 

The following deadlock is not an ORACLE error. It is a

deadlock due to user error in the design of an application

or from issuing incorrect ad-hoc SQL. The following

information may aid in determining the deadlock:

 

Deadlock graph:

                       ---------Blocker(s)--------  ---------Waiter(s)---------

Resource Name          process session holds waits  process session holds waits

TX-00090016-000004ec        19      52     X             30      53           X

TX-0005001f-0000050a        30      53     X             19      52           X

 

session 52: DID 0001-0013-000032A7      session 53: DID 0001-001E-00000A06

session 53: DID 0001-001E-00000A06      session 52: DID 0001-0013-000032A7

 

Rows waited on:

  Session 52: obj - rowid = 00015A9A - AAAVqaAAEAAAAITAAB

  (dictionary objn - 88730, file - 4, block - 531, slot - 1)

  Session 53: obj - rowid = 00015A9A - AAAVqaAAEAAAAITAAA

  (dictionary objn - 88730, file - 4, block - 531, slot - 0)

 

----- Information for the OTHER waiting sessions -----

Session 53:

  sid: 53 ser: 4307 audsid: 155632 user: 83/SCOTT

    flags: (0x45) USR/- flags_idl: (0x1) BSY/-/-/-/-/-

    flags2: (0x40009) -/-/INC

  pid: 30 O/S info: user: oracle, term: UNKNOWN, ospid: 12721

    image: oracle@seiang11g.comsys.com (TNS V1-V3)

  client details:

    O/S info: user: oracle, term: pts/5, ospid: 12703

    machine: seiang11g.comsys.com program: sqlplus@seiang11g.comsys.com (TNS V1-V3)

    application name: SQL*Plus, hash value=3669949024

  current SQL:

  update tb_1 set ename='WJQORA' where empno=7369

 

----- End of information for the OTHER waiting sessions -----

 

Information for THIS session:

----- Current SQL Statement for this session (sql_id=g3gy243v5tj1c) -----

update tb_1 set ename='SEIANG_WJQ' where empno=7499

 

 

根据trace信息,查询:

SYS@seiang11g> select addr,pid,spid,username,serial# from v$process t where t.PID in (19,30);

 

ADDR                    PID SPID                     USERNAME           SERIAL#

---------------- ---------- ------------------------ --------------- ----------

00000000AA51A628         19 12360                    oracle                  98

00000000AA525E10         30 12721                    oracle                  27

 

通过dba_blockers表中的HOLDING_SESSION字段可以查询到hang住会话的ID
SYS@seiang11g>select * from dba_blockers;

 

HOLDING_SESSION

---------------

             52


Oracle死锁发生
 
1、  Oracle对查询不加锁。Oracle本身支持多版本一致读,如果当前的数据块正在被修改(独占)而并未提交,Oracle会根据SCN查找日志和Undo机制,找到合适SCN的版本返回结果。所以,在查询的时候,是不需要加锁的。
2、  Oracle数据操作使用行级锁(本质上是事务锁),实现最小粒度的独占范围。Oracle在DML的时候,只会对操作的行进行独占锁定,而不是过大的数据单元(如页page)。这样,就保证了不会引起过多的独占资源。

同时,Oracle本身也提供了死锁监视程序功能,能及时发现死锁状态,并自动的进行解锁。在这些机制下,Oracle认为死锁发生的概率很低(起码自身不会引起死锁)。只有一种情况会引发死锁,就是开发人员手工提高加锁的级别。

Oracle对于SQL的拓展中,有一部分是显示对象加锁。从lock table XXX到select XXX for update。这些操作都会引起对对象锁级别的提升,这些都可能引发死锁的发生。

 

 

定位死锁语句:


1)执行下面SQL,先查看哪些表被锁住了:

SYS@seiang11g>select c.owner, c.object_name, c.object_type, b.sid,

  2  b.serial#, b.lockwait, b.status, b.osuser, b.machine, b.process, b.program

  3  from v$locked_object a ,

  4  v$session b,

  5  dba_objects c

  6  where b.sid = a.session_id

  7  and a.object_id = c.object_id;

 

OWNER      OBJECT_NAME     OBJECT_TYPE                SID    SERIAL# LOCKWAIT         STATUS   OSUSER          MACHINE    PROCESS                   PROGRAM

---------- --------------- ------------------- ---------- ---------- ---------------- -------- --------------- -------------------- ------------------------ -------------------------

SCOTT      TB_1            TABLE                       53       4307 00000000AAD39888 ACTIVE   oracle          seiang11g.comsys.com 12703                   sqlplus@seiang11g.comsys.

                                                                                                                           com (TNS V1-V3)

 

SCOTT      TB_1            TABLE                       52       3879                  INACTIVE oracle          seiang11g.comsys.com 12359                   sqlplus@seiang11g.comsys.

                                                                                                                           com (TNS V1-V3)

 

如果有输出的结果,则说明有死锁,且能看到死锁的机器是哪一台。字段说明:

OWNER:死锁语句所用的数据库用户,这里是HR用户。
OBJECT_NAME:产生死锁的对象,这里是TEST表。
OBJECT_TYPE:产生死锁的对象类型,这里是TABLE。
SID:SESSION标识,常用于连接其它列。
SERIAL#:SID有可能会重复,当两个session的SID重复时,SERIAL#用来区别session
LOCKWAIT:可以通过这个字段查询出当前正在等待的锁的相关信息,如果有内容表示被死锁或者有锁等待事件。
STATUS:用来判断session状态。ACTIVE:正执行SQL语句。INACTIVE:等待操作。KILLED:被标注为杀死。
OSUSER:客户端操作系统用户名。
MACHINE:客户端操作系统的机器名。
PROCESS:客户端进程的ID。
PROGRAM:客户端执行的应用程序。

 

2)查看引起死锁的会话

SYS@seiang11g>select b.username,b.sid,b.serial#,logon_time

  2         from v$locked_object a,v$session b

  3     where a.session_id = b.sid order by b.logon_time;

 

USERNAME                              SID    SERIAL# LOGON_TIM

------------------------------ ---------- ---------- ---------

SCOTT                                  52       3879 19-SEP-17

SCOTT                                  53       4307 19-SEP-17

 

2)用dba用户执行以下语句,可以查看到被死锁的语句。

SYS@seiang11g>select sql_text from v$sql where (address,hash_value) in

  2  (select sql_address,sql_hash_value from v$session where sid in

  3  (select session_id from v$locked_object));

 

SQL_TEXT

----------------------------------------------------------------------------------------------------

update tb_1 set ename='WJQORA' where empno=7369

select b.username,b.sid,b.serial#,logon_time        from v$locked_object a,v$session b    where a.se

ssion_id = b.sid order by b.logon_time

 

或者是:

SYS@seiang11g>select sql_text from v$sql where (address,hash_value) in

  2  (select sql_address,sql_hash_value from v$session where lockwait is not null);

 

SQL_TEXT

----------------------------------------------------------------------------------------------------

update tb_1 set ename='WJQORA' where empno=7369

 

这里查出来出现死锁的语句和之前的测试结果一致。

 

3)查看被阻塞的会话

SYS@seiang11g>select * from dba_waiters;

 

WAITING_SESSION HOLDING_SESSION LOCK_TYPE    MODE_HELD  MODE_REQUESTED    LOCK_ID1   LOCK_ID2

--------------- --------------- ------------ ---------- --------------- ---------- ----------

             53              52 Transaction  Exclusive  Exclusive           589846       1260

 

 

那么,如果出现死锁的情况,我们如何处理?

 

解决死锁问题

 

方法一:提交或回滚死锁的会话

这里Session1出现死锁,只要执行commit或者rollback就可以解除死锁,只不过事务中第一个SQL执行成功,第二个SQL执行失败。

Session1

SCOTT@seiang11g>commit;:

Commit complete.

 

此时,死锁状态解除:

SYS@seiang11g>select sid, serial#, username, command, lockwait, osuser from v$session where lockwait is not null;

 

no rows selected

 

方式二:重新启动数据库

 

 

方式三:杀掉阻塞的会话

(1)查找死锁的进程:

SYS@seiang11g>select sid, serial#, username, command, lockwait, osuser from v$session where lockwait is not null;

 

       SID    SERIAL# USERNAME                          COMMAND LOCKWAIT         OSUSER

---------- ---------- ------------------------------ ---------- ---------------- ---------------

        53       4307 SCOTT                                   6 00000000AAD39888 oracle

 

2)kill掉这个死锁的进程:

SYS@seiang11g>alter system kill session '53,4307';

 

 

总结:

原因分析:

首先死锁是怎么发生的:
     简单说,两个或多个并发事务相互等待,互补想让,没有外力就无法继续下去,这就制造了死锁。数据库检测到死锁时,就会将死锁的各个事务回滚,并抛出ORA-00060异常。所以上面报错出现的情况极少,将死锁解除后又可以正常运行。

 

解决思路:

 死锁是无法根除的,特别在高并发的系统中。只有尽可能优化速度,减少互相等待的机会。原则为:执行速度越快越好,访问资源时锁的范围越小越好。根据这个原则就可以优化我们的sql,将负责的sql拆分,若果业务允许的情况下。还有事务越小越好。

 

解决技巧:

1、出现死锁异常后,手工将死锁解开。
2、找出造成死锁的sql:
      (1)直接看日志:程序中日志做的很详细的话,是能够找到具体哪个sql报的错,操作的哪个表,还有别的模块也操作这个表,线程,并发的程序也会引起。
      (2)通过oracle的后台v$session表 和 v$sql 的分析 找到。  
3、对sql进行优化。 

 

 

参考链接:

http://blog.itpub.net/17203031/viewspace-682115/

http://www.linuxidc.com/Linux/2012-07/66890.htm

http://blog.itpub.net/31394774/viewspace-2144941/

 

作者:SEian.G(苦练七十二变,笑对八十一难)



阅读(401) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册