Oracle的锁机制:Enqueue详解

来源:这里教程网 时间:2026-03-03 22:55:21 作者:

在 Oracle 数据库的世界里,锁(Lock)是我们最熟悉的陌生人。我们都知道 UPDATE 会加锁,SELECT FOR UPDATE 也会加锁,但你是否想过,当成千上万个会话同时争抢同一个资源时,Oracle 是如何在底层管理这些锁请求的?

答案就是:排队(Enqueue)。

今天,我们就潜入 Oracle 内核深处,去看看这个名为 Enqueue 的神秘结构是如何指挥交通的。

什么是 Enqueue?

简单来说,实现锁的方式就是排队,而管理这个排队过程的内部结构,就叫 Enqueue。

在 Oracle 内核中,所有与 Enqueue 相关的函数都以 KSQ (Kernel Service Enqueue) 开头。

Lock: 是我们从应用层面看到的概念(比如 TX 锁、TM 锁)。

Enqueue: 是 Oracle 内部管理这些锁的实际数据结构。

你可以通过视图 v$lock_type 来查看各种锁的详细描述。但在底层,最关键的是锁模式(Lock Mode)。

6 种锁模式简介

Oracle 定义了 6 种锁模式,级别越高,限制越严:

NULL: 空锁。

SS (Row-S): 行级共享锁。

SX (Row-X): 行级排它锁(最常见的 DML 锁)。

S (Share): 共享锁(阻止其他 DML)。

SSX (S/Row-X): 共享行级排它锁。

X (Exclusive): 排它锁(唯我独尊,别人只能看不能动)。

谁和谁能共存?

这就涉及到了锁的兼容性。简单记:

大家都是读(S 或 SS),可以和平共处。

只要有人想写(X 或 SX),通常就不兼容了。

比如:持有 S 锁时,别人也能申请 S 锁(共享);但别人申请 X 锁就得排队。

核心机制:Owners, Waiters 与 Converters

Oracle 的 Enqueue 结构通过三个队列来管理锁:

Owners: 已经拿到锁的会话。

Waiters: 没拿到锁,正在苦苦等待的会话。

Converters: 已经拿到了锁,但想升级锁模式(比如从 S 升到 X)的会话。

重点来了:Oracle 的“插队”规则

Oracle 处理锁请求是有优先级的:先处理 Converters,再处理 Waiters。

这意味着,如果你已经持有一把小锁想换大锁,你会比那些手里没锁的人更有优先权。

场景模拟:一场精彩的“插队”大戏

为了讲清楚这个机制,我们来看一个经典的场景:

资源:表 TEST (TM-432-0)

角色:

S1: 会话 1

S2: 会话 2

S3, S4: 会话 3 和 4

剧情发展:

S1 持有 S 锁:S1 正在读取表,加了 S 锁。此时 S1 在 Owners 队列。

S2 申请 X 锁:S2 想独占表,申请 X 锁。因为 S 和 X 不兼容,S2 只能去 Waiters 队列排队。

S1 想要升级 (S -> X):S1 突然想改数据,申请将自己的 S 锁升级为 X 锁。

此时,S1 会进入 Converters 队列。

关键点:虽然 S2 先来排队的,但 S1 是“老客户”要求升级,所以 S1 的优先级高于 S2。

S3, S4 申请 S 锁:这俩兄弟想读表,申请 S 锁。虽然 S 锁和 S 锁兼容,但因为前面有个 S2 在等 X 锁(X 和 S 不兼容),为了防止 S2 被无限饿死,S3 和 S4 也得去 Waiters 队列排在 S2 后面。

结局:

S1 释放 S 锁:系统先扫描 Converters 队列,发现 S1 要升级,于是 S1 立马获得 X 锁,进入 Owners 队列。

S1 释放 X 锁:S1 完事了。系统扫描 Converters 发现没人了,转而去扫描 Waiters 队列。

S2 获得 X 锁:排在队首的 S2 终于熬出头,拿到了 X 锁。

S2 释放 X 锁:S2 完事了。

S3, S4 获得 S 锁:S3 和 S4 终于拿到了锁,大家一起开心读取。

动手实验:眼见为实

我们可以通过 4 个 SQLPlus 会话来复现这个过程。

Session 1 (S1):

-- 1. S1 持有 S 锁

SQL> LOCK TABLE test IN SHARE MODE;

Table(s) Locked.

Session 2 (S2):

-- 2. S2 申请 X 锁(会被堵塞)

SQL> LOCK TABLE test IN EXCLUSIVE MODE;

... (等待中)

Session 3 & 4 (S3, S4):

-- 3. S3, S4 申请 S 锁(也会被堵塞,排在 S2 后面)

SQL> LOCK TABLE test IN SHARE MODE;

... (等待中)

Session 1 (S1 再次操作):

-- 4. S1 尝试升级为 X 锁

-- 注意:这里如果执行会死锁,因为 S2 在等 S1 释放,S1 升级需要 S2 让路。

-- 但在上述原理场景中,通常是 S1 释放 S 锁的瞬间被转化为 X。

-- 我们这里模拟 S1 释放锁:

SQL> commit;

观察结果:

你会发现,S1 提交后,S2 立刻获得了 X 锁。而 S3 和 S4 继续等待。

直到 S2 提交 (commit),S3 和 S4 才会同时提示 Table(s) Locked。

总结

理解了 Enqueue 的 Owners、Waiters 和 Converters 队列机制,你就能看懂那些复杂的锁等待(如 enq: TM - contention)到底是在等什么。

记住一句口诀:“老锁升级最优先,新锁排队在后面”。

相关推荐

热文推荐