Oracle中的锁机制1
有一些性能问题是由于对数据库锁的错误使用造成的。记得之前有个客户,alert中全是死锁的信息,最后发现原因是外键没有建立索引导致的。
1.基本概念为了确保用户在存取同一数据库对象时的正确性(无丢失修改、可重复读、不读“脏”数据等),数据库引入了锁机制。基本的锁类型有两种:排它锁 X锁和共享锁 S锁。
若事务T对数据D加X锁,则其他任何事务都不能再对D加任何类型的锁,直至T释放D上的X锁;一般要求在修改数据前向该数据加排它锁,所以排它锁有成为写锁。一个资源,比如一行或者一个表只能加一个排它锁,但是可以加多个共享锁。
共享锁若事务T对数据D加S锁,则其他事务只能对D加S锁,而不能加X锁,直至T释放D上的S锁;一般要求读取数据前向该数据加共享锁,所以共享锁又称为读锁。
2.锁行为锁作用于读取和写入的交互。读是对资源的查询,写是对资源的修改。以下列出了Oracle数据库对读和写的锁规则:
A row is locked only when modified by a writer.行只有被写更改的时候锁定。
含义:当一个语句更新一行的时候,会话仅获得该行的锁。通过在行级别锁定数据,数据库最大程度的减少了对相同数据的争用。正常情况下,数据库不会把行锁升级为表锁。
对于行级别的修改阻止对相同行的并发修改。
含义:如果一个事务正在修改一行,行锁阻止其它的事务同时修改同一行。
读操作从来不会阻止写操作。
含义:因为读不会锁定,所以可以对该行做修改。除了SELECT … FOR UPDATE这个特殊的select语句。
写操作不会阻止读操作。
含义:当一行数据正在被修改,数据库使用UNDO提供给读一个该行的一致性视图。
pending distributed transactions
一个特殊情况就是挂起的分布式事务中,读操作可能需要等待对于相同数据块的写完成。
锁转换:Oracle根据需要执行锁转换。例如在SELECT … FOR UPDATE中,Oracle数据库自动将行共享锁转换为行排它锁。
锁升级:Oracle不会升级锁。例如Oracle不会将行锁升级为表锁。
锁定时间:Oracle在事务rollback或者commit或者rollback savepoint等之后,都会释放相应语句获得的锁,也就是在事务执行期间。但是有个特例就是:由于在外键没有建立索引子表的锁不是在事务期间,而是在语句执行期间。
死锁是两个或者多个用户等待彼此锁定的数据的一种情况。Oracle数据库自动检测死锁,并通过回滚死锁涉及的一条语句,释放一组冲突的行锁来解决死锁。数据库向进行语句级回滚的事务返回相应的消息,通常,此语句应该显式回滚。
记得有次电话面试,让我口头模拟一个死锁,知道是互相等待,两个会话就够了,但是最后说错了,回想起来,除了紧张,可能还是理解不够。
模拟两个会话产生死锁:
T | session1 | session2 |
---|---|---|
t0 | UPDATE employees set salary=salary*1.1 where employee_id=100; 1 row updated | UPDATE employees set salary=salary*1.1 where employee_id=200; 1 row updated |
t1 | UPDATE employees set salary=salary*1.1 where employee_id=200; no return | UPDATE employees set salary=salary*1.1 where employee_id=100; no return |
t2 | ORA-00060: deadlock detected while waiting for resource | |
t3 | commit; | |
t4 | 1 row updated. | |
t5 | commit; Commit complete. |
Oracle数据库根据资源和要执行的操作自动获取不同类型的锁。
Lock | Description |
---|---|
DML Locks | Protect data. For example, table locks lock entire tables, while row locks lock selected rows. |
DDL Locks | Protect the structure of schema objects—for example, the dictionary definitions of tables and views. |
System Locks | Protect internal database structures such as data files. Latches, mutexes, and internal locks are entirely automatic. |
DML锁,也称为数据锁,保证多个用户并发访问的数据的完整性。DML语句自动获取TX锁和TM锁,即行锁与表锁。
5.1.DML–ROW Locks(TX)行锁,也称为TX锁。事务为每一行请求行锁,当执行insert、update、delete、merge、select … for update,直至会话commit或者rollback之后行锁释放。
有一些锁,如果我们不知道具体含义的话,可以通过数据字典查看,之前记得碰到TO锁,第一时间也是不知道啥含义。
SQL> set serveroutput onSQL> exec print_table('select * from v$lock_type where type=''TX''');TYPE : TXNAME : TransactionID1_TAG : usn<<16 | slotID2_TAG : sequenceIS_USER : YESDESCRIPTION : Lock held by a transaction to allow othertransactions to wait for it-----------------PL/SQL procedure successfully completed.SQL>SQL> exec print_table('select * from v$lock_type where type=''TO''');TYPE : TONAME : Temp ObjectID1_TAG : object #ID2_TAG : 1IS_USER : NODESCRIPTION : Synchronizes DDL and DML operations on a tempobject-----------------PL/SQL procedure successfully completed--以上pring_table引用了一个脚本,是《Oracle性能优化与诊断案例精选》里面看到的create or replace procedure print_table( p_query in varchar2 )AUTHID CURRENT_USERisl_theCursor integer default dbms_SQL.open_cursor;l_columnValue varchar2(4000);l_status integer;l_descTbl dbms_SQL.desc_tab;l_colCnt number;beginexecute immediate'alter session setnls_date_format=''dd-mon-yyyy hh24:mi:ss'' ';dbms_SQL.parse( l_theCursor, p_query, dbms_SQL.native );dbms_SQL.describe_columns( l_theCursor, l_colCnt, l_descTbl );for i in 1 .. l_colCnt loopdbms_SQL.define_column(l_theCursor, i, l_columnValue, 4000);end loop;l_status := dbms_SQL.execute(l_theCursor);while ( dbms_SQL.fetch_rows(l_theCursor) > 0 ) loopfor i in 1 .. l_colCnt loopdbms_SQL.column_value( l_theCursor, i, l_columnValue );dbms_output.put_line( rpad( l_descTbl(i).col_name, 30 )|| ': ' ||l_columnValue );end loop;dbms_output.put_line( '-----------------' );end loop;execute immediate'alter session set nls_date_format=''dd-MON-rr'' ';exceptionwhen others thenexecute immediate'alter session set nls_date_format=''dd-MON-rr'' ';raise;end;/
那TX锁我们怎么查呢,参考这里
-------------------------------小节分割线-----------------------------------