暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

面试官收手吧,我已经摸透Oracle锁了!

kk的DBA随笔 2025-02-22
367

Oracle锁机制介绍

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

Part1Lock 锁

锁介绍

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

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

模拟锁的情况:

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

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

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

窗口一:

窗口二:

Oracle 中锁的大体分类

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

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

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

环境 oracle 19c 共 291 种 锁。

 .........

锁的原则

  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)。


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

RI 锁

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

示例:

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

 

 

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

死锁

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

会话一:

会话二:

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

Part2Latch 锁(闩锁)

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

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 锁。


文章转载自kk的DBA随笔,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论