Oracle关于锁的知识总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qqww120102/article/details/79608476
一、什么是锁?
锁是指在oracle中用来记录资源是否可用、可用范围和获得顺序的机制,用来保护并发性、数据一致性、隔离性等问题。
二、锁的分类
oracle数据库锁按照封锁级别分三大类
1、DML锁(data locks 数据锁):用于保护数据的完整性,主要包括(TM表级锁)和(TX事务锁或行级锁)。
2、DDL锁(dictionary locks 字典锁):用于保护数据库对象的结构,如表、索引等的结构定义。
3、内部锁和闩(internal locks and latches):保护数据库的内部结构,由oracle内部调用。
DML和DDL都属于lock锁,内部闩、锁则属于latch锁。
这里所说的保护是指,保护并发访问,事务隔离性和数据一致性
三、锁的作用
由于latch是内部调用机制,这里只讲LOCK锁
lock锁主要分为行级锁、事务锁(TX)、表级锁(TM)和修改数据库内部结构的DDL字典锁
lock锁的最小粒度是:数据行
1、行级锁:由于DML语句产生,是指数据块内对应的ITL事务槽记录对应数据行的锁定。行级锁只有一种X锁(排他锁),任何增删改和for update操作都会对目标行加锁。
2、事务锁(TX):由于行级锁产生事务锁,并且一个事务只会产生一个事务锁。
加锁就是DML语句,解锁就是commit或者rollback。
对于行级锁和事务锁,容易产生混淆。认为每一个TX锁代表锁定数据块内的一行数据,其实TX的本意就是Transaction(事务),当一个事务第一次执行修改数据行DML语句或select...for update语句进行查询时,它此时就获得一个TX锁,直至该事务结束(commit或rollback)时,该TX锁才会释放。所以一个TX锁,可以对应被该事务锁定的所有数据行。在oracle数据库上,每一个数据行都有一个对应的锁位,用来表示该行数据是否被锁定。oracle在数据行上只有X锁,而没有S锁。
3、表级锁(TM)
当对整个表进行变更或删除时,需要考虑表中的所有数据行有没有被锁定。但是如果逐行去检查,遇到大量数据时显然不可操作,oracle引出了表级锁概念。表级锁根据强度,分为多种。
每个DML操作都会在数据行所在的表加上一种表级锁,行级排他锁(RX)。这是一种共享锁,既不妨碍其他会话对表的访问,而且也可以在进行表结构变更或者删除表这类需要X锁的事务,快速检查出表内数据行是否有锁定。
手动加表级锁的方式
lock table table_name in [row share] [row exclusive] [share] [share row exclusive] [exclusive] mode;

oracleTM锁的类型

锁模式

描述

含义

锁表SQL

0

none(没有)

没有

没有

1

null

没有

2

Row-s (RS)

行级共享锁,是限制最少的TM锁,可以提供最高程度的并发性。其他会话可以对锁定的表进行任何类型的DML操作,还可以与其他会话锁并存

lock table 表名 in row share mode;

3

Row-x(RX)

行级排他锁,通常已经有事务在修改行或者select...for update修改结果集。允许其他事务对锁定的表进行select,insert,update,delect或lock table 同时锁定一张表

lock table 表名 in row exclusive mode;

4

Share (S)

共享锁,其他事务可以查询锁定的表但不能修改,只允许当前事务修改,但可以多个事务持有它

lock table 表名 in share mode;

5

S/Row-X(SRX)

共享行级排他锁,同一时间只允许一个事务持有和修改锁定的表,其他事务可以查询但不能修改

lock table 表名 in share row exclusive mode;

6

exclusive(X)

排他锁,是限制最高的TM锁,禁止其他事务执行任何类型的DML语句或者锁表

lock table 表名 in exclusive mode;

oracleTM锁互斥关系

锁模式

锁名称

允许级别

互斥级别

2

行级共享锁(RS)

2,3,4,5

6

3

行级排他锁(RX)

2,3

4,5,6

4

共享锁(S)

2,4

3,5,6

5

共享行级排他锁(SRX)

2

3,4,5,6

6

排他锁(X)

2,3,4,5,6

