博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Oracle 锁
阅读量:5262 次
发布时间:2019-06-14

本文共 13192 字,大约阅读时间需要 43 分钟。

1锁

    lock是用于对共享资源的并发访问,还能提供数据的完整性跟一致性,多个用户访问和修改数据或数据结构,就要有一种机制来防止对同一份信心的并发修改,

Oracle:事务是数据库的核心

        应该延时要适当时机才提交,必要时才提交,事务的大小只应该有业务逻辑来决定

        如果需要,就长时间的保持对数据所加的锁,

        oracle中,行级锁没有相关的开销,固定的常量

        不会对锁升级

        同时得到并发性和一致性,非阻塞读,写不会阻塞读,读不会被写阻塞

2 锁定问题

  2.1 防止丢失更新

   两个会话操作同一行

  2.2 悲观锁定:必须在有状态或有连接环境,粒度较大,代价昂贵

    Select* from emp for update nowait/for update wait n

   一个会话修改在没提交之前别的会话看不见所修改。

  会话1:select * from emp where empno=7934 and ename='MILLER' and sal=1300

for update nowait

  会话2:select *  from emp where empno=7934 正常

  会话2:update emp set mgr=7781 where empno=7934 挂起

  会话1:commit或者rollback;会话2就会更新;

  在会话1查看该数据并没有修改;

  会话2 commit或者rollback,其他会话才能看见其修改。

  所有表都应该有1个主键(select最多获取一条数据,因为条件包含主键)而且主键是不可变的,不应该更新主键.

   2.2 乐观锁定

    把所有锁定都延迟到提交之前才去做,我们认为数据不会被其他用户修改,会等到最后一刻才去验证

   乐观控制的方法:

  1使用版本列的乐观锁定:对要保护的表新增加一个number或timestamp列,通常通过表上的一个行触发器来维护,这个触发器负责递增number列或更新timestamp列

  

create table dept_lock     ( deptno     number(2),      dname      varchar2(14),      loc        varchar2(13),       last_mod   timestamp with time zone                  default systimestamp not null,      constraint dept_lock_pk primary key(deptno)    )   insert into dept_lock( deptno, dname, loc )     select deptno, dname, loc      from scott.dept;select dname, loc, to_char( last_mod, 'DD-MON-YYYY HH.MI.SSXFF AM TZR' )          from dept_lock      where deptno = 10;  update dept_lock        set dname = initcap(dname),           last_mod = systimestamp     where deptno =10       and last_mod = to_timestamp_tz('29-MAY-2013 03.40.47.500000 PM +08:00', 'DD-MON-YYYY HH.MI.SSXFF AM TZR' );

 

   第一次更新1行,第二次在执行就不会更新,因为条件last_mod 不满足

 还可以使用trigger来维护这个last_mod字段,建议避免使用触发器,让dml来负责,触发器会引入大量开销

2 使用校验和的乐观锁定:用基数生成一个虚拟列 ora_hash

  select deptno, dname, loc, ora_hash( dname || '/' || loc ) hash    

    from dept 

   where deptno = 10;

  10 ACCOUNTING NEW YORK 401273349

  然后更新

update dept 

       set dname = initcap(dname)

     where deptno = 10

      and ora_hash( dname || '/' || loc ) = 401273349

 第一次成功,第二次hash值重新计算,再次更新则失败

 

2.3 阻塞

   如果某个会话持有某资源的锁,而另一个会话在请求这个资源,就会出现阻塞locking

  2.3.1 阻塞的insert

    一个带主键的表,或者表上有唯一约束,但2个会话视图用相同的值插入一行,其中一个会阻塞,直到另一个会话提交或回滚,如果提交,则另个会话报错,回滚,阻塞的会话就会成功。

  2.3.2 阻塞的update,delete,merge

    如果代码中存在视图更新其他人正在更新的行(有人已经锁住了这一行),可以通过使用select for update nowait,

 2.4死锁

2个会话都在互相请求另一个会话持有的资源,就会死锁

A,b 2个表,各一行数据

 A                                  b

Update a                         update b

                                 Update a(阻塞)

Update b(死锁)

Session1

SQL> create table a (x number);

Table created.

SQL> create table b (y number);

