• 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;
  • 相关阅读:
    mybatis中的#和$的区别
    Java 导出 CSV
    java生成UUID
    Java并发编程的艺术(七)——Executors
    Java并发编程的艺术(六)——线程间的通信
    Java并发编程的艺术(五)——中断
    Java并发编程的艺术(四)——线程的状态
    Java并发编程的艺术(三)——volatile
    Java并发编程的艺术(二)——重排序
    Java并发编程的艺术(一)——并发编程需要注意的问题
  • 原文地址:https://www.cnblogs.com/yhq1314/p/10065294.html
Copyright © 2020-2023  润新知