四、下面测试几种lock的相容、互斥关系
创建用户测试环境
会话1:更改当前用户标签
set sqlprompt "_user'@'_connect_identifier '_s1'> "
查询用户环境(sid)编号
select userenv ('sid') from dual;
创建测试表"T"
create table t (x number(1));
插入一行记录
insert into t values (1);
commit;
查询此行记录
select x from t;
会话2:更改当前用户标签
set sqlprompt "_user'@'_connect_identifier '_s2'> "
查询用户环境(sid)编号
select userenv ('sid') from dual;
1、行级共享锁(RS)
row-s 行共享(RS):对应行加上RS锁后,仍然允许其他事物加RS锁
会话1为t表加RS锁
lock table t in row share mode;
查看锁模式
select d.session_id,t.owner,t.object_name,d.locked_mode from v$locked_object d,all_objects t where d.object_id = t.object_id;
查看锁类型和级别(TM表级锁还是TX事务、行级锁)
select type,lmode from v$lock where type in ('TX','TM') and sid=138;(注意输入查询时显示的sid号)
会话2:验证锁互斥
lock table t in row share mode;(2级锁)
rollback;
lock table t in row exclusive mode;(3级锁)
rollback;
lock table t in share mode;(4级锁)
rollback;
lock tbale t in share row exclusive mode;(5级锁)
rollback;
lock table t in exclusive mode;(6级锁)
6级锁时发生锁等待现象,此时必须有一方放弃锁资源
2、行级排他锁(RX)
记录行上RX锁后(INSERT,UPDATE,DELETE,SELECT... ...FOR UPDATE),其他事务不能对行加锁
会话1:T表增加RX锁
lock table t in row exclusive mode;
查看锁模式
select d.session_id,t.owner,t.object_name,d.locked_mode from v$locked_object d,all_objects t where d.object_id = t.object_id;
查看锁类型和级别(TM表级锁还是TX事务、行级锁)
select type,lmode from v$lock where type in ('TX','TM') and sid=138;(注意输入查询时显示的sid号)
会话2:验证锁互斥(2、3允许,4、5、6互斥)
lock table t in row share mode;(RS)
rollback;
lock table t in row exclusive mode;(RX)
rollback;
其他级别都会产生锁等待,必须有一方放弃锁资源
lock table t in share mode;(S)
lock table t in share row exclusive mode;(SRX)
lock table t in exclusive mode;(X)
此时在会话1持有RX锁,但会话2进行对表进行DML(select,insert,update,delete)操作都可以同时持有3级锁,commit或rollback之后会话2持有的锁即释放
select x from t;
insert into t values(2);
update t set x=0 where x=1;
delete t where x=0;
3、共享锁(S)
只允许其他事务使用S锁锁表,不能修改数据
会话1:T表上S锁
lock table t in row share mode;
会话2:验证锁互斥(2、4允许3、5、6互斥)
lock table t in row share mode;(RS)
rollback;
lock table t in share mode;(S)
rollback;
其他级别都会产生锁等待,必须有一方放弃锁资源
lock table t in row exclusive mode;(RX)
lock table t in share row exclusive mode;(SRX)
lock table t in exclusive mode;(X)
4、共享排他锁(SRX)
对应行加X锁,但对表加S锁,所以可以让别的事务对其他行加入X锁
会话1:T表上SRX锁
lock table t in share row exclusive mode;
会话2:验证锁互斥(2允许,3、4、5、6互斥)
lock table t in row share mode;(RS)
rollback;
其他级别都会产生锁等待,必须有一方放弃锁资源
lock table t in row exclusive mode;(RX)
lock table t in share mode;(S)
lock table t in share row exclusive mode;(SRX)
lock table t in exclusive mode;(X)
5、排他锁(X)
会话1:T表上X锁
lock table t in exclusive mode;
会话2:验证锁互斥(2、3、4、5、6都会互斥)
lock table t in row share mode;(RS)
lock table t in row exclusive mode;(RX)
lock table t in share mode;(S)
lock table t in share row exclusive mode;(SRX)
lock table t in exclusive mode;(X)
共享锁和共享排他锁的区别
6、共享锁(S)
会话1:在T表加上共享锁
lock table t in share mode;
会话2:在T表也加上共享锁
lock table t in share mode;
查看锁定情况
select d.session_id,t.owner,t.object_name,d.locked_mode from v$locked_object d,all_objects t where d.object_id = t.object_id;
会话1:做DML操作,此时insert 操作出现锁等待现象
insert into t values(5);
会话2:同样做DML操作,依然出现锁等待现象
insert into t values(6);
在会话2:出现锁等待的同时,会话1立即出现死锁的提示
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
会话2:此时依然在锁等待中......
会话1:释放所持有的S锁
会话2:此时获取锁资源,完成DML操作。不提交的情况下查询锁定情况会发现会话2持有的锁资源已变为5级共享排他锁(SRX)
select d.session_id,t.owner,t.object_name,d.locked_mode from v$locked_object d,all_objects t where d.object_id = t.object_id;
7、共享排他锁(SRX)
会话1:在T表上共享排他锁
lock table t in share row exclusive mode;
会话1:查看当前锁定情况,会发现已经会话1已经持有SRX锁
select d.session_id,t.owner,t.object_name,d.locked_mode from v$locked_object d,all_objects t where d.object_id = t.object_id;
会话2:在T表加上共享排他锁,我们可以发现并未成功出现锁等待情况
lock table t in share row exclusive mode;
会话1:再次查看锁定情况,会话1依然持有SRX锁。会话2没有持有SRX锁但持有了0级锁。
会话3:同样为T表加上共享排他锁,依然会失败并且持有0级锁。
会话1:此时放弃SRX锁资源,锁资源会按照先入先出顺序会话2优先持有,会话3继续锁等待直至会话2放弃锁资源