Table created

SQL> insert into a values (1);

1 row created.

SQL> insert into b values (2);

1 row created.

SQL> commit;

Commit complete.

SQL> update a set x=x+1;

Session2

SQL> update b set y=y+1;

1 row updated.

SQL> update a set x=5;-----该会话被阻塞

Session1

SQL> update b set y=8;----阻塞

1 row updated.

Session2

update a set x=5------发现死锁

       *

ERROR at line 1:

ORA-00060: deadlock detected while waiting for resource

SQL> commit;-----提交或回滚

Commit complete.

Session1

SQL> select * from a;

         X

----------

         2

SQL> select * from b;

         Y

----------

         8

SQL> commit;

Session2

SQL> select * from a;

 

         X

----------

         1

SQL> select * from b;

 

         Y

----------

         3

SQL> select * from a;-------当session 1 提交过后

         X

----------

         2

SQL> select * from b;

 

         Y

----------

         8

---会话b阻塞

select sess.sid,sess.serial#,lo.oracle_username,lo.os_user_name,ao.object_name,lo.locked_modefrom v$locked_object lo,dba_objects ao,v$session sesswhere ao.object_id = lo.object_id and lo.session_id = sess.sid;

 

Oracle会回滚与死锁有关的某条语句,会话b必须决定将b表上未执行的工作提交还是回滚

Oracle认为死锁很少出现,每次出现都会在服务器上创建一个跟踪文件

Oracle会在以下情况,修改父表后会对子表加一个全表锁

 1 如果更新了父表的主键,若外键没有索引,子表会被锁住

 2 如果删除了父表中的一行,整个子表会被锁住(没有index)

 3 如果合并到父表,整个子表会被锁住(么有index) 11g后取消

Session1

SQL> create table a1 (x int primary key);

Table created.

SQL> create table a2 (x references a1);

Table created.

SQL> insert into a1 values(1);

1 row created.

SQL> insert into a1 values (2);

1 row created.

SQL> commit;

Commit complete.

SQL> insert into a2 values(2);

1 row created.

Session2

SQL> delete from a1 where x=1;-----卡住

 

Session1 提交

select * from v$session;

 Oracle在修改父表后会对子表加一个全表锁(外键未加索引)

 查看为建索引的外键

 

select table_name, constraint_name,     cname1 || nvl2(cname2,','||cname2,null) ||     nvl2(cname3,','||cname3,null) || nvl2(cname4,','||cname4,null) ||     nvl2(cname5,','||cname5,null) || nvl2(cname6,','||cname6,null) ||     nvl2(cname7,','||cname7,null) || nvl2(cname8,','||cname8,null)            columns  from ( select b.table_name,                b.constraint_name,                max(decode( position, 1, column_name, null )) cname1,                max(decode( position, 2, column_name, null )) cname2,                max(decode( position, 3, column_name, null )) cname3,                max(decode( position, 4, column_name, null )) cname4,                max(decode( position, 5, column_name, null )) cname5,                max(decode( position, 6, column_name, null )) cname6,                max(decode( position, 7, column_name, null )) cname7,                max(decode( position, 8, column_name, null )) cname8,                count(*) col_cnt           from (select substr(table_name,1,30) table_name,                        substr(constraint_name,1,30) constraint_name,                        substr(column_name,1,30) column_name,                        position                   from user_cons_columns ) a,                user_constraints b          where a.constraint_name = b.constraint_name            and b.constraint_type = 'R'             group by b.table_name, b.constraint_name       ) cons where col_cnt > ALL         ( select count(*)             from user_ind_columns i            where i.table_name = cons.table_name              and i.column_name in (cname1, cname2, cname3, cname4,                                    cname5, cname6, cname7, cname8 )              and i.column_position <= cons.col_cnt            group by i.index_name         )

 

EMP FK_DEPTNO   DEPTNO

 emp上的deptno列 外键

 未在外键上建立索引还可能带来的问题

  如果有on delete cascade,而且没有对子表建立索引,删除父表的每一行行都会对子表做全表扫描,

 从父表查询子表,速度慢,2个相关联

create index GOODS_LANGUAGE_inex on GOODS_LANGUAGE(GOODS_ID)

