首页 > 其他分享 >深入探讨锁升级问题

深入探讨锁升级问题

时间:2024-12-01 21:28:59浏览次数:6  
标签:问题 事务 行级 数据库 深入探讨 升级 死锁 表级

1. 引言

本文深入探讨锁升级问题。

2. 锁升级问题概述

2.1 锁升级的概念

2.1.1 定义

锁升级是指数据库管理系统将较低粒度的锁(如行级锁)转换为较高粒度的锁(如表级锁)的过程。这种情况通常发生在事务对同一对象的多个较低粒度的锁操作导致系统开销过大,或者系统检测到可能出现死锁等并发问题时。例如,在一个数据库事务中,如果对表中的大量行都加了行级锁,数据库可能会判断将这些行级锁升级为表级锁会更高效,从而进行锁升级操作。

2.1.2 目的

主要目的是为了减少系统的资源开销和管理成本。因为管理大量的细粒度锁(如行级锁)需要更多的内存和 CPU 资源来维护锁的信息,包括锁的状态、锁的持有者等。通过锁升级,可以简化锁的管理结构,降低系统开销。

2.2 锁升级的触发条件

2.2.1 锁数量达到阈值

1. 原理: 当一个事务对一个表中的行级锁数量达到数据库系统设定的某个阈值时,可能会触发锁升级。不同的数据库系统对于这个阈值的设定可能不同,这取决于数据库的配置和内部算法。

2. 例: 在某些数据库中,如果一个事务对一个表中的超过 50% 的行都加了行级锁,系统可能会考虑进行锁升级。假设一个表有 100 行数据,当一个事务已经对其中 51 行都加了行级锁时,就有可能触发锁升级。

2.2.2 系统资源考虑

1. 原理: 如果数据库系统检测到管理大量行级锁所消耗的系统资源(如内存、CPU 时间用于锁的维护和检查)超过了一定限度,并且认为升级为表级锁可以减少这种资源消耗,就会触发锁升级。这通常是基于数据库内部的性能优化算法来判断的。

2. 例: 在高并发环境下,如果系统发现由于大量的行级锁操作导致内存中的锁信息存储区域接近满负荷,并且对这些行级锁的操作导致频繁的锁检查和等待,数据库可能会决定将部分行级锁升级为表级锁,以减少内存占用和锁管理的复杂性。

2.2.3 避免死锁风险

1. 原理: 当数据库系统预测到当前的锁模式可能会导致死锁情况发生时,可能会进行锁升级。例如,在多个事务对同一表中的行进行交叉锁定,并且系统检测到死锁的可能性较高时,通过将部分行级锁升级为表级锁,可以改变锁的模式,避免死锁的发生。

2. 例: 假设有两个事务 T1 和 T2,T1 对表中的行 R1 和 R2 加了行级锁,T2 对行 R2 和 R3 加了行级锁,并且两个事务接下来可能会尝试获取对方已经锁定的行,系统检测到这种潜在的死锁情况,可能会将 T1 或 T2 对部分行的行级锁升级为表级锁,从而改变锁的获取顺序,避免死锁。

2.3 锁升级的影响

2.3.1 并发性能降低

1. 原理: 锁升级后,从细粒度的行级锁变为表级锁,会限制其他事务对整个表的访问。例如,原本多个事务可以同时对表中的不同行进行读取操作(基于行级共享锁的兼容性),但在锁升级为表级锁后,其他事务可能无法对表进行读取或写入操作,直到持有表级锁的事务完成,这会导致并发性能下降。

2. 例: 在一个多用户的在线购物系统中,有一个事务对订单表中的大部分行进行了操作,导致锁升级为表级锁。此时,其他用户想要查询自己的订单信息(原本只需要获取行级共享锁即可)就会被阻塞,直到这个事务完成,从而影响了系统的并发处理能力。

2.3.2 死锁可能性变化

1. 原理: 锁升级可能会改变锁的模式和持有情况,从而对死锁的可能性产生影响。一方面,如前面提到的,锁升级可能会避免一些潜在的死锁情况;另一方面,如果锁升级的策略不合理,也可能会导致新的死锁模式出现。

2. 例: 假设在一个数据库系统中,原本事务之间的行级锁竞争比较均衡,不会导致死锁。但由于锁升级策略导致某些事务频繁地获取表级锁,而其他事务又在等待这些表级锁的释放,可能会出现新的死锁场景,例如多个事务等待不同的表级锁,形成循环等待的情况。

3. 锁升级对数据库性能可能产生的影响

3.1 并发性能下降