总结:共享锁可以同时持有锁资源。但如果其中一方进行DML操作就会出现死锁现象,必须其他所有持有锁资源的会话都放弃锁资源才能解除死锁现象。并且持有锁资源的会话变更为5级共享排他锁。
对比发现,5级共享排他锁不会出现同时持有锁资源的情况。会话1持有锁资源,其他会话申请5级锁资源时,不会得到5级锁资源而会得到0级锁资源,并且会进入5级锁资源排队队列。当持有会话放弃锁资源时队列最前面的用户会持有该锁资源。
8、更新丢失
会话1:创建测试表
create table t_lock as select rownum as id, 0 as type from dual connect by rownum <=3;
会话1:查询创建的测试表
select * from t_lock;
会话1:显示当前会话时间,查询type为0的最小id
set time on;
select min(id) from t_lock where type=0;
会话2:同样查询type为0的最小id
select min(id) from t_lock where type=0;
会话1:将id为1的这条记录的type更新为1
update t_lock set type=1 where id=1;
commit;
会话2:将id为1的这条记录的type更新为2
update t_lock set type=2 where id=1;
commit;
此时会话1的更新已经被覆盖,这种现象即为“更新丢失”
此种现象的发生,是由于逻辑有问题造成。在设计逻辑关系时要注意不要发生低级错误。
9、悲观锁
会话1:查询id为2的记录并进行锁定(查询并且立即更新,但是处在没有提交或回滚状态。以此对这条记录进行锁定)
select * from t_lock where id=2 and type=0 for update nowait;
会话2:查询id为2的记录,此时查询报错
select * from t_lock where id=2 and type=0 for update nowait;
会话1:对id为2的记录type更新为1
update t_lock set type=1 where id=2 and type=0;
commit;
会话1:查询id为2的这行记录,确认已经更新
select * from t_lock where id=2;
会话2:再次查询id为2、type为0的这条记录,但是由于type已经变更为1。此时查询结果不会报错,但由于避免会话1更新丢失,会显示没有此条记录。
10、乐观锁
会话1:查询id为3的伪列ora_rowscn的值
select id,type,ora_rowscn from t_lock where id=3;
会话2:查询id为3的伪列ora_rowscn的值
select id,type,ora_rowscn from t_lock where id=3;
会话1:更新id为3的type为1
update t_lock set type=1 where ora_rowscn=查询结果值 and id=3;
commit;
会话2:更新id为3的type为1,此时会显示没有任何记录被更新。
update t_lock set type=1 where ora_rowscn=查询结果值 and id=3;

总结:造成会话2未能更新成功的原因是ora_rowscn随着数据的变化而变化的,所以当记录发生变化时,如果相同的sql进行更新,已经无法更新那一条记录了。
11、死锁事件
会话1:创建测试表
create table t_lock_1(id number(2),name varchar2(15));
create table t_lock_2 as select * from t_lock_1;
插入数据
insert into t_lock_1 values(1,'liubei');
insert into t_lock_2 values(1,'guanyu');
commit;
查询表
select * from t_lock_1;
select * from t_lock_2;
利用子查询测试
会话1:更新t_lock_1表name列为liuxunande,id列为1的记录
update t_lock_1 set name='liuxuande' where id=1;
会话2:更新t_lock_2表name列为guanyunchang,id列为1的记录
update t_lock_2 set name='guanyunchang' where id=1;
会话1:再次更新t_lock_2表name列为guanyunchang,id列为1的记录 此时由于会话2更新t_lock_2后没有提交,会话1再次更新会出现锁等待现象
update t_lock_2 set name='guanyunchang' where id=1;
会话2:再次更新t_lock_1表name列为liuxunande,id列为1的记录 此时会话1更新t_lock_1同样没有提交,会话2此时再次更新将出现死锁事件
update t_lock_1 set name='liuxuande' where id=1;

