2、检测并解决冲突
我们先执行下面的语句获得当前session的SID号,然后执行DML语句:
SQL> select sid from v$mystat where rownum=1;
SID
----------
159
SQL> update employees set last_name=last_name||'a'
where department_id=60;
6 rows updated.
结果显示出,该事务更新了6条记录。然后我们来检查该事务的状态:
SQL> select xidusn,xidslot,xidsqn,status from v$transaction;
xIDUSN xIDSLOT xIDSQN STATUS
---------- ---------- ---------- ----------------
6 36 5839 ACTIVE
我们看到该事务使用了6号回滚段,槽号为36号。然后我们观察v$lock视图:
SQL> select sid,type,id1,id2,decode(lmode,0,'None',1,'Null',
2,'Row share',
2 3,'Row Exclusive',4,'Share',5,'Share Row Exclusive',
6,'Exclusive') lock_mode,
3 decode(request,0,'None',1,'Null',2,'Row share',
4 3,'Row Exclusive',4,'Share',5,'Share Row Exclusive',
6,'Exclusive') request_ mode,block
5 from v$lock
6 where sid=159;
SID TYPE ID1 ID2 LOCK_MODE REQUEST_MODE BLOCK
---- ----- -------- ---------- -------------- ------------- ------
159 TM 53777 0 Row Exclusive None 0
159 TX 393252 5839 Exclusive None 0
从输出结果中我们可以看到两个级别的锁:TX和TM。对于TX来说,Oracle只有一种模式,也就是排他模式。同时我们应该会注意到,该事务更新了6条记录, 但是只有一个行级锁,并不是会产生6个行级锁。事实上,TX锁也叫做事务锁(transaction,其中的X表示事务)。也就是说,一个事务只会产生一个TX锁。 而对于TM锁定来说,其锁定模式为行级排他锁。
对于TM锁来说,ID1表示被锁定的对象的对象ID,ID2始终为0。如下所示:
SQL> select object_name from dba_objects where object_id=53777;
OBJECT_NAME
-------------------------------
EMPLOYEES
对于TX锁来说,ID1表示事务使用的回滚段编号以及在事务表中对应的记录编号,ID2表示该记录编号被重用的次数(wrap)。使用下面的SQL语句将上面的 ID1(393252)转换为直观的数字:
SQL> select trunc(393252/power(2,16)) as undo_blk#,
2 bitand(393252,to_number('ffff','xxxx')) + 0
as slot#
3 from dual;
UNDO_BLK# SLOT#
---------- ----------
6 36
可以看到,该值与v$transaction中记录的值是一样的。
我们再启动一个session,获得SID号以后,执行下面的DML语句:
SQL> select sid from v$mystat where rownum=1;
SID
----------
131
SQL> update employees set last_name=last_name||'b' where
department_id=60;
131号session所发出的DML语句被阻塞了,无法执行下去。我们执行下面的SQL语句:
SQL> select sid,type,id1,id2,decode(lmode,0,'None',1,'Null',2,'
Row share',
2 3,'Row Exclusive',4,'Share',5,'Share Row Exclusive',
6,'Exclusive') lock_mode,
3 decode(request,0,'None',1,'Null',2,'Row share',
4 3,'Row Exclusive',4,'Share',5,'Share Row Exclusive',
6,'Exclusive') request_ mode,block
5 from v$lock
6 where sid in(159,131)
7 order by sid;
SID TYPE ID1 ID2 LOCK_MODE REQUEST
_MODE BLOCK
---- ----- -------- ---------- -------------- -------------
-------
131 TM 53777 0 Row Exclusive None 0
131 TX 393252 5839 None Exclusive 0
159 TX 393252 5839 Exclusive None 1
159 TM 53777 0 Row Exclusive None 0
可以看到,131号session在TX锁的请求模式(request_mode列)上为排他锁,因为131号session要更新记录,就必须获得被更新记录上的排他锁。但是由于 这些记录正在被159号session锁定,没能获得行级锁,因此其锁定模式(lock_mode)为None。有趣的是,这时131号session的事务信息(ID1与ID2的值)与 159号session的事务信息是完全一样的,因为131号session的事务由于无法获得锁定,因此还没能开始。131号session在TM锁定上,已经获得了行级排他锁 。因为159号session在表上添加的RX锁定,并不会阻止其他session在相同表上添加RX锁。
我们可以再次启动另一个session,并执行下面的DML语句:
SQL> select sid from v$mystat where rownum=1;
SID
----------
135
SQL> update employees set last_name=last_name||'c' where
department_id=60;
这时,我们查询v$enqueue_lock来获得锁定队列中的session信息。
SQL> select sid,type,decode(request,0,'None',1,'Null',2,'Row share',
2 3,'Row Exclusive',4,'Share',5,'Share Row Exclusive',6,
'Exclusive') request_mode
3 from v$enqueue_lock
4 where sid in(131,135);
SID TYPE REQUEST_MODE
---------- ---- -------------------
131 TX Exclusive
135 TX Exclusive
可以看到,131号session先进入队列,而135号session后进入队列。我们还可以使用下面的SQL语句将session之间的阻塞关系显示得更加清楚:
SQL> select a.sid blocker_sid,a.serial#,a.username as blocker_username,
2 b.type,decode(b.lmode,0,'None',1,'Null',2,'Row share',
3 3,'Row Exclusive',4,'Share',5,'Share Row Exclusive',6,
'Exclusive') lock_mode,
4 b.ctime as time_held,c.sid as waiter_sid,
5 decode(c.request,0,'None',1,'Null',2,'Row share',
6 3,'Row Exclusive',4,'Share',5,'Share Row Exclusive',6,
'Exclusive') request_mode,
7 c.ctime time_waited
8 from v$lock b, v$enqueue_lock c, v$session a
9 where a.sid = b.sid
10 and b.id1 = c.id1(+)
11 and b.id2 = c.id2(+)
12 and c.type(+) = 'TX'
13 and b.type = 'TX'
14 and b.block = 1
15 order by time_held, time_waited;
BLOCKER_SID SERIAL# BLOCKER_USERNAME TY LOCK_MODE
----------- -------- ---------------- ----- ----------
159 5 HR TX Exclusive
159 5 HR TX Exclusive
TIME_HELD WAITER_SID REQUEST_MODE TIME_WAITED
--------- ---------- ----------- -----------
5488 135 Exclusive 5
5488 131 Exclusive 2770
从输出结果中可以很明显地看出,159号session阻塞了135号和131号session。要解决该锁定冲突,我们只需要让159号session提交事务即可,提交以后再次 查询v$lock:
SQL> select a.sid blocker_sid,a.serial#,a.username as blocker_username,
2 b.type,decode(b.lmode,0,'None',1,'Null',2,'Row share',
3 3,'Row Exclusive',4,'Share',5,'Share Row Exclusive',6,
'Exclusive') lock_mode,
4 b.ctime as time_held,c.sid as waiter_sid,
5 decode(c.request,0,'None',1,'Null',2,'Row share',
6 3,'Row Exclusive',4,'Share',5,'Share Row Exclusive',
6,'Exclusive') request_ mode,
7 c.ctime time_waited
8 from v$lock b, v$enqueue_lock c, v$session a
9 where a.sid = b.sid
10 and b.id1 = c.id1(+)
11 and b.id2 = c.id2(+)
12 and c.type(+) = 'TX'
13 and b.type = 'TX'
14 and b.block = 1
15 order by time_held, time_waited;
BLOCKER_SID SERIAL# BLOCKER_USERNAME TY LOCK_MODE
----------- -------- ---------------- ----- ----------
131 6 HR TX Exclusive
TIME_HELD WAITER_SID REQUEST_MODE TIME_WAITED
--------- ---------- ----------- -----------
357 135 Exclusive 215
由于在Oracle中使用队列来锁定,在队列中的session按照先进先出的原则,先进入队列的session先获得锁定。因此,当159号session释放锁定以后,131号 session会获得锁定,而135号session则被131号session阻塞。
如果159号session无法提交,则我们可以发出下面的语句直接删除159号session:
SQL> alter system kill session '150,6';
其中,150为session的SID号,而6为session的SERIAL#号。当删除159号session以后,该session所获得的锁定就会被pmon进程释放。
当我们更新某个表的记录,于是在表级别上添加RX模式的TM锁定。这时如果其他的用户要删除该表或修改该表结构时,则需要在表上添加X模式的TM锁定。由 于RX模式与X模式不兼容,则报错。如下所示:
SQL> drop table employees;
drop table employees
*
ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified
从前面我们已经看到,一个事务不管更新多少条记录,都只能获得一个TX锁定。但是一个事务可以获得多个TM锁定。我们来看下面的例子:
SQL> select sid from v$mystat where rownum=1;
SID
----------
150
SQL> update employees set last_name=last_name||'a' where
department_id=60;
6 rows updated.
SQL> update departments set department_name='unknow' where
department_id=10;
1 row updated.
SQL> update locations set city='unknown' where location_id=1100;
1 row updated.
在同一个事务中,我们总共更新了3个表。然后检查v$lock视图:
SQL> select sid,type,id1,id2,decode(lmode,0,'None',1,'Null',2,
'Row share',
2 3,'Row Exclusive',4,'Share',5,'Share Row Exclusive',6,
'Exclusive') lock_mode,
3 decode(request,0,'None',1,'Null',2,'Row share',
4 3,'Row Exclusive',4,'Share',5,'Share Row Exclusive',6,
'Exclusive') request_mode,block
5 from v$lock
6 where sid=150;
SID TYPE ID1 ID2 LOCK_MODE REQUEST_MODE BLOCK
---- ----- -------- ---------- -------------- ------------- -------
150 TM 53777 0 Row Exclusive None 0
150 TM 53772 0 Row Exclusive None 0
150 TM 53767 0 Row Exclusive None 0
150 TX 262185 5932 Exclusive None 0
可以看到,我们获得了1个TX锁定及3个TM锁定。或者说,TX锁定与事务个数相同,而TM锁定则与被更新的表的个数相同。
在数据库系统中,我们同时可以获得的TX锁定的总个数由初始化参数transactions决定,而可以获得的TM锁定的个数则由初始化参数dml_locks决定。如下所 示:
SQL> select name,value from v$parameter where name in('transactions',
'dml_locks');
NAME VALUE
---------------- -------
dml_locks 748
transactions 187
默认情况下,同时可以启动187个事务,也就是获得187个TX锁定,以及获得748个TM锁定。也就是说,平均每个事务更新4个表(748/187=4)。我们还可以查 看v$resource_limit视图来了解这两个资源的使用情况:
SQL> select resource_name as "R_N",current_utilization as "C_U",max_
utilization as "M_U",
2 initial_allocation as "I_U" from v$resource_limit where resource
_name in('transactions ','dml_locks');
R_N C_U M_U I_U
--------------- ---------- ---------- ----------
dml_locks 4 55 748
transactions 2 7 187
从结果中可以看到当前所使用的资源个数(C_U字段)、曾经达到过的同时被使用的资源的最大个数(M_U字段)、能够分配的资源的最大个数(I_U字段)。 这两个初始化参数修改以后,都必须重启实例使之生效。如果将初始化参数transactions设置为0,则重启以后,会发现初始化参数dml_locks也被自动设置 为0。但是我们查询视图v$resource_limit:
SQL> select resource_name as "R_N",current_utilization as "C_U",
max_utilization as "M_U",
2 initial_allocation as "I_U" from v$resource_limit where
resource_name in('transactions ','dml_locks');
R_N C_U M_U I_U
--------------- ---------- ---------- ----------
dml_locks 0 0 0
transactions 0 5 187
输出结果说明,尽管我们将transactions设置为0,但是仍然可以发出事务操作语句,同时能够发出的最大事务个数仍然为默认的187个事务。而dml_locks的 资源个数则确实为0了,但这并不是说明不能对表添加表级锁,而只是说明不能进行大部分的DDL操作了。比如我们这时发出删除employees表的命令,如下所 示:
SQL> drop table employees;
drop table employees
*
ERROR at line 1:
ORA-00062: DML full-table lock cannot be acquired; DML_LOCKS is 0