Oracle 常见死锁发生的原因以及解决方法

参考:https://blog.csdn.net/fenyu8/article/details/53811686

一.删除和更新之间引起的死锁

造成死锁的原因就是多个线程或进程对同一个资源的争抢或相互依赖。这里列举一个对同一个资源的争抢造成死锁的实例。

死锁现象的重现:

1.在sql 窗口 执行:select * from TEST_TABLE t for update; – 加行级锁 并对内容进行修改,不要提交
在这里插入图片描述

查询死锁:

select s.username,l.object_id, l.session_id,s.serial#, s.lockwait,s.status,s.machine,s.program from v$session s,v$locked_object l where s.sid = l.session_id;

字段说明:

Username:死锁语句所用的数据库用户;
SID: session identifier, session 标示符,session 是通信双方从开始通信到通信结束期间的一个上下文。
SERIAL#: sid 会重用,但是同一个sid被重用时,serial#会增加,不会重复。
Lockwait:可以通过这个字段查询出当前正在等待的锁的相关信息。
Status:用来判断session状态。Active:正执行SQL语句。Inactive:等待操作。Killed:被标注为删除。
Machine: 死锁语句所在的机器。
Program: 产生死锁的语句主要来自哪个应用程序。

此时有一行在命令在等待操作Inactive
在这里插入图片描述

查看引起死锁的语句:

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

此时没有死锁的语句。

2.另开一个command窗口,执行:delete from testLock WHERE ID=1;

此时发生死锁(注意此时要另开一个窗口,不然会提示:POST THE CHANGE RECORD TO THE DATABASE. 点yes 后强制commit):
在这里插入图片描述

死锁查看:

SQL>  select s.username,l.object_id, l.session_id,s.serial#, s.lockwait,s.status,s.machine,s.program from v$session s,v$locked_object l where s.sid = l.session_id;

在这里插入图片描述
查看引起死锁的语句:

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

在这里插入图片描述
查出以下语句死锁:
update TEST_TABLE t set t.name=‘ASDF’,t.time=sysdate where t.id=800;

死锁的处理:alter system kill session ‘session_id,serial#’;
alter system kill session ‘159,6197’;

再查看一下死锁,会发现已经没有stauts为active的记录了,发生死锁的语句已经被终止。

二.在外键上没有加索引引起的死锁

客户的10.2.0.4 RAC for AIX环境频繁出现ORA-60死锁问题,导致应用程序无法顺利执行。
经过一系列的诊断,发现最终问题是由于外键上没有建立索引所致,由于程序在主子表上删除数据,缺少索引导致行级锁升级为表级锁,最终导致大量的锁等待和死锁。
下面通过一个例子简单模拟一下问题:
SQL> create table t_p (id number primary key, name varchar2(30));
Table created.
SQL> create table t_f (fid number, f_name varchar2(30), foreign key (fid) references t_p);
Table created.
SQL> insert into t_p values (1, ‘a’);
1 row created.
SQL> insert into t_f values (1, ‘a’);
1 row created.
SQL> insert into t_p values (2, ‘b’);
1 row created.
SQL> insert into t_f values (2, ‘c’);
1 row created.
SQL> commit;
Commit complete.
SQL> delete t_f where fid = 2;
1 row deleted.
这时在会话2同样对子表进行删除:
SQL2> delete t_f where fid = 1;
1 row deleted.
回到会话1执行主表的删除:
SQL> delete t_p where id = 2;
会话被锁,回到会话2执行主表的删除:
SQL2> delete t_p where id = 1;
会话同样被锁,这时会话1的语句被回滚,出现ORA-60死锁错误:
delete t_p where id = 2
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
SQL> rollback;
Rollback complete.
将会话1操作回滚,会话2同样回滚并建立外键列上的索引:
1 row deleted.
SQL2> rollback;
Rollback complete.
SQL2> create index ind_t_f_fid on t_f(fid);
Index created.
重复上面的步骤会话1删除子表记录:
SQL> delete t_f where fid = 2;
1 row deleted.
会话2删除子表记录:
SQL2> delete t_f where fid = 1;
1 row deleted.
会话1删除主表记录:
SQL> delete t_p where id = 2;
1 row deleted.
会话2删除主表记录:
SQL> delete t_p where id = 1;
1 row deleted.
所有的删除操作都可以成功执行,关于两种情况下锁信息的不同这里就不深入分析了,重点就是在外键列上建立索引。
虽然有一些文章提到过,如果满足某些情况,可以不在外键列上建立的索引,但是我的观点一向是,既然创建了外键,就不要在乎再多一个索引,因为一个索引所增加的代价,与缺失这个索引所带来的问题相比,是微不足道的。

ORACLE里锁有以下几种模式: 0:none 1:null 空 2:Row-S 行共享(RS):共享表锁 3:Row-X 行专用(RX):用于行的修改 4:Share 共享锁(S):阻止其他DML操作 5:S/Row-X 共享行专用(SRX):阻止其他事务操作 6:exclusive 专用(X):独立访问使用 数字越大锁级别越高, 影响的操作越多。 一般的查询语句如select ... from ... ;是小于2的锁, 有时会在v$locked_object出现。 select ... from ... for update; 是2的锁。 当对话使用for update子串打开一个游标时, 所有返回集中的数据行都将处于行级(Row-X)独占式锁定, 其他对象只能查询这些数据行,不能进行update、delete或select...for update操作。 insert / update / delete ... ; 是3的锁。 没有commit之前插入同样的一条记录会没有反应, 因为后一个3的锁会一直等待上一个3的锁, 我们必须释放掉上一个才能继续工作。 创建索引的时候也会产生3,4级别的锁。 locked_mode为2,3,4不影响DML(insert,delete,update,select)操作, 但DDL(alter,drop等)操作会提示ora-00054错误。 有主外键约束时 update / delete ... ; 可能会产生4,5的锁。 DDL语句时是6的锁。 以DBA角色, 查看当前数据库里锁的情况可以用如下SQL语句: select object_id,session_id,locked_mode from v$locked_object; select t2.username,t2.sid,t2.serial#,t2.logon_time from v$locked_object t1,v$session t2 where t1.session_id=t2.sid order by t2.logon_time; 如果有长期出现的一列,可能是没有释放的锁。 我们可以用下面SQL语句杀掉长期没有释放非正常的锁: alter system kill session 'sid,serial#'; 如果出现了锁的问题, 某个DML操作可能等待很久没有反应。 当你采用的是直接连接数据库的方式, 也不要用OS系统命令 $kill process_num 或者 $kill -9 process_num来终止用户连接, 因为一个用户进程可能产生一个以上的锁, 杀OS进程并不能彻底清除锁的问题。 记得在数据库级别用alter system kill session 'sid,serial#';杀掉不正常的锁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值