zoukankan      html  css  js  c++  java
  • 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;
  • 相关阅读:
    Oracle 11g SQL Fundamentals Training Introduction02
    Chapter 05Reporting Aggregated data Using the Group Functions 01
    Chapter 01Restriicting Data Using The SQL SELECT Statemnt01
    Oracle 11g SQL Fundamentals Training Introduction01
    Chapter 04Using Conversion Functions and Conditional ExpressionsConditional Expressions
    Unix时代的开创者Ken Thompson (zz.is2120.bg57iv3)
    我心目中计算机软件科学最小必读书目 (zz.is2120)
    北京将评估分时分区单双号限行 推进错时上下班 (zz)
    佳能G系列领军相机G1X
    选购单反相机的新建议——心民谈宾得K5(转)
  • 原文地址:https://www.cnblogs.com/yhq1314/p/10065294.html
Copyright © 2011-2022 走看看