3.1.1 行级锁到表级锁的转换

1. 原理: 锁升级过程中,从细粒度的行级锁转换为表级锁。在表级锁模式下,对整个表的并发访问受到限制。原本多个事务可以同时对表中的不同行进行操作,如在一个在线商城的数据库中,多个用户可以同时对商品表中的不同商品进行查询或购买操作(通过行级锁实现并发)。一旦锁升级为表级锁,这些并发操作就会被阻塞,因为其他事务不能访问被锁定的整个表,直到持有表级锁的事务完成。

2. 例: 假设有 10 个用户同时在商城中查询和购买不同商品,当一个事务对商品表进行大量操作导致锁升级后,这 10 个用户的操作都会被暂停等待,直到锁被释放,这会导致系统的响应时间变长,并发处理能力大幅下降。

3.1.2 共享锁与排他锁的相互影响

1. 原理: 在锁升级后,如果表级锁是排他锁,那么所有对该表的读取(共享锁)和写入(其他排他锁)操作都将被阻止。即使表级锁是共享锁,对于需要排他锁进行写入操作的事务也会被阻塞。这种相互影响会导致事务等待时间增加,系统的整体吞吐量降低。

2. 例: 在一个数据库中有一个报表生成事务需要对销售数据表进行读取操作(加共享锁),同时有一个数据更新事务需要对同一表进行写入操作(加排他锁)。如果因为某些原因锁升级为表级排他锁,那么报表生成事务就会被阻塞,无法进行读取操作,直到数据更新事务完成,这会降低系统的并发性能。

3.2 死锁风险增加(可能出现新的死锁模式)

3.2.1 锁模式改变导致的死锁

1. 原理: 锁升级可能会改变锁的持有模式和顺序。例如,原本事务之间通过行级锁可以按照一定的顺序访问数据,避免死锁。但锁升级后,表级锁的持有时间较长且范围广,可能会导致新的循环等待情况。当多个事务等待不同的表级锁释放时,就容易形成死锁。

2. 例: 假设有三个事务 T1、T2 和 T3,它们都需要访问表 A 和表 B。在没有锁升级时,它们通过行级锁可以有序地访问不同行的数据。但如果 T1 对表 A 进行了大量操作导致锁升级为表级锁,同时 T2 对表 B 进行操作也导致锁升级,T3 可能会同时等待 T1 释放表 A 的锁和 T2 释放表 B 的锁,T1 又可能在等待 T3 释放某个资源,从而形成死锁。

3.2.2 死锁检测和处理开销

1. 原理: 数据库系统需要不断地检测死锁情况。当死锁发生时,数据库要选择一个事务进行回滚来解除死锁,这个过程会消耗系统资源。随着锁升级导致死锁风险增加,死锁检测和处理的频率也会增加,从而占用更多的 CPU 时间和内存等资源。

2. 例: 在一个高并发的数据库系统中,如果频繁因为锁升级出现死锁,数据库会不断地执行死锁检测算法。这些算法需要遍历锁的信息、事务的等待链等,消耗大量的 CPU 资源。而且每次死锁处理时的回滚操作也会增加系统的负担,影响数据库的性能。

3.3 系统资源利用效率降低

3.3.1 内存资源

1. 原理: 在锁升级前,行级锁需要维护每行锁的相关信息,如锁的类型、持有者等。虽然单个行级锁的信息占用空间较小,但当行级锁数量众多时,也会占用一定的内存空间。然而,锁升级为表级锁后,数据库可能不会立即释放之前行级锁占用的内存空间,导致内存资源的浪费。同时,表级锁本身也需要占用一定的内存来记录锁的状态等信息。

2. 例: 一个数据库中有大量的小事务频繁地对一个大表进行行级锁操作,这些行级锁信息存储在内存的锁管理区域。当发生锁升级后,这些行级锁信息可能没有及时清理,并且还增加了表级锁的内存占用,使得内存中用于锁管理的区域膨胀,降低了内存资源的有效利用率。

3.3.2 CPU 资源

1. 原理: 锁升级过程本身需要消耗 CPU 资源来进行判断和转换操作。此外,升级后的表级锁可能会导致更多的事务等待,这些等待事务会不断地检查锁是否释放,这也会消耗 CPU 资源。同时,如前面提到的死锁检测等操作也会增加 CPU 的开销。

2. 例: 在一个数据库服务器中,当锁升级频繁发生时,CPU 需要花费时间来处理锁升级的逻辑,如判断是否满足升级条件、进行锁模式的转换等。而且大量被阻塞的事务会不断地轮询锁的状态,使得 CPU 的使用率增加,但实际有效的工作减少,导致系统资源利用效率降低。