总结:出现死锁的原因是,用户1和用户2互相持有锁,但没有释放锁资源。此时再次更新对方已锁定的表就会造成同时锁等待,这种现象称为死锁事件。oracle本身一定不会出现死锁事件,出现的原因一定是程序的逻辑出现问题。
五、与锁相关的各类查询语句
select name,value from v$parameter where name in ('transactions','dml_locks');
dml_locks 同时可以获得DML操作的数量,即TM锁
transactions 同时可以存在的事务数量,即TX锁
--查看被锁的表 
select b.owner,b.object_name,a.session_id,a.locked_mode from v$locked_object a,dba_objects b where b.object_id = a.object_id;
--查看哪个用户哪个进程照成死锁
select b.username,b.sid,b.serial#,logon_time from v$locked_object a,v$session b where a.session_id = b.sid order by b.logon_time;
--查看连接的进程 
SELECT sid, serial#, username, osuser FROM v$session;
--.查出锁定表的sid, serial#,os_user_name, machine_name, terminal,锁的type,mode
SELECT s.sid, s.serial#, s.username, s.schemaname, s.osuser, s.process, s.machine,
s.terminal, s.logon_time, l.type
FROM v$session s, v$lock l
WHERE s.sid = l.sid
AND s.username IS NOT NULL
ORDER BY sid;
这个语句将查找到数据库中所有的DML语句产生的锁,还可以发现,
任何DML语句其实产生了两个锁,一个是表锁,一个是行锁。
--杀掉进程 sid,serial#
alter system kill session'210,11562';
查询是事务被哪个会话的哪条sql语句加锁的
select xidusn,xidslot,xidsqn,status from v$transaction;
select s.username,s.sid,s.status,sq.sql_text sql_text, t.USED_UREC Records, t.USED_UBLK Blocks, 
        t.USED_UBLK*(select value from v$parameter where name='db_block_size')/1024 KBytes 
from v$transaction t,
v$session s,
v$sql sq
where t.addr = s.taddr
and s.PREV_SQL_ID = sq.sql_id
-- and s.username = '<user>'
/
六、与锁相关的几个表、视图
SELECT * FROM v$lock;
主要记录会话ID、锁类型、模式和关于ITL经过函数计算得出数值等信息
SELECT * FROM v$locked_object;
和v$lock基本相同,不同是不记录ITL信息,但记录OS ID、oracle登录用户名等信息
SELECT * FROM v$sqlarea、SELECT * FROM v$sql;
v$sql和v$sqlarea视图基本相同,都是记录share pool中共享SQL区的sql统计信息。 如内存消耗、IO(物流磁盘读和逻辑内存读)、排序操作、哈希ID等数据。不同是v$sqlarea根据sql_text进行group by,统计列进行sum(),通过version_count计算子指针的个数。
SELECT * FROM v$sql_text;
这个视图记录着完整的sql语句(最大1000字符)和生成的哈希值等信息 。sql_fulltext能够记录sql语句的所有字符
可以利用 v$sql、v$sqlarea和 v$sql_text以及相关事务、锁等信息将执行过的sql语句找出。
SELECT * FROM v$session;
通过SID和SERIAL来确定一个Session、会话拥有者用户名username、会话状态、会话由哪个客户端发起、正在执行什么sql(通过sql_address、sql_hash_value、sql_id、sql_child_number确定,再借助v$sqltext就可以知道)、锁等待相关信息(如所在表、文件、块、被锁行)等。
SELECT * FROM v$process ;
可以查看当前数据库内的所有进程
SELECT * FROM dba_objects;
包含当前数据库所有的事务信息
select * from v$resource_limit; (资源限制动态视图)
resource_name:资源名
current_utilization:资源当前使用量
max_utilization:实例启动以来,资源达到的最大值
initial_allocation:资源上限设定值
limit_value:系统默认值
由于PMON是定期更新 v$resource_limit,所以可能由于短时间内资源增长过快,造成与其他视图对资源数量并不完全相同。
SELECT * FROM v$session_wait;
当前数据库存在的等待事件

猜你喜欢

转载自blog.csdn.net/qqww120102/article/details/79608476