1.为什么会有锁
没有并发就没有锁
当多个用户都想操作同一个表时,就需要有锁来保证任何一个时间段内都只有一个用户拥有操作该表的权限,这就是锁。
2.oracle锁的分类
enqueues:与队列类型的锁,通常和业务有关系。就如第一点所说,多个用户都想操作同一个表时,可以利用锁的机制,是用户们形成一种队列。
latches:系统资源方面的锁,比如内存结果,sql解析(将会在别的文章中讲解)
3.锁的类型
可见锁一共有205种类型。想要了解所有的锁是不能的,但是我们可以熟悉一下几种锁
4.常见锁类型
4.1 TM锁和TX锁(如下图)
TM锁是表级锁,发生在insert ,update以及select for update 操作时,目的是保证操作能够正常运行,并且阻止其他人对表执行DDL(数据定义型语言:create,drop,truncate,alter)操作。
TX锁是事务锁(有时也叫做行锁)。对于正在修改的数据,阻止其他会话进行修改。
由图可知;
- 黄色标记的行是我们要操作的行,
- 在我们要操作这行数据的时候,oracle会给该表(表名:EMPLOYEES)添加一把TM锁(这把锁一般是共享锁,并不会阻止别的用户操作这个表,如进行select查询)。该锁的目的是阻止其他用户对表执行DDL操作,如删除表,删除该表数据,增加字段等。
- 同时会在黄色标记行上添加一把TX锁,这个锁的目的就是阻止别的用户对该行数据进行修改。
4.1.1 TM和TX锁举例
如图所知:
- 会话1,正在对t表中x=3的值更改为2。且没有提交
- 会话2,在会话1还没有提交对T表的修改时,又对x=3的值进行更为4。此时。会话2就被阻塞。
- 会话3,sid 为69(69是会话2的oracle服务进程号)和101(101是会话1的oracle服务进程号)。69和101都对T表进行了TM锁,但是没有阻塞(这就是因为锁级别为3级(因为lmode=3),该锁为共享锁)。又因69和101都是要进行修改某行数据,所以要使用事务锁(TX锁),此时形成了阻塞,101锁的类型为6(因lmode=6),同时block=1(代表阻塞了一个用户),69则是在request=6,表示69正在请求一个lmode=6的锁。
如图所知:
- 这是两个会话都打算删除同一个表中所有数据,最后形成了阻塞
如图所知:
注意:该insert形成阻塞是有条件的,当前要插入的值,是唯一不可重复字段值,否则数据库是允许同时插入多条数据再一次性提交的。
- 两个用户同时向表中(插入的行拥有主键属性)插入一个相同的值,最终导致阻塞
如图所知:
- 某个会话在查询t表中的值,并将更新数据,所以与其他将修改该表数据的会话形成阻塞。
4.1.2TM锁的互斥关系
- 第一列:表示lmode 的模式,数字越大限制性越高
- 第二列:表示手工锁表语句,即我们也可以通过自己写这些sql语句达到锁表目的
- 第三列:当TM锁的第一列和第三列的值不能同时共存。(这话具体何意,有待进一步研究)
- 第四列:当TM锁模式为第一列时,我们还可以进行DML语句。
4.2 RI锁
RI锁定—基于引用关系的锁定。当对具有主外键关系的表做DML操作时,锁定不单单发生在操作表上,相应的引用表上也可能加上相应的锁定.具体详解见代码:
LEO1@LEO1> create table a (id int primary key); a是主表,定义了id字段为主键 Table created. LEO1@LEO1> create table b (id references a(id)); b是从表,id字段是引用主表的id字段 Table created. LEO1@LEO1> insert into a values(1); 往主表a中插入一条数据但没有提交,事务没有结束会产生锁定 1 row created. LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in (\'TM\',\'TX\') order by 1,2; SID TYPE ID1 ID2 LMODE REQUEST BLOCK ---------- ---------- ---------- ---------- ---------- ---------- ---------- 138 TM 73465 0 3 0 0 insert由于有从属关系因此会在2个表上都加3号共享锁 138 TM 73467 0 3 0 0 138 TX 196640 940 6 0 0 LEO1@LEO1> select object_name from dba_objects where object_id in (73465,73467); ID1就是138会话操作的对象id,我们会在主表和从表上都加上表级锁 OBJECT_NAME -------------------------------------------------------------------------------- A 73465 B 73467 LEO1@LEO1> commit; 提交之后释放锁 Commit complete. LEO1@LEO1> select sid,type,id1,id2,lmode,request,block from v$lock where type in (\'TM\',\'TX\') order by 1,2; 锁会随着事务的结束而释放 no rows selected LEO1@LEO1> select * from a; a表中有一条记录 ID ---------- 1 LEO1@LEO1> select * from b; no rows selected LEO1@LEO1> update a set id=100 where id=1; 主表a上更新了一条记录 1 row updated.
4.3 BI锁和外键索引
如图所知:
- DEPARTMENTS 表是一个主键表;EMPLOYEES 表是一个子表, 其中DEPARTMENT_ID是外键。
- 在删除主键表中的数据时,oracle 会根据表中的主键去查找所有的子表,是否在子表中有值和主键关联。
- 可见,如果此时执行删除主键行/修改主键值,那么oracle就会去所有子表中查询该主键相关的数据。因为所有的子表外键没有构建索引,所以oracle只能进行全表扫描。那么就会形成阻塞
如图所说:
- 该图与上图形成对比。当子表的外键生成了索引后,那么当修改主键时,oracle只需要去索引中查找相关数据。
总结:因主键表,会和很多子表相关联,所以从上面这一点可以看出来,主键表建立后,最好就不要再修改,会导致很大的影响。
4.4 死锁
以上的所有锁,对oracle的影响只能算是阻塞,是因为某事物没有结束导致后面的会话排队。
真正的死锁是如下这种情况:
死锁就是a在等待b释放锁,而b又在等待a释放,形成交叉。 会话一: SQL> update t1 set id=11 where id=1; 已更新 1 行。 会话二: SQL> update t2 set id=11 where id=1; 已更新 1 行。 会话一: SQL> update t2 set id=11 where id=1; __(等待) 会话二: SQL> update t1 set id=11 where id=1; __(等待)
针对这种情况,在会话一种将会弹出这样一段内容:
会话一: SQL> update t2 set id=11 where id=1; update t2 set id=11 where id=1 * 第 1 行出现错误: ORA-00060: 等待资源时检测到死锁
所以,死锁,oracle是能够自动检测出来的, 并自动对死锁进行处理(处理方式:结束其中某一个会话)。
5.当我们知道锁的问题后,我们更想知道是什么原因导致的锁,锁的是什么
1.查看当前锁 select * from v$lock where type in (\'TM\',\'TX\'); 获取到sid为XXX;TM锁对应的id1的值为PPP;
2.查看被锁的表
select object_name from dba_objects where object_id=PPP; object_name 就是表名;object_id是第一条中TM锁对应的id1的值
3.查看sid为XXX。会话
select sid,username,sql_address,blocking_session from v$session where sid=\'xxxx\'; sql_address为OOO
4.查看地址为OOO的sql语句 select * from v$sql where address=\'OOO\';
5.查看sid为XXX。会话等待事件是什么 select * from v$session_wait where sid=\'XXX\';
从该表中我们已经列出了如何查询会话级性能的步骤。
第一步:查找锁。这一步只是限于当发生锁机制时使用。目的也是为了获取到发生锁机制的会话id。
注:在发生别的性能问题时,如何查找会话id有待进一步研究
第二步:根据会话id 可以获取到用户名、sql地址(映射的是sql语句)、与同他发生阻塞的会话id。
第三步:根据第二步获取到的sql地址查找该id当前正在执行的sql 语句
第四步:根据会话id 可以获取到会话等待事件是什么。