oracle从来不会进行锁升级

3 锁类型

  Dml锁:行级锁,表级锁

  Ddl锁:create或alter等语句,ddl锁保护对象结构

  Latch

  3.1 dml锁

   Dml锁用于确保一次只有一个人修改某一行,当你在处理这行的时候,其他人不能删除该表

  1 tx锁

   TX锁,事务发起第一修改时会得到TX锁(事务锁),而且会一直持有这个锁,直至事务提交或回滚

   TX锁用作一种排队机制,使得其他会话可以等待这个事务的执行

   V$transaction:对应每个活动事务都包含一个条目

   V$session:显示已登录的会话

   V$lock:对应持有所有sequence队列锁以及正在等待锁的会话,

create table dept as select * from scott.dept;create table emp as select * from scott.emp;alter table dept add constraint dept_pk primary key(deptno);alter table emp add constraint emp_pk primary key(empno);alter table emp add constraint emp_fk_dept foreign key (deptno) references dept(deptno);create index emp_deptno_idx on emp(deptno);update dept set dname = initcap(dname);

 

select username,       v$lock.sid,       trunc(id1/power(2,16)) rbs,       bitand(id1,to_number('ffff','xxxx'))+0 slot,       id2 seq,       lmode,       requestfrom v$lock, v$sessionwhere v$lock.type = 'TX'  and v$lock.sid = v$session.sidand v$session.username = USER;

 

prompt update emp set ename = upper(ename);;

prompt update dept set deptno = deptno-10;;

SCOTT 133 7 23 564 0 6

SCOTT 133 3 2 651 6 0

SCOTT 143 7 23 564 6 0

Lmode为6是一个排他锁,request为0 你拥有这个锁,request为6就表示该会话正在请求锁

   select XIDUSN, XIDSLOT, XIDSQN

  from v$transaction;

查看阻塞的SQL

select(select username from v$session where sid=a.sid) blocker,a.sid,' is blocking ',(select username from v$session where sid=b.sid) blockee,b.sidfrom v$lock a, v$lock bwhere a.block = 1and b.request > 0and a.id1 = b.id1and a.id2 = b.id2;

 

SCOTT 143  is blocking SCOTT 133

 Tm锁

  3.2 TM锁(表级锁)用于在修改表数据时,表的结构不能被改变

  例如已经更新了一个表,会得到这个表的tm锁,这会防止另一个用户在该表上执行dropalter命令,如果有表的一个tm锁,另一个用户试图在该表上执行ddl,得到错误 ora--00054

 每个事务只能得到一个tx锁,但tm锁则不同,修改了多少个对象,就能得到多少个tm锁

create table t1 ( x int );

create table t2 ( x int );

connect /

insert into t1 values ( 1 );

insert into t2 values ( 1 );

select (select username          from v$session         where sid = v$lock.sid) username,       sid,       id1,       id2,       lmode,       request, block, v$lock.type         from v$lock where sid = (select sid                from v$mystat               where rownum=1);

 

SCOTT 143 52953 0 3 0 0 TM 多个tm锁

SCOTT 143 52954 0 3 0 0 TM

SCOTT 143 327699 644 6 0 0 TX

   select object_name, object_id

  from user_objects

 where object_name in ('TT1','TT2')

TT1 52953

TT2 52954

 

3.2 ddl锁

 Ddl操作中会自动为对象加ddl锁(ddl lock),从而保护对象不被其他会话所修改

 如果在表t上执行alter table t,表t上就会添加一个排他DDL锁,防止其他会话得到这个表的ddl锁和tm锁

Ddl操作期间,会一直持有ddl锁

  3种ddl类型

   排他ddl锁:期间可以查询该表,无法以任何方式修改该表,防止其他会话得到他们自己的ddl锁或tm锁

  共享ddl锁:该锁会保护引用对象的结构,使之不会被其他会话修改,但是允许修改数据(对依赖的对象加个共享ddl锁)

  可中断解析锁:这些锁允许一个对象向另外某个对象注册其依赖性

大多数的ddl都带有一个排他的ddl锁

