Oracle 深入理解Lock和Latch ,解析访问数据块全流程

Oracle 锁机制介绍

根据保护对象的不同,单实例Oracle数据库锁可以分为以下几大类: DML lock(data locks,数据锁):用于保护数据的完整性; DDL lock(dictionary locks,字典锁):用于保护数据库对象的结构(例如 表、视图、索引的结构定义); internal locks,latches,mutex,pin:保护内部数据库结构;Oracle DML锁共有两个层次,即行级锁(TX)和表级锁(TM)。

一 .Lock 锁

1.锁介绍

我们在谈到性能优化的时候,通常是讲系统缓慢的时候需要性能优化,系统缓慢通常是由什么原因导致的呢?大概上讲分为两种

  1. 系统的资源耗尽了(cpu耗尽了,IP,吞吐量上不去)性能枯竭,性能达到了极限。
  2. 锁定,阻塞,发现系统性能很高,但应用程序性能就是上不去,这种情况通常来讲是阻塞。

模拟锁的情况:

窗口一:建一张表,插入一条数据,id上有主键

窗口二:再往这张表id列插入一条同样的数据

由于primary key本质上是 unique key + not null ,由于第二个事务不知道第一个事务是提交还是回滚,如果提交则事务二插入失败,如果回滚则插入成功,此时会造成锁阻塞。

窗口一:

窗口二:

2.Oracle中锁的大体分类

Enqueues : 队列类型的锁,通常和业务相关的锁。

Latches: 系统资源方面的锁,比如内存结构,SQL解析等

锁的所有类型可以根据视图v$lock_type 查看

环境oracle 19c 共291种 锁。

.........

3.锁的原则

  1. 只有被修改时,行才会被锁定。
  2. 当一条语句修改了一条记录,只有这条记录上被锁定,在Oracle数据库中不存在锁升级。
  3. 当某行被修改时,它将阻塞别人对它的修改。
  4. 当一个事务修改一行时,将在这个行上加上行锁(TX),用于阻止其它事务对相同行的修改。
  5. 读永远不会阻止写。
  6. 读不会阻塞写,但有唯一的一个例外,就是select ...for update。
  7. 写永远不会阻塞读。
  8. 当一行被修改后,Oracle通过回滚段提供给数据的一致性读。

这里其实听过其它很多人说过Oracle锁升级的概念,根据一下Oracle资料调查Oracle不存在锁升级,但确实存在锁转换。

