Oracle 锁

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_mode
from v$locked_object lo,
dba_objects ao,
v$session sess
where 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,
       request
from v$lock, v$session
where v$lock.type = 'TX'
  and v$lock.sid = v$session.sid
and 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.sid
from v$lock a, v$lock b
where a.block = 1
and b.request > 0
and a.id1 = b.id1
and 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 sess
where 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 s
WHERE l.object_id = o.object_id
   AND l.session_id = s.sid
ORDER 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_time
from v$locked_object l,all_objects o,v$session s
where l.object_id=o.object_id
and l.session_id=s.sid
order 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.XIDSQN
lmode,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 ) d
where a.id1=b.id1
and a.id2=b.id2
and a.sid=c.sid
and b.sid=d.sid;

 

posted @ 2018-12-04 17:12  春困秋乏夏打盹  阅读(374)  评论(0编辑  收藏  举报