Oracle锁机制之分类和死锁

1 Oracle锁

1.1 简介

数据库是一个多用户使用的共享资源。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。

加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作。
Oracle的锁机制是一种轻量级的锁定机制,不是通过构建锁列表来进行数据的锁定管理,而是直接将锁作为数据块的属性,存储在数据块首部。

Oracle 数据库中,它并不是对某个表加上锁或者某几行加上锁, 锁是以数据块的一个属性存在的。 也就是说, 每个数据块本身就存储着自己数据块中数据的信息,这个地方叫 ITLInterested Transaction List), 凡是在这个数据块上有活动的事务,它的信息就会记录在这里面供后续的操作查询,一保证事务的一致性。
点击了解Oracle中 逻辑存储结构

oracle数据库中,不存在真正意义上属于某个对象或数据的锁。oracle锁的信息是数据块的一个物理属性,而不是逻辑上属于某个表或某个行。

1.2 分类

1.2.1 用户和系统分

按用户和系统分可以分为自动锁显示锁

  • 自动锁( Automatic Locks
    当进行一项数据库操作时,缺省情况下,系统自动为此数据库操作获得所有有必要的锁。
    自动锁分为三种:
    • DML 锁
    • DDL 锁
    • systemlocks
  • 显示锁( Manual Data Locks
    某些情况下,需要用户显示的锁定数据库操作要用到的数据,才能使数据库操作执行得更好,显示锁是用户为数据库对象设定的

1.2.2 锁级别分

按锁级别分可以分为排它锁共享锁
排他锁(exclusive lock,即X锁)和共享锁(share lock,即S锁)
点击了解 oracle锁机制之悲观锁与乐观锁以及for update用法

  • 排他锁(exclusive lock,即X锁)
    事务设置排它锁后,该事务单独获得此资源,另一事务不能在此事务提交之前获得相同对象的共享锁或排它锁。

  • 共享锁(share lock,即S锁)
    共享锁使一个事务对特定数据库资源进行共享访问——另一事务也可对此资源进行访问或获得相同共享锁。
    共享锁为事务提供高并发性,但如拙劣的事务设计+共享锁容易造成死锁或数据更新丢失。

1.2.3 锁类型分

1.2.3.1 内部闩锁

Oracle的内部闩锁(Latch)是一种特殊的低级别同步机制,用于保护共享数据结构,以避免并发访问时发生冲突。在Oracle数据库中,Latch作为一种轻量级的锁资源,主要工作在内存中,它允许多个进程不会同时修改某个特定的共享资源。

Latch特点:

  • 快速简短的操作:Latch 设计用于快速、短暂的锁定资源,以防止并发进程同时修改某个共享资源。
  • 内存中的运作:与锁定数据库对象(如表和索引)的锁不同,Latch 仅在内存中工作,并且只能被同一实例访问。
  • 非入队机制:Latch 的获取是非入队的,也就是说,没有先来后到的概念,一旦前面的进程释放了Latch,后面的进程会立即竞争获取。
  • 不存在死锁问题:由于Latch的设计和实现方式,它在Oracle中不存在死锁的问题。
  • 旋转和休眠策略:当进程无法立即获得Latch时,它会进行CPU的spin(旋转),如果spin次数达到阈值,则会进行短期的休眠,然后再尝试获取Latch
  • 等待链表机制:在某些情况下,比如长期等待Latch时,进程会进入休眠,并把请求放入Latch等待链表中。当占用Latch的进程释放它时,它会检查链表并唤醒等待的进程。
  • 系统监控与调整:虽然Latch的等待在Oracle中是不可避免的,但通常不需要调整,除非获取次数异常高或等待时间过长。

Oracle数据库中,Share Pool LatchLibrary Cache Latch都是用于保护共享内存区域访问的同步机制。
share pool latchlibrary cache latch 区别:

  • 作用范围
    • Share Pool LatchShare Pool Latch保护共享池中的内存分配与释放操作。共享池是SGA(系统全局区)中的一个区域,它存储如SQL语句、PL/SQL程序单元等数据。当需要从共享池中分配或释放内存时,Share Pool Latch确保这些操作不会受到并发执行的其他进程的干扰。
    • Library Cache LatchLibrary Cache Latch保护库缓存内的内存结构。库缓存位于共享池内,主要存放SQL语句、PL/SQL过程及包等的解析后表示。当数据库需要搜索、管理库缓存中的对象时,Library Cache Latch防止并发访问造成的冲突。
  • 争用情况
    • Share Pool Latch:当多个进程试图同时获取共享池资源时,可能会发生Share Pool Latch争用。如果共享池配置过大,或者存在大量硬解析,会增加Share Pool Latch的争用概率。
    • Library Cache Latch:类似地,Library Cache Latch争用通常发生在大量SQL需要硬解析时。由于每个SQL都需要遍历特定的链(bucket),长时间的锁定会导致其他进程等待。
  • 性能影响
    • Share Pool Latch:频繁的Share Pool Latch争用可能会导致性能下降,因为进程需要等待获取 Latch才能继续其操作。此外,过大的共享池大小可能不仅无法解决问题,反而可能加剧Latch争用的问题。
    • Library Cache LatchLibrary Cache Latch的争用同样会影响数据库性能,特别是在SQL语句频繁进行硬解析的情况下。这会导致latch: library cache等待事件,并可能触发数据库性能瓶颈。
  • 调优方向
    • Share Pool Latch:减少硬解析的发生是减少Share Pool Latch争用的有效方法。可以通过调整初始化参数以增加库缓存的容量,或者优化应用程序以更多地使用绑定变量。
    • Library Cache Latch:为了减少Library Cache Latch的争用,可以优化应用程序逻辑以重复利用已解析的SQL语句,减少硬解析的需要。同时,保持库缓存足够大,以存储经常访问的对象也是有效的策略。
  • 数量关系
    • Share Pool Latch:在Oracle 10g及之前的版本中,Share Pool Latch的数量与CPU数量有关,但自11g起已被Mutexes取代。Latch的数量通常设置为一个接近CPU数量的质数。
    • Library Cache LatchLibrary Cache Latch的数量同样依赖于系统中的CPU数量,并且这个数量相对较小,直到Oracle 10g版本为止。
1.2.3.2 DDL锁

DDL锁作用:可以保护模式中对象的结构。

DDL锁细分:

  • Excluseve DDL Lock:排他DDL锁,对象加锁后不能被其他会话修改,如果是表此时可以读取数据
  • Shared DDL Lock:共享DDL锁,保护对象结构,会话不能修改对象结构,但是允许修改数据
  • Breakable Parsed Lock:能打破的解析锁定。这个类型的锁可以被打断,不能禁止DDL操作

会话执行DDL操作时,Oracle会自动的提交一次事务,然后自动给处理对象加锁,当DDL结束时,Oracle会隐式提交事务并释放DDL锁。与 DML不同,用户不能显示的使用DDL锁。

1.2.4 DML锁

也属于按类型分得锁,主要用于保护数据,主要保证访问时数据的完整性
DML 锁用于控制并发事务中的数据操纵,保证数据的一致性和完整性。主要用于保护并发情况下的数据完整性。
DML 语句能够自动地获得所需的 表级锁(TM)行级(事务)锁(TX)

1.2.4.1 TM锁

TM(表级锁):主要作用是防止在修改表的数据时,表的结构发生变化。例如会话S在修改表A的数据时它会得到表A的TM锁,而此时不允许其他会话对该表进行变更或者删除。
当一个事务通过INSERT、UPDATE、DELETE、MERGE或SELECT...FOR UPDATE等语句修改表时,就会获得一个表锁,也称为TM锁。这种锁的主要作用是为事务保留对表的DML访问权限,并防止与事务冲突的DDL操作

当事务获得行锁后,此事务也将自动获得该行的表锁(共享锁),以防止其它事务进行 DDL 语句影响记录行的更新。
事务也可以在进行过程中获得共享锁或排它锁,只有当事务显示使用 LOCK TABLE 语句显示的定义一个排它锁时,事务才会获得表上的排它锁,也可使用 LOCK TABLE 显示的定义一个表级的共享锁。

TM 锁包括了 SS、 SX、 S、 X 等多种模式,在数据库中用 0-6 来表示。不同的 SQL 操作产生不同类型的 TM 锁.
详解各种锁模式:

  • 0 (None):没有施加任何锁。适用于只读查询,不对数据进行任何修改的操作。
  • 1 (NULL):保留字,无实际意义。
  • 2 (SS, Row-S):行级共享锁,允许其他事务查询这些行,但不允许修改。
  • 3 (SX, Row-X):行级排它锁,不允许其他事务进行任何DML操作。
  • 4 (S, Share):共享锁,用于读取操作,允许多个事务共享资源,但不允许修改。
  • 5 (SSX, S/Row-X):共享行级排它锁,允许事务在读取过程中升级为排它锁。
  • 6 (X, Exclusive):排它锁,提供独占访问,适用于数据插入、更新、删除等操作

会话在做DML操作的时候,数据库会先申请数据对象上的共享锁,防止其他会话对此对象做DDL操作。一旦申请成功。 则会对要修改的记录申请排它锁。(如果此时其他会话正在修改数据,则等待其他事务结束后再增加排它锁)

1.2.4.2 TX锁

TX(事务锁或行级锁):也叫事务锁。当修改表中某行记录时。需要对将要修改的记录加行级锁。该锁属于排他锁,是在事务首次发起 DML 语句时获得的,保持到事务被提交或回滚
TX 锁主要用于保护数据行的完整性,确保在多会话环境下对同一数据行的访问不会导致数据不一致的问题。
TM锁获得后(即表级锁已经被锁定),系统再自动申请TX锁。

1.2.4.3 加锁过程和原理

Oracle 执行 DML 语句时,系统自动在所要操作的表上申请 TM 类型的锁。当 TM 锁获得后,系统再自动申请 TX 类型的锁,并将实际锁定的数据行的锁标志位进行置位。
这样在事务加锁前检查 TX 锁相容性时就不用再逐行检查锁标志,而只需检查 TM 锁模式的相容性即可,大大提高了系统的效率。

在数据行上只有 X 锁(排他锁)
Oracle 数据库中,当一个事务首次发起一个 DML 语句时就获得一个 TX 锁,该锁保持到事务被提交或回滚。当两个或多个会话在表的同一条记录上执行 DML 语句时,第一个会话在该条记录上加锁,其他的会话处于等待状态。当第一个会话提交后, TX 锁被释放,其他会话才可以加锁。
Oracle 数据库发生 TX 锁等待时,如果不及时处理常常会引起 Oracle 数据库挂起,或导致死锁的发生,产生ORA-600 的错误。这些现象都会对实际应用产生极大的危害,如长时间未响应,大量事务失败等。

加行锁原理:

一个事务要修改块中的数据,必须获得该块中的一个itl,通过itlundo segment header中的transaction table,可以知道事务是否处于活动阶段。事务在修改块时(其实就是在修改行)会检查行中row header中的标志位,如果该标志位为0(该行没有被活动的事务锁住),就把该标志位修改为事务在该块获得的itl的序号,这样当前事务就获得了对记录的锁定,然后就可以修改行数据了,这也就是oracle行锁实现的原理。

1.2.4.4 共享方式的表级锁

共享方式的表级锁(Share)是对表中的所有数据进行加锁,该锁用于保护查询数据的一致性,防止其它用户对已加锁的表进行更新。
其它用户只能对该表再施加共享方式的锁,而不能再对该表施加独占方式的锁,共享更新锁可以再施加,但不允许持有共享更新封锁的进程做更新。
共享该表的所有用户只能查询表中的数据,但不能更新。

共享方式的表级锁只能用户用 SQL 语句来设置.
语句格式如下:

LOCK TABLE <表名>[,<表名>]... IN SHARE MODE [NOWAIT]

执行该语句,对一个或多个表施加共享方式的表封锁。
当指定了选择项 NOWAIT,若该锁暂时不能施加成功,则返回并由用户决定是进行等待,还是先去执行别的语句。
持有共享锁的事务,在出现如下之一的条件时,便释放其共享锁:

  • 执行 COMMIT 或 ROLLBACK 语句
  • 退出数据库( LOG OFF)
  • 程序停止运行

共享方式表级锁常用于一致性查询过程,即在查询数据期间表中的数据不发生改变

1.3 锁等待和死锁

1.3.1 定义

由于占用资源不能及时释放,会造成锁的等待。例如,当一个会话S1修改记录A,A被加锁,如果另一个会话S2也来修改记录A,那么第二个会话将得不到排它锁会一直等待,知道会话S1事务提交释放锁,会话S2才能对数据进行操作。

死锁锁等待的一个特征,加入,当会话S1需要修改2个资源,这2个资源在同一个事务里,修改第一个对象A时对其加锁,修改第2个对象B时另一个会话S2获取了锁并锁定了B,等待A释放锁,那么就出现了死锁。
当出现死锁的时候可以使用下面的SQL尝试处理,例如下面是之前遇到的一个问题,SID869修改DICTEN表,且锁住了表。 SID1393尝试修改DICTEN表时死锁一直等待

1.3.2 死锁分析

Oracle 数据库中,当出现死锁时,Oracle 会自动检测到并记录相关信息。我们可以通过以下步骤查看和分析死锁日志:

  • 查看死锁日志:
    • 查看 Alert 日志文件
      Oracle 的 Alert 日志文件包含了数据库的各种警告和错误信息,包括死锁信息。要查看 Alert 日志文件,可以使用以下方法:
      通过 SQL*Plus 或 Oracle SQL Developer SHOW PARAMETER alert
      这条命令会显示 Alert 日志文件的路径。通常路径为 $ORACLE_BASE/diag/rdbms/your_db/trace/alert_your_db.log
    • 通过操作系统访问
      直接访问数据库服务器,找到 Alert 日志文件的路径,然后使用操作系统的命令(如 cat, less, more, tail)查看日志文件。
  • 使用 Oracle Enterprise Manager (OEM)
    如果你有使用 OEM,可以通过其图形化界面查看和搜索死锁相关的信息。
  • 分析死锁日志
    在 Alert 日志文件中,死锁会以 “ORA-00060: deadlock detected” 错误开始。以下是一个死锁日志示例及其分析方法:
    示例死锁日志
*** 2024-06-05 14:23:45.123
DEADLOCK DETECTED (ID 0x70007b3c8)

[Transaction Deadlock]
  Current SQL Statement for this session:
  UPDATE employees SET salary = salary + 1000 WHERE employee_id = 101

  *** Deadlock graph:
        ---------Blocker(s)--------
        Resource Name: TX-00070007b3c8-00000000
        Lock Mode:     Exclusive
        Hold Mode:     Exclusive
        Wait Mode:     None
        Session ID:    34
        Session Serial #:  15
        Session Owner: HR
        ----------------------------

        ---------Waiter(s)--------
        Resource Name: TX-00070007b3c8-00000000
        Lock Mode:     Exclusive
        Hold Mode:     None
        Wait Mode:     Exclusive
        Session ID:    47
        Session Serial #:  8
        Session Owner: HR
        ----------------------------

  *** Final Blocker:
        Resource Name: TX-00070007b3c8-00000000
        Lock Mode:     Exclusive
        Hold Mode:     Exclusive
        Wait Mode:     None
        Session ID:    47
        Session Serial #:  8
        Session Owner: HR

*** 2024-06-05 14:23:45.123
  • 分析步骤
    识别死锁会话和资源
    Session ID:日志中显示了涉及死锁的会话 Session ID 和 Session Serial #。在本例中,会话 34 和 47 互相持有对方所需的资源。
    资源名称:Resource Name: TX-00070007b3c8-00000000 表示锁的资源名称,TX 表示事务锁。
    查看 SQL 语句:当前死锁会话的 SQL 语句也记录在日志中。在本例中,会话 34 正在执行 UPDATE employees SET salary = salary + 1000 WHERE employee_id = 101
    分析死锁图
    Blocker(s):描述了当前持有资源且阻塞其他会话的会话。在本例中,会话 34 是阻塞会话。
    Waiter(s):描述了等待资源且被阻塞的会话。在本例中,会话 47 是等待会话。

Oracle V$ 视图
使用以下查询查看当前锁和等待的详细信息:

-- 查看当前锁信息
SELECT * FROM v$lock;

-- 查看等待事件
SELECT * FROM v$session_wait;

-- 查看死锁检测器信息
SELECT * FROM v$system_event WHERE event = 'enqueue';


-- 查看死锁是否存在
SELECT username, lockwait, status, machine, program   
FROM v$session WHERE sid IN (SELECT session_id FROM v$locked_object);

-- 查看死锁的详细信息
SELECT   
    l.xidusn, l.object_id, o.owner, o.object_name,   
    l.session_id, l.oracle_username, l.os_user_name, l.process,   
    DECODE(l.locked_mode, 0, '', 1, 'NULL', 2, '(SS)', 3, '(SX)', 4, '(S)', 5, '(SSX)', 6, '(X)', '??') locked_mode   
FROM   
    v$locked_object l, dba_objects o   
WHERE   
    l.object_id = o.object_id;

--分析死锁日志
SELECT   
    username, osuser, machine, terminal, program, sid, serial#, status, sql_address, sql_text   
FROM   
    v$session ss, v$sqltext sq   
WHERE   
    type='USER' AND ss.sql_address=sq.address   
ORDER BY   
    ss.sid, ss.serial#, sq.piece;

1.3.3 死锁解决

Oracle中,当发生死锁时,用户或应用程序需要手动介入以解决问题。这可能包括终止导致死锁的事务、调整事务的隔离级别或锁策略、优化查询语句等

一旦确定了死锁的原因,可以采取相应的措施来解决它。这通常包括结束导致死锁的会话、优化数据库操作、修改Java代码等。
结束会话:使用 ALTER SYSTEM KILL SESSION 'sid,serial#'语句, 锁表进程中的 SID和SERIAL 字段的值,把所有的值全部杀掉即可,该命令可以结束导致死锁的会话。注意,这可能会导致数据丢失或不一致,因此应谨慎使用。
优化数据库操作:通过减少长时间的事务、优化查询语句、调整锁策略等方式来减少死锁的发生。

相关推荐

  1. Oracle机制

    2024-06-16 13:50:03       8 阅读
  2. Oracle 、指标汇总

    2024-06-16 13:50:03       16 阅读
  3. 解决Oracle问题

    2024-06-16 13:50:03       6 阅读
  4. PostgreSQL Oracle机制对比

    2024-06-16 13:50:03       10 阅读
  5. 2024-06-16 13:50:03       18 阅读
  6. 软设问题

    2024-06-16 13:50:03       11 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-16 13:50:03       14 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-16 13:50:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-16 13:50:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-16 13:50:03       18 阅读

热门阅读

  1. Web前端收入来源:探索多元化的盈利渠道

    2024-06-16 13:50:03       5 阅读
  2. yolov10 学习笔记

    2024-06-16 13:50:03       6 阅读
  3. js面试题

    2024-06-16 13:50:03       6 阅读
  4. ndk-build

    2024-06-16 13:50:03       5 阅读
  5. AI学习指南机器学习篇-KNN基本原理

    2024-06-16 13:50:03       7 阅读
  6. XML XSLT:技术与应用解析

    2024-06-16 13:50:03       5 阅读
  7. 【C++】priority_queue的用法(模板参数的实例)

    2024-06-16 13:50:03       6 阅读
  8. 决策树算法介绍 - 原理与案例实现

    2024-06-16 13:50:03       8 阅读
  9. Web前端设计培训机构:深度解析与实战指南

    2024-06-16 13:50:03       8 阅读