alter table t move

 期间,表t不能被别人修改,可以使用select查询,

  oracle中,现在有些ddl操作没有ddl锁也可以发生

 Create index t_idx on t(x) online

 Online关键字会改变具体建立索引的方式,oracle并不是加一个排他ddl锁来防止数据修改,会试图得到表上的一个低级(mode 2)的tm锁,会有效的防止其他的ddl发生,同时还允许dml正常进行

对表完成直接路径加载和创建索引不能同时进行

Od锁11g新增加,支持真正的联机ddl

另外一类ddl会获得共享ddl锁,在创建存储的编译对象(过程跟视图)时,会对依赖的对象加这种共享ddl锁。

Create view myview

As

Select emp.empno,emp.ename,dept.deptno,dept.dname

From emp,dept

Where emp.deptno=dept.deptno;

emp跟dept都会加上共享ddl锁,而create view命令仍在处理,可以修改这些表的内容,但是不能修改其结构

Ddl 可中断解析锁,你的会话解析一条语句时,对于该语句引用的每一个对象都会加一个解析锁,加锁的目的是:如果以某种方式删除或修改了一个被引用的对象,可以将共享池中已解析的缓存语句置为无效(刷新输出)

视图dba_ddl_locks

 ,默认没有安装,运行/rdbms/admin/catblock.sql脚本,(sys运行)

 可以得到我的会话锁定的所有对象

Alter procedure p compile

Select session_id,owner,name,type,mode_held,mode_requested

From dba_ddl_locks

Where session_id =(select sid from v$mystat where rownum=1)

这个视图对开发人员很有用,发现测试或开发系统中某段代码无法编译时,将会挂起并最终超时,说明有人正在使用这段代码,可以使用这个视图来找他使用的人

Oracle11g r2版本引入一个新特性,基于版本的重定义(ebr),允许在同一个模式中同时有同一个存储过程的多个版本

oracle ORACLE里锁有以下几种模式:

0none
1null 
2Row-S 行共享(RS):共享表锁,sub share 
3Row-X 行独占(RX):用于行的修改,sub exclusive 
4Share 共享锁(S):阻止其他DML操作,share
5S/Row-X 共享行独占(SRX):阻止其他事务操作,share/sub exclusive 
6exclusive 独占(X):独立访问使用,exclusive
数字越大锁级别越高影响的操作越多。
同一个用户,不同用户的区别
1级锁有:Select,有时会在v$locked_object出现。
2 Row-S 行共享(RS)级锁有:Select for update,Lock For Update,Lock table tt in Row Share mode 
select for update当对话使用for update子串打开一个游标时,所有返回集中的数据行都将处于行级(Row-X)独占式锁定,其他对象只能查询这些数据行,不能进行updatedeleteselect for update操作。
3 Row-X 行独占(RX)级锁有:Insert, Update, Delete, Lock Row Exclusive
没有commit之前插入同样的一条记录会没有反应因为后一个3的锁会一直等待上一个3的锁我们必须释放掉上一个才能继续工作。
4 Share 共享锁(S):级锁有:Create Index, Lock Share 
locked_mode2,3,4不影响DML(insert,delete,update,select)操作DDL(alter,drop)操作会提示ora-00054错误。00054, 00000, "resource busy and acquire with NOWAIT specified"
// *Cause: Resource interested is busy.
// *Action: Retry if necessary.
5 S/Row-X 共享行独占(SRX)级锁有:Lock Share Row Exclusive 
具体来讲有主外键约束时update / delete ... ; 可能会产生4,5的锁
6 exclusive 独占(X)级锁有:Alter table, Drop table, Drop Index, Truncate table, Lock Exclusive

一个sessionInset后不commitlocked_mode=3,然后create index,自动提交,锁消失,drop可以

Select* for updatelocked_mode=3 --10G已经把FOR UPDATE改成了表级3号锁

查看锁表进程SQL语句 1

select sess.sid,sess.serial#,lo.oracle_username,lo.os_user_name,ao.object_name, lo.locked_mode from v$locked_object lo,   dba_objects ao,   v$session sesswhere ao.object_id = lo.object_id and lo.session_id = sess.sid;

 

查看锁表进程SQL语句2:

select * from v$session t1, v$locked_object t2 where t1.sid = t2.SESSION_ID;

