您的位置:首页 > 数据库 > Oracle

oracle的锁和并行机制 推荐

2011-06-07 14:25 232 查看
锁是并发访问的时候用于保护不共享资源不被同时并发修改的机制。

oracle锁分为DML锁,DDL锁,内部锁和latch

DML锁确保一次只能只有一个人修改某一行(TX锁),而且正在处理一个表时别人不能删除(TM锁)。

DDL锁,在DDL操作是系统会自动为对象加上DDL锁,保护这些对象不被其他会话锁修改。

latch是轻量级的串行化设备,用于协调对共享数据结构、对象、文件的多用户访问,一般都是保护共享内存结构使用的锁,在此不做讨论。

一般的锁管理器工作过程:

1.找到想要锁定的那一行地址

2.在锁管理器排队

3.锁定列表

4.搜索列表,查看别人是否锁定这一行

5.在列表中创建一个新的条目,表明已经锁定这一行

6.对列表解锁

接下里修改,之后提交修改后,继续过程:

7.再次排队

8.锁住锁的列表

9.在这个列表中锁定,并释放所有的锁

10.对列表解锁
oracle锁管理方式:

找到需要锁定的那行地址

到达那一行

锁定这一行
通常lock有三个组件:Resource Structure(资源)、Lock Structure(锁)和Enqueue(排队机制)

Resource和lock是数据结构,而Enqueue是算法。

Resource Structure每一个需要并发控制的资源都有用这个数据结构来描述,先关的成员为:owner、waiter和converter,这是三个指针,分别指向3个由Lock Structure组成的链表。

Lock Structure

每当进程需要访问共享资源时,必须先“锁定”该资源,这个动作实际上是从内存中申请一个Lock Structure,

,在其中记录“锁模式、进程ID”等重要信息。然后看是否立即能够获得资源的访问权,如果不能的话将这个Lock structure挂到Resource Structure的Waiter链表中,如果能够获得,则把Lock Structure的owner链表中。
最常用的锁模式

Share 拥有这对资源进行只读访问,允许其他用户并发只读访问

Exclusive 拥有者对资源进行修改访问,不允许其他用户并发访问
Enqueue 算法

Lock使用的是Enqueue算法,可以理解为“先入先出队列”,如果进程的锁定请求不能满足,该进程的Lock
Structure就被加到Waiter链表的末端。当占用进程释放锁时,会检查Waiter和Converter队列,把锁分配给先入对的请求者。
converter和waiter两个等待队列,算法的有些区别:如果某个操作先后需要2中不同模式的锁,比如先是share
mode然后是exclusive mode,则进程会先请求share mode 后获得lock
structure会挂在owner队列上,当需要exclusive mode锁时,进程先释放share
mode的锁,然后再次申请exclusive
mode的锁,但是可能无法立即获得,这时请求会挂在converter队列下,converter队列会被优先于waiter队列处理。
oracle行级锁机制

首先明白三个概念:

ITL:每个数据块的头部有一个叫做ITL的数据结构,用于记录那些事务修改了这个数据块的内容。

记录头ITL索引:每条记录的记录头部有一个字段,用于记录ITL表项号,可以看做指向ITL表的指针

TX锁,事务锁

TM锁:保护表或视图定义不被修改的锁
当一个事务开始时,必须申请一个TX锁,这种锁保护资源是回滚段、回滚段数据块,因此这个这个申请意味着:用户进程必须先申请到回滚段资源后才能开始一个事务,才能执行DML语句修改数据。

申请到回滚段资源后,用户事务就可以开始修改数据了,事务信息可在v$transaction中查到,在修改数据表的记录时,需要遵守如下操作顺序:

首先获得这个表的TM锁,这个锁用于保护事务执行过程中其他用户不能修改表结构;

事务修改某个数据块记录时,首先需要在改数据块块头的ITL表中申请一个空闲表项,并在其中记录事务号,实际就是在记录这个事物要使用的回滚段地址;

事务修改该数据块的某条记录时,会设置该记录头部的ITL索引指向上一步申请到的表项,然后再修改记录内容,修改前先在回滚段对记录修改前的状态做一个拷
贝,然后才能修改数据记录,这个拷贝用于以后的回滚、恢复和一致性读。当其他用户并发修改这条记录时,会根据记录头的ITL索引读取ITL表项内容,查看
这个事务是否已经提交,如果没有提交,则这个用户的TX锁会等待前一个用户的TX锁的释放。

例如如下式转储的一个数据块的ITL信息:

Block header dump: 0×00411819

Object id on Block? Y

seg/obj: 0×10396 csc: 0×00.d62e7 itc: 2 flg: O typ: 1 – DATA

fsl: 0 fnx: 0×0 ver: 0×01
Itl Xid Uba Flag Lck Scn/Fsc

0×01 0×0008.00b.0000029b 0x00c05271.006a.3c —- 3 fsc 0×0000.00000000

0×02 0×0000.000.00000000 0×00000000.0000.00 —- 0 fsc 0×0000.00000000

seg/obj:seg/obj id

csc:clean scn

itc:itl slots的个数,此时多少个事务在对本data block进行操作

flg: 0=on the freelist

typ:数据块类型

fsl: itl tx freelist slot

fnx: dba of next block on freelist

Itl:interested transaction list index

Xid:transaction id

Uba:undo address

Flag:事务状态标志