4. 如何监控数据库中的锁升级情况

4.1 利用数据库系统的动态视图或系统表

4.1.1 SQL Server

1. sys.dm_tran_locks 视图: 这个视图提供了有关当前在数据库中活动的锁的详细信息。可以通过查询这个视图来观察锁的级别(行级、表级等)以及锁的状态变化,以判断是否发生了锁升级。例如,通过检查request_mode(锁模式)、resource_type(资源类型,如行或表)和request_status(请求状态)等列,可以了解锁的详细情况。
例:

SELECT request_session_id, resource_type, request_mode, request_status
FROM sys.dm_tran_locks;
  1. SQL Server 扩展事件(Extended Events): 可以使用扩展事件来捕获锁升级事件。通过创建一个扩展事件会话,定义一个事件来捕获lock_escalation事件,就可以收集锁升级相关的详细信息,包括发生锁升级的会话、对象和时间等。
    例(创建扩展事件会话):
-- 创建扩展事件会话
CREATE EVENT SESSION [LockEscalationMonitoring] ON SERVER
ADD EVENT sqlserver.lock_escalation
ADD TARGET package0.event_file(SET filename = N'C:\temp\LockEscalation.xel')
WITH (MAX_MEMORY = 4096KB, EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS, MAX_DISPATCH_LATENCY = 30 SECONDS, MAX_EVENT_SIZE = 0KB, MEMORY_PARTITION_MODE = NONE, TRACK_CAUSALITY = OFF, STARTUP_STATE = OFF);
-- 启动会话
ALTER EVENT SESSION [LockEscalationMonitoring] ON SERVER STATE = START;