杀掉锁表进程: 

如有記錄則表示有lock,記錄下SID和serial# ,將記錄的ID替換下面的738,1429,即可解除LOCK
alter system kill session '738,1429';

select 'alter system kill session '''||trim(t2.sid)||','||trim(t2.serial#)||''';'from v$locked_object t1,v$session t2 where t1.session_id=t2.sid;
SELECT l.session_id      sid,       s.serial#,       l.locked_mode     锁模式,       l.oracle_username 登录用户,       l.os_user_name    登录机器用户名,       s.machine         机器名,       s.terminal        终端用户名,       o.object_name     被锁对象名,       s.logon_time      登录数据库时间  FROM v$locked_object l, all_objects o, v$session sWHERE l.object_id = o.object_id   AND l.session_id = s.sidORDER BY sid, s.serial#;
select l.session_id,s.serial#,l.locked_mode,l.oracle_username,l.os_user_name,s.machine,s.terminal,o.object_name,s.logon_timefrom v$locked_object l,all_objects o,v$session swhere l.object_id=o.object_idand l.session_id=s.sidorder by 1,2;

 

 

查看数据库中被锁住的对象和相关会话      

SELECT a.sid,       a.serial#,       a.username,       a.program,       c.owner,       c.object_name  FROM v$session a, v$locked_object b, all_objects c WHERE a.sid = b.session_id AND c.object_id = b.object_id;

 

--查看当前所有的锁

select (select username from v$session where sid = v$lock.sid) username, sid,        trunc((case   ----如果锁类型是TX,则显示事务 ID,对应v$transaction.XIDUSN,如果是 TM锁,则对应 user_objects 里的 object_id               when type = 'TX' then        trunc(ID1/power(2, 16))                        else                id1             end)) T#_or_obj#,            trunc((case   ----如果锁类型是TX,则显示事务 SLOT,对应 v$transaction.XIDSLOT               when type = 'TX' then                 trunc(bitand(ID1, to_number('ffff', 'xxxx')) + 0)            else                0              end)) slot,id2 seq,---这里对应v$transaction.XIDSQNlmode,request,block,type from v$lock where type ='TX’'or type='TM' order by sid;

 

首先,如果系统有锁,我们需要看锁对应的类型和锁定的对象,有的是行级排它锁,表级共

享锁,这些概念,应该查资料去搞清楚,上面的 SQL 是查,锁定的对象用的,以及事务的

 

---就是查锁阻塞

----增加了serial#

select c.username,a.sid,c.serial#,' is blocking ',d.username,b.sid,d.serial#from  (select sid,id1,id2 from v$lock where block =1) a,(select sid,id1,id2 from v$lock where request > 0) b,(select sid,serial#,username from v$session ) c,(select sid,serial#,username from v$session ) dwhere a.id1=b.id1and a.id2=b.id2and a.sid=c.sidand b.sid=d.sid;

 

转载于:https://www.cnblogs.com/yhq1314/p/10065294.html

你可能感兴趣的文章
283. Move Zeroes把零放在最后面
查看>>
Visual Studio Code 打开.py代码报Linter pylint is not installed解决办法
查看>>
Python 数据类型
查看>>
S5PV210根文件系统的制作(一)
查看>>
centos下同时启动多个tomcat
查看>>
slab分配器
查看>>
数据清洗
查看>>
【读书笔记】C#高级编程 第三章 对象和类型
查看>>
针对sl的ICSharpCode.SharpZipLib,只保留zip,gzip的流压缩、解压缩功能
查看>>
【转】代码中特殊的注释技术——TODO、FIXME和XXX的用处
查看>>
【SVM】libsvm-python
查看>>
Jmeter接口压力测试,Java.net.BindException: Address already in use: connect
查看>>
Leetcode Balanced Binary Tree
查看>>
Leetcode 92. Reverse Linked List II
查看>>
九.python面向对象(双下方法内置方法)
查看>>
go:channel(未完)
查看>>
[JS]递归对象或数组
查看>>
LeetCode(17) - Letter Combinations of a Phone Number
查看>>
Linux查找命令对比(find、locate、whereis、which、type、grep)
查看>>
路由器外接硬盘做nas可行吗?
查看>>