数据库在必要时执行锁转换。在锁转换中,数据库自动将较低限制的表锁转换为较 高限制的其它锁定。(锁转换不同于锁升级,锁升级发生在当某个粒度级别持有许多锁(例如行),数据库将其提高到更高粒度级别(例如表)Oracle数据库永远不会升级锁。

Oracle的锁是block里面实现的,(SQLSERVER、DB2是内存里面实现的。内存实 现有资源消耗问题,当内存不足会引发锁升级)但是Oracle不会发生锁升级。

事务拥有在此事务内被插入(insert)、更新(update)、删除(delete)的数 据行的排它行级锁(exclusive row lock)。对于数据行来说,排它行级锁已经是 限制程度最高的锁,因此无需再进行锁转换(lock conversion)。

4.TM锁和TX锁

  1. TM表锁,发生在insert,update,delete以及select for update操作时,目的是保证操 作能够正常进行,并且阻止其它人对表执行DDL操作。
  2. TX锁事务锁(行锁)对于正在修改的数据,阻止其它会话进行修改。

根据官方文档归纳总结: v$lock视图列出当前系统持有的或正在申请的所有锁的情况,其主要字段说明如下:

v$locked_object视图列出当前系统中哪些对象正被锁定,其主要字段说明如下:

inert 锁过程验证:

窗口一:

窗口二:

查看锁结构:

在整个过程中

  1. 首先会话58 对表T 加TM(表锁 )和TX(行锁),TM锁的BLOCK = 1 代表这个会话在阻塞其它会话
  2. 第二个会话273 也对T表加了一个TM(表锁),又加了一个TX(行锁),这个行锁request = 4,代表它正在被阻塞
  3. 中间那把锁 (会话273的行锁)正在请求锁并且被会话58的行锁阻塞。会话58持有的行锁(LMODE = 6)正在阻塞会话273对相同行的访问,导致会话273的请求(REQUEST = 4)被阻塞。

update锁过程验证:

窗口一:

窗口二:

查看锁过程:

整个过程

会话53 对表T加TM和TX锁,TX锁阻塞了其它会话,会话273对表T 加TM和TX锁,TX锁request=6 被阻塞。

delete 锁过程验证:

窗口一:

窗口二:

查看锁过程:

会话58对表T加表锁和行锁,会话273对表T加表锁和行锁,58的行锁阻塞了273的行锁。

从v$session_wait 查看阻塞详情(从会话层面查看锁阻塞):

所以TX锁又叫队列锁(enq锁)

TM锁的几种模式:

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ORACLE里锁有以下几种模式: 0:none 1:null 空 2:Row-S 行共享(RS):共享表锁,sub share 3:Row-X 行独占(RX):用于行的修改,sub exclusive 4:Share 共享锁(S):阻止其他DML操作,share 5:S/Row-X 共享行独占(SRX):阻止其他事务操作,share/sub exclusive 6:exclusive 独占(X):独立访问使用,exclusive 数字越大锁级别越高, 影响的操作越多。 1级锁有:Select,有时会在v$locked_object出现。 2级锁有:Select for update,Lock For Update,Lock Row Share select for update当对话使用for update子串打开一个游标时,所有返回集中的数据行都将处于行级(Row-X)独占式锁定,其他对象只能查询这些数据行,不能进行update、delete或select for update操作。 3级锁有:Insert, Update, Delete, Lock Row Exclusive 没有commit之前插入同样的一条记录会没有反应, 因为后一个3的锁会一直等待上一个3的锁, 我们必须释放掉上一个才能继续工作。 4级锁有:Create Index, Lock Share locked_mode为2,3,4不影响DML(insert,delete,update,select)操作, 但DDL(alter,drop等)操作会提示ora-00054错误。 00054, 00000, "resource busy and acquire with NOWAIT specified" 5级锁有:Lock Share Row Exclusive 具体来讲有主外键约束时update / delete ... ; 可能会产生4,5的锁。 6级锁有:Alter table, Drop table, Drop Index, Truncate table, Lock Exclusive |

TM锁几种模式的互斥关系:

|----|------------------------------------------|-----------|--------------------------------------------|
| 模式 | 锁定的SQL | 排斥的模式 | 允许的DML |
| 2 | lock table t in row share mode | 6 | select, insert, update, delete, for update |
| 3 | lock table t in row exclusive mode | 4,5,6 | select, insert, update, delete, for update |
| 4 | lock table t in share mode | 3,5,6 | select |
| 5 | lock table t in share row exclusive mode | 3,4,5,6 | select |
| 6 | lock table t in exclusive mode | 2,3,4,5,6 | select |

5..RI锁 - 基于 引用关系的锁

当对具有主外键关系的表做DML操作时,锁定不单单发生在操作表上,相应的引用表上也可能加上相应的锁定

示例:

insert 操作在往主表注入一条数据的时候会在主表和从表上同时上TM锁,对主表上TX锁

update 操作 只会在主表上加TM和TX锁

6.死锁

两个会话互相持有对方资源导致死锁 (Oracle 会自动检测死锁)

会话一:

会话二:

如上,Oracle自动检测到了死锁,释放了会话一。

二 .Latch 锁(闩锁)

用中国话理解闩,就是古代插在门后面那个上锁的。(意味着获取一个资源之后,把它插住,谁也用不了,用完之后再把它打开)

Latch和Lock的区别:

Lock 可以想象成食堂排队打饭,是有序的。

Latch可以想象成微信群里抢红包,抢红包不是一个一个排队的抢,是无序的。而就因为Latch的无序性,才需要Latch锁

|-----|----------|------------|
| | Latch | Lock |
| 队列性 | 无序 | 有序 |
| 时长 | 很短 | 可能很长 |
| 层面 | 数据库资源层 | 业务应用层 |
| 目的 | 保护资源的完整性 | 保证业务操作的完整性 |

Latch的目的

  1. 保证资源的串行访问: -保护SGA的资源访问 -保护内存的分配

  2. 保证执行的串行化: -保护关键资源的串行执行 -防止内存结构损坏

Latch在哪里 ->SGA

sharedpool -sql解析,sql重用....... buffercache -数据访问,数据写入磁盘,数据读入内存,修改数据块,数据段扩展

oracle19c 共993个Latch

Latch的的获取:

wait方式--如果无法获取请求的latch ,则:

-spin

当一个会话无法获得需要的latch时,会继续使用CPU( CPU空转),达到一个间隔后, 再次尝试申请latch ,直到达到最大的重试次数。

-sleep

当一个会话无法获得需要的latch时,会等待一段时间(sleep) ,达到 Y间隔后,再次 尝试申清latch,如此反复,直到达到最大的重试次数。

No wait方式--如果无;标取请求的latch ,则:

-不会发生sleep或者spin.

-转而去获取其它可用的Latch

Latch锁在data buffer中的应用

data buffer存在的意义就是为了在内存中进行高速的数据查找和更新,尽量减少磁盘的IO操作, Buffer Cache中存在一个Hash Bucket结构,将数据库中已经读取的数 据块放到里面,在从数据库文件中读取到一个数据块后,Oracle会根据这个数据块的 文件编号,段编号,数据块号组合到一起通过一个内部的hash算法运算后,会放到不同 的hash bucket中,每个Hash Bucket都有一个Hash chain list,保留Buffer Header 中的信息,然后通过这个list,把相同hash值的Buffer串起来.结构如图:

​​​

为保护这个结构不受同步更新的破坏,Oracle设计了一个CBC latch的锁结构 (Cache Buffers Chains),一个latch保护32的桶(Bucket),所以为了访问hash列表, 必须先获得CBC Latch.先获取Latch后,在对Buffer进行操作.对buffer操作是一个比较耗时的操作,比如从磁盘读取block。由于一个latch管理 32个桶,所以对buffer操作时不能继续持有latch。Oracle使用两阶段加latch锁的 方式解决这个问题。

  1. 加latch锁
  2. 查找buffer,并对buffer加pin锁,如果是读取操作则为shared pin,若是写操 作,则exclusive pin
  3. 释放latch锁
  4. 对buffer进行操作
  5. 加latch锁
  6. 释放pin锁
  7. 释放latch锁

参见下图,一个buffer拥有一个pin锁,每个pin锁对应两个列表,user's list(拥有 锁的用户列表)和waiter's list(等待锁的列表),如果用户等待pin锁的时间超过1 秒,则认为出现死锁。buffer和pin锁是 一 一对应关系

加上锁保护以后,整体的图

所以读取数据块的过程如下,当一个用户进程想要访问一个数据块:

  1. 根据数据文件号,块号生成hash 值
  2. 在Hash Table中找到bucket地址
  3. 获取CBC latch,一个latch保护32个bucket
  4. bucket有一个指针指向bh(buffer head)
  5. 根据bh的搜索链表
  6. 匹配bh里面的内容和要找的
  7. 匹配到一个bh,给bh加锁buffer pin(共享和独占)
  8. 释放cbc latch, cbc latch做两件事情:保护链表,保护加buffer pin锁
  9. 进程根据bh里面的ba地址找内存块
  10. 找完了,获得cbc latch,在保护下 释放buffer pin锁

利用哈希表管理已经被缓存的data block。为了使得哈希桶上链表尽量短,理想目 标是一个链表上最多只有一个buffer,哈希桶的数量一般是data buffer数量的两 倍,并把桶分组,每个组对应一个latch,latch用来保护桶中的链表。一个latch保 护32个桶。

虽然和lock相比,latch是轻量级的,但是使用latch也是有比较大的开销的,所以 oracle尽量减少latch数量,所以一个latch管理了高达32个桶。

Oracle 等待事件 latch free:

如:

Oracle事件latch free定义为:当多个进程竞争一个latch 2次,而第2次的竞争者失败时,在 Statspack 或ASH报告中可以看到的Oracle事件名称。 当数据库发生IO瓶颈,并且出现这类事件时,就可以查看latch free对Oracle性能的影响。

latch free 是一种 Oracle 的内部技术,是用于提升性能的一种需要得到保护的数据结构。 通俗讲,在有多个几秒之间,用来竞争访问相同的资源的多个进程中,如果短时间内无法访问此特定资源,则会发生latch free事件。

由于Oracle数据库中大量组件都是由不同的进程驱动的,每一个进程都可以同时访问资源,并产生latch free的。 而我们的目的是保护共有的资源,因此在资源访问时,可能会出现竞争的情况。 在竞争时,如果第二个进程竞争失败,则表示该进程无法获取资源,这就引发了latch free事件。

解决方案:首先查看 latch free 看看哪个类型latch 最多

复制代码
select p1,p2,p3,inst_id,count(*) from gv$active_session_history where sample_time > sysdate-1/24 and event='latch free' group by p1,p2,p3,inst_id order by 5 desc; 

Latch锁类型较多,需要根据不同类型的Latch 做不同的处理方案

快速查看当前Lock锁等待信息:

Oracle提供一个名为utllockt.sql的脚本,它会给出一个树形结构的锁等待图,显示持有影响其他会话的锁的会话。使用此脚本,可以看出一个会话正在等待什么锁,哪个会话持有这些锁。该脚本位于$ORACLE_HOME/rdbms/admin目录下。

复制代码
@?/rdbmsa/admin/utllockt.sql

执行脚本可以迅速看到 会话51在请求一个Share锁,等待47释放Exclusive锁。

相关推荐
白鹭26 分钟前
MySQL源码部署(rhel7)
数据库·mysql
666和7771 小时前
Struts2 工作总结
java·数据库
还听珊瑚海吗1 小时前
SpringMVC(一)
数据库
星期天要睡觉2 小时前
MySQL 综合练习
数据库·mysql
Y4090012 小时前
数据库基础知识——聚合函数、分组查询
android·数据库
JosieBook3 小时前
【数据库】MySQL 数据库创建存储过程及使用场景详解
数据库·mysql
处女座_三月3 小时前
改 TDengine 数据库的时间写入限制
数据库·sql·mysql
酷ku的森4 小时前
Redis中的hash数据类型
数据库·redis·哈希算法