Lck:事物所影响行的数量
oracle
在对数据行锁定时,行指向事务ID的一个副本,事务ID存储在包含数据的块中,释放锁时,事务ID会保存下来,这个事务ID时事务特有的,表示了回滚段
号、槽和序列号,事务ID留在包含数据行的块中,可以告诉其他会话:一个会话拥有这个数据行。另一个会话会看到锁ID,由于锁ID表示一个事务,所以可以
很快的查看持有这个锁的事务是否活动的。如果锁不活动的,则允许会话访问这个数据,如果锁还是活动的,会话会要求一旦释放锁就得到通知。所以这需要一个排
队机制:请求锁的会话会排队,等待目前拥有这个锁的事务执行,然后的到这个数据。可以根据v$lock视图的lmode和request
mode判断谁是owner、waiter和converter

owner:lomode>0,request=0

waiter:lmode=0,request>0

converter:lmode>0,request>0
例如下试验可以清楚看到这些信息:

系统已更改。

SQL> create table t1 ( x int );

表已创建。

SQL> create table t2 ( x int );

表已创建。

SQL> insert into t1 values ( 1 );

已创建 1 行。

SQL> insert into t2 values ( 1 );

已创建 1 行。

SQL> select (select username

2 from v$session

3 where sid = v$lock.sid) username,

4 sid,

5 id1,

6 id2,

7 lmode,

8 request, block, v$lock.type

9 from v$lock

10 where sid = (select sid

11 from v$mystat

12 where rownum=1)

13 /
USERNAME SID ID1 ID2 LMODE REQUEST BLOCK TYPE

——– —– —- —- —– ——- —– —-
SYS 13 66455 0 3 0 0 TM
SYS 13 66456 0 3 0 0 TM
SYS 13 589840 662 6 0 0 TX
SQL> select object_name, object_id

2 from user_objects

3 where object_name in (‘T1′,’T2′)

4 /
OBJECT_NAME OBJECT_ID

———— ———

T1 66455

T2 66456

每个事务只能有一个TX锁,但是TM锁依照修改的对象个数而定,TM对应的ID1列就是DML锁定对象ID.

SQL> select username,

2 v$lock.sid,

3 trunc(id1/power(2,16)) rbs,

4 bitand(id1,to_number(‘ffff’,'xxxx’))+0 slot,

5 id2 seq,

6 lmode,

7 request

8 from v$lock, v$session

9 where v$lock.type = ‘TX’

10 and v$lock.sid = v$session.sid

11 and v$session.username = USER;
USERNAME SID RBS SLOT SEQ LMODE REQUEST

——– —– —— —– —— —— ———

SYS 13 9 16 662 6 0
SQL> select XIDUSN, XIDSLOT, XIDSQN from v$transaction;
XIDUSN XIDSLOT XIDSQN

—— ———- ———-

9 16 662
oracle事务不同于其他数据库之处,不需要专门语句显示开始事务,事务会在修改数据的第一条语句处开始,但是一定要用commit或rollback事务。

oracle的commit做了如下操作:

为事务生成一个SCN

LGWR将所有余下的缓存重做日志条目写至磁盘,并把SCN记录到在线重做日志文件中

v$lock中记录着会话持有的锁,这些锁将被释放,而排队等待这些锁的每一个队列都会被唤醒

如果事务处理的某些块还在缓存中,则会快速的模式访问并清除

Itl Xid Uba Flag Lck Scn/Fsc

0×01 0×0008.00b.0000029b 0x00c05271.006a.3c C— 0 scn 0×0000.000d6470

0×02 0×0005.007.00000372 0x00c0dbca.006e.2f –U- 1 fsc 0×0000.000d6584

如上的flag列,第一条ITL信息显示数据块当前事务信息已经被清除,第二个还未清除事务ITL信息标志为U;

oracle并发支持,实现了一种多版本体系,能够同时物化多个版本的数据,能够提供读一致性机制,数据读取器绝不会被写入器所阻塞,也就是写不会阻塞读。一种情况例外,那就是在分布式事务处理(2PC)期间。

另外,记住大多数DDL都带排它锁,有些DDL没有DDL锁,如create index idx on t(x)
online;online关键字会改变建立索引的方法。oracle只会得到表上的TM锁,防止其他DDL发生,但是运行DML运行。oracle发生
死锁的原因外键未加索引、位图索引发生更新,外键未加索引更新或删除父表都会对整个子表加锁

会话1:

create table p ( x int primary key );

create table c ( x references p );

insert into p values ( 1 );

insert into p values ( 2 );

commit;

insert into c values ( 2 );

会话2:

delete from p where x = 1;;

这个时候机会发生阻塞:

SQL> select

2 (select username from v$session where sid=a.sid) blocker,

3 a.sid,

4 ‘ is blocking ‘,

5 (select username from v$session where sid=b.sid) blockee,

6 b.sid

7 from v$lock a, v$lock b

8 where a.block = 1

9 and b.request > 0

10 and a.id1 = b.id1

11 and a.id2 = b.id2;
BLOCKER SID ‘ISBLOCKING’ BLOCKEE SID

——- —– ——— ——— ——-

SYS 142 is blocking SYS 13

不需要对外键加索引的情况:

1、没有从父表删除行

2、没有更新父表的唯一键/主键值

3、没有从父表联结子表

参考:

TOM oracle 9i&10g编程艺术摘自我的blog:ww.manotes.net
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息