4.1.2 Oracle

  1. V L O C K 视 图 : 类 似 于 S Q L S e r v e r 的 动 态 视 图 , V LOCK视图: 类似于SQL Server的动态视图,V LOCK视图:类似于SQLServer的动态视图,VLOCK视图用于查看数据库中的锁信息。可以通过检查锁的类型(如TM代表表级锁,TX代表事务锁)和相关的事务信息来判断是否有锁升级的情况。例如,如果看到大量原本应该是行级锁相关的事务(TX类型)突然被表级锁(TM` 类型)所取代,可能表示发生了锁升级。
    例:
SELECT SID, TYPE, LMODE, ID1, ID2
FROM V$LOCK;

2. Oracle Trace 功能: 可以启用 Oracle 的跟踪功能(如 SQL Trace 或事件 10046 跟踪)来捕获详细的数据库操作信息,包括锁的获取、释放和升级情况。通过分析跟踪文件,可以确定锁升级发生的时间、涉及的事务和 SQL 语句等信息。

4.1.3 MySQL

1. Performance Schema: MySQL 的 Performance Schema 提供了很多关于数据库性能的信息,包括锁相关的数据。可以通过查询performance_schema.data_locks表来查看锁的详细信息。通过观察锁的范围(是行级还是表级)以及锁的持有时间等参数,来判断是否发生了锁升级。
例:
SELECT * FROM performance_schema.data_locks;

2. SHOW ENGINE INNODB STATUS 命令: 这个命令可以显示 InnoDB 存储引擎的状态信息,其中包含了关于锁的信息。虽然信息相对较简略,但可以快速查看是否有表级锁等待等可能与锁升级相关的情况。例如,查看TRANSACTIONS部分中关于锁等待的描述。

4.2 使用数据库管理工具

4.2.1 SQL Server Management Studio(SSMS)

1. 活动监视器(Activity Monitor): 在 SSMS 中,可以通过活动监视器来查看当前的数据库活动,包括锁相关的信息。它以图形化的方式展示了不同类型的锁以及持有锁的进程等内容。可以通过观察锁的数量和类型变化来发现锁升级的迹象。例如,如果看到表级锁的数量突然增加,而相应的行级锁数量减少,可能是发生了锁升级。

4.2.2 Oracle Enterprise Manager(OEM)

1. 性能中心(Performance Hub): OEM 提供了性能中心来监控数据库的各种性能指标。在这里,可以查看锁的活动情况,包括锁等待时间、锁的类型分布等。通过设置警报,可以在出现可能与锁升级相关的异常情况(如长时间的表级锁等待)时收到通知。

4.2.3 MySQL Workbench

1. 性能仪表板(Performance Dashboard): MySQL Workbench 的性能仪表板可以显示一些关于锁的基本信息。虽然它没有像前两者那样丰富的功能,但可以查看诸如当前锁等待的数量等信息,帮助初步判断是否有锁升级相关的问题。同时,可以通过执行自定义查询来深入挖掘 Performance Schema 中的锁信息。

5. 如何避免锁升级问题

5.1 优化事务设计

5.1.1 缩小事务范围

1. 原理: 减少事务中操作的数据量,可降低锁升级的触发几率。如果事务只涉及少量行的操作,数据库就不太可能因为大量行级锁而触发升级。

2. 例: 在一个库存管理系统中,不要在一个事务里更新所有商品的库存,而是将更新操作拆分成多个小事务,每次只更新一种或几种商品的库存。比如,将更新 1000 种商品库存的事务,拆分成 100 个每次更新 10 种商品库存的事务。

5.1.2 合理安排事务操作顺序

1. 原理: 避免事务之间出现交叉锁定相同数据的情况,降低死锁风险,从而减少因死锁预防而导致的锁升级。通过按照相同的顺序访问表和行,可以使锁的获取更加有序。

2. 例: 假设有两个事务,事务 A 和事务 B,都需要操作表 X 和表 Y 中的数据。如果事务 A 总是先操作表 X 再操作表 Y,事务 B 也遵循同样的顺序,就可以减少因相互等待对方释放锁而导致锁升级的情况。

5.2 调整数据库参数

5.2.1 调整锁升级阈值

1. 原理: 不同数据库系统有参数来控制锁升级的阈值。通过适当提高这个阈值,可以让数据库容忍更多的行级锁,减少不必要的锁升级。

2. 例: 在 SQL Server 数据库中,可以通过配置MAXDOP(最大并行度)和LOCK_ESCALATION等参数来控制锁升级。例如,将LOCK_ESCALATION设置为DISABLE可以完全禁止锁升级,但这需要谨慎使用,因为可能会增加系统资源的消耗。在 Oracle 数据库中,可以通过调整一些隐藏参数来影响锁升级行为,但这需要对数据库有深入的了解并且在测试环境充分验证后才能使用。

5.3 优化查询和索引使用

5.3.1 使用合适的索引

1. 原理: 索引可以加快数据访问速度,使事务能够更快地获取和释放行级锁。当查询和更新操作能够通过索引高效地定位数据时,就不需要对大量行进行扫描和加锁。

2. 例: 在一个员工信息数据库中,如果经常通过员工编号查询和更新员工工资,那么在员工编号列上建立索引。这样,当执行更新工资的事务时,如UPDATE employees SET salary = new_salary WHERE employee_id = 123,数据库可以通过索引快速定位到要更新的行,减少对其他行的不必要扫描和加锁,从而降低锁升级的可能性。

5.3.2 避免全表扫描

1. 原理: 全表扫描会导致数据库对表中的大量行进行访问和可能的加锁。通过优化查询,使用索引或者限制查询范围,避免全表扫描,可以减少行级锁的数量。

2. 例: 如果有一个查询SELECT * FROM orders WHERE order_date BETWEEN ‘2024-01-01’ AND ‘2024-02-01’,确保在order_date列上有索引,并且查询条件的范围是合理的,避免写成SELECT * FROM orders这样的全表扫描查询,从而减少锁升级的风险。

5.4 采用替代的并发控制机制(如果适用)

5.4.1 使用乐观并发控制(OCC)

1. 原理: 乐观并发控制假设事务在执行过程中很少发生冲突。在这种机制下,事务在读取数据时不会加锁,只有在提交时才会检查数据是否被其他事务修改。如果没有冲突,事务提交成功;如果有冲突,则回滚并重新执行。

2. 例: 在一个多人协作的文档编辑系统中,多个用户可以同时编辑文档的不同部分。采用乐观并发控制,用户在编辑过程中可以自由操作,当提交修改时,系统检查文档是否被其他用户修改。如果没有修改,提交成功;如果有修改,则可以提示用户合并修改或者重新编辑,这样就避免了因锁而导致的升级问题。

5.4.2 使用版本号或时间戳

1. 原理: 为每行数据添加版本号或时间戳,事务在更新数据时,通过比较版本号或时间戳来判断数据是否被其他事务修改。如果数据未被修改,更新操作成功;如果数据已经被修改,事务可以根据业务规则决定是放弃更新还是重新获取最新数据后再更新。

2. 例: 在一个产品信息管理系统中,为每个产品记录添加一个版本号。当一个事务想要更新产品价格时,它首先读取产品的版本号,在提交更新时,再次检查版本号是否改变。如果版本号不变,说明没有其他事务修改该产品信息,更新成功;如果版本号改变,事务可以重新获取最新的产品信息和版本号,然后决定是否继续更新。这种方式可以在一定程度上避免锁的使用和升级。

6. 后记

锁问题一直是事务处理和高并发编程的核心问题,大家一定要搞透彻明白才能写出高效的代码。

码字不易,宝贵经验分享不易,请各位支持原创,转载注明出处,多多关注作者,后续不定期分享DB基本知识和排障案例及经验、性能调优等。

标签:问题,事务,行级,数据库,深入探讨,升级,死锁,表级
From: https://blog.csdn.net/qq_45732829/article/details/144114870

相关文章

  • win10图片预览图无法查看,资源管理器卡死奔溃问题排查
    问题最近在打开文件夹时,发现部分图片无法查看缩略图,且异常卡顿,资源管理器上方绿色加载条一直在加载中,另外只要在任意图片上右键,然后移动到打开更多(H)上,就能稳定使资源管理器卡死崩溃问题排查只有图片文件有问题,且卡死时资源占用没有太大变化,可能是软件的问题因为电脑里没有......
  • odoo18运行报错问题解决
    File"/Users/melon/.pyenv/versions/3.11.9/lib/python3.11/code.py",line90,inruncodeexec(code,self.locals)File"<input>",line1,in<module>File"/Applications/PyCharm.app/Contents/plugins/python/helpers/p......
  • 火车驶入驶出顺序验证问题 | 栈
    问题描述小F在观察火车驶入和驶出休息区的顺序时,注意到休息区的结构类似于栈,即遵循先进后出的规则。她记录了火车驶入和驶出的顺序,并希望验证这些顺序是否可能实际发生。火车在进入休息区后可以按顺序驶入或停留,然后根据休息区的规则依次驶出。你的任务是帮助小F验证所记录的......
  • wsl中用qemu运行linux kernel ,ping github.com有问题
    我在qemu上运行我编译的linuxkernel,并尝试在上面用wget安装unixbench(宿主机是wsl)pinggithub.com会失败但是我的dns是可以将github.com解析成功的我尝试ping对应的ip地址,发现是可以的我的/etc的所有文件列在下面了,我现在不知道这究竟是什么原因(猜测可能是wsl的网......
  • 巧克力板选择问题
    问题描述小M在春游时打算携带尽可能多的巧克力板。她拥有n块巧克力板,每块巧克力的边长为ai​,重量为ai2​。小M有多个不同大小的背包,每个背包都有一定的最大承重限制。她希望你帮助她计算在每个背包的最大承重范围内,最多可以带走多少块巧克力板。例如:小M有5块巧克力板,边长分别......
  • Javascript遍历目录时使用for..in循环无法获取Files对象和SubFolders对象问题的解决方
      1Javascript遍历目录时使用for..in循环无法获取Files对象和SubFolders对象1.1问题场景  在JavaScript中遍历目录,使用for..in循环时,无法获取到Files对象和SubFolders对象,导致无法遍历目录和子目录。  代码如下:functionGetAllFilesInSubFolder(path,filter)......
  • pagehelper常见问题【分页失效】【ThreadLocal污染线程】
    ......
  • 当网站遇到性能问题时,你是如何一步步解决的?说说你的方法
    当网站遇到性能问题时,我会按照以下步骤进行分析和解决,并迭代这个过程直到性能达到预期:1.问题界定和测量:明确性能瓶颈的具体表现:网站加载慢?动画卡顿?特定操作响应迟缓?用户感受到的卡顿是什么样的?量化性能指标:使用性能分析工具(例如ChromeDevTools,Lighthouse,WebP......
  • 使用递归时应该注意哪些问题?
    在前端开发中使用递归时,需要注意以下几个问题:栈溢出(StackOverflow):这是递归最常见的问题。递归调用会占用调用栈的空间,如果递归深度过大,调用栈空间会被耗尽,导致栈溢出错误。这通常发生在递归的终止条件没有正确设置或数据量过大的情况下。解决方案:设置合适的终止条......
  • 如何快速定位线上出现的问题?
    快速定位线上前端问题需要结合多种方法和工具,以下是一些常用的技巧:1.复现问题:收集用户信息:尝试获取尽可能多的用户信息,例如:浏览器版本、操作系统、网络环境、用户操作步骤、错误截图、屏幕录制等。用户提供的具体操作步骤至关重要。利用开发者工具:浏览器内置的开发者......