讲解更新锁(U)与排它锁(X)的相关知识

一直没有认真了解UPDATE操作的锁,最近在MSDN论坛上看到一个问题,询问堆表更新的死锁问题,问题很简单,有类似这样的表及数据:

CREATE TABLE dbo.tb(     c1 int,     c2 char(10),     c3 varchar(10));GODECLARE @id int;SET @id = 0;WHILE @id <5BEGIN;     SET @id = @id + 1;     INSERT dbo.tb VALUES( @id, 'b' + RIGHT(10000 + @id, 4), 'c' + RIGHT(100000 + @id, 4) );END;

在查询一中执行更新操作:

BEGIN TRANUPDATE dbo.tb SET c2 = 'xx' WHERE c1 = 2;WAITFOR DELAY '00:00:30';UPDATE dbo.tb SET c2 = 'xx' WHERE c1 = 5;ROLLBACK;

在查询一执行开始后,马上在查询二中执行下面的操作

BEGIN TRANUPDATE dbo.tb SET c2 = 'xx' WHERE c1 = 1;ROLLBACK;

为什么会出现死锁,如果条件改为 c1 = 4 则不会死锁。

开始的时候想得比较简单,死锁的表现是形成循环等待(对于两个查询而言,可以简单地认为就是在相互等待对方锁定资源的释放)。

对于这个例子而言,第一个查询更新两次,会先更新并锁定一条记录,然后等待第二个更新;但第二个查询只会更新一条记录,它要么与第一个查询冲突,无法获得锁,需要等待查询一完成,这个时候它并没有锁定什么;要么能够获得锁,完成更新。似乎不应该会出现死锁,死锁会不会是其他原因导致。

在自己的电脑上简单测试了一下,似乎也确实没有死锁。

但后面通过Profile跟踪更新操作的下锁情况才发现,自己的分析大错特错了。主要原因在于没有正确理解更新操作是如何用锁的。

在联机帮助上“锁模式”中有关于更新的U(更新锁)和X(排它锁)的说明

http://msdn.microsoft.com/zh-cn/library/ms175519(v=sql.105).aspx

不过说得确实挺模糊的,里面还提到了S锁,我一直以为是查询数据过程中用的S锁(也 SELECT 一样),找到满足条件的记录后用U锁,再转换为X锁做更新。

    Profile(事件探查器)跟踪的结果让我知道了这是一个错误的理解,在Profile中新建一个跟踪,选择Locks中的Lock:Acquired(加锁),Lock:Acquired(释放锁)解两个事件,在筛选中设置只跟踪测试用的查询窗口对应的spid(可以执行 PRINT @@SPID获得),然后执行一个更新语句,比如 UPDATE dbo.tb SET c2 = ‘xx’ WHERE c1 = 3

在Profile中可以看到,对于每条记录都有加 U 锁的操作,对于不满足条件的记录,会马上释放U锁;对于满足条件的记录,最终转换为X锁。如下图所示。

讲解更新锁(U)与排它锁(X)的相关知识

注意一下,在这个跟踪结果里面,并没有出现S锁。

另外学做了一些测试:

通过加大记录量做更新测试,会发现数据扫描涉及的记录都有U锁,并不限于更新记录所在的页。这从另一个角度说明了大表中Scan 可怕。

当使用索引Scan的时候,也会通过跟踪发现所Scan的索引资源有U锁,如果更新不涉及索引变化,那以只会对应的记录有U转X锁,索引的U锁会释放;如果影响索引,那么索引的U锁会转X锁。

删除操作与更新操作类似

使用 UPDATE aSET c2 = ‘xx’ FROM dbo.tb AS a WITH(NOLOCK) WHERE c1 = 3  的加锁情况是一样的, 并不会因为NOLOCK的提示而不加 U 或者 X 锁

最后回头研究一下示例中的死锁问题:

对于查询一,第一个更新依次扫描表中所有记录,对于每条记录,加 U 锁,判断是否符合更新条件,如果符合,转换为 X 锁;如果不符合条件,释放 U 锁。第一个更新完成的时候,查询一锁定了一条记录(由于事务未完成,所以锁一直保持),然后等待第二个更新

对于查询二,依次扫描表中的每条记录(与前面的更新一样),如果它更新的记录在查询一更新的记录前被扫描到,那么这条记录也会变成 X 锁;当继续并进行到查询一的X锁记录的零点,U 与 X 冲突,无法继续,这时候查询二等待查询一释放锁

查询一的第二个更新开始执行,依次扫描每条记录,同一个事务内不会有冲突,所以它不会与自己之前锁定的记录有冲突,但进行到查询二锁定的记录的时候,它也无法获得 U 锁,它需要等待查询二释放资源。这个时候就形成了相互等待,符合死锁条件

如果查询二需要更新的记录在查询一的第一个更新记录之后,则不会有死锁,因为查询二在扫描到查询一第一个更新的记录时就会因为锁冲突等待了,这个时候它没有对任何记录设置与查询一的操作有冲突的锁。我自己测试的时候没有死锁,就是这种情况。

注意这里面提到的顺序,是数据读取的顺序,不一定与存储顺序一样,磁盘上记录的顺序也不一定与INSERT的记录顺序一样,这也是我用同样条件没有测试出死锁的原因(我的环境中,恰好读出的顺序与INSERT的顺序不一样)

更新时,记录读取的顺序,可以通过Profile跟踪的Lock:Acquired (加锁)事件来看,涉及大量数据时,如果服务器支持,还会有并发读取。这也是分析死锁时要考虑的因素

本文讲解了讲解更新锁(U)与排它锁(X)的相关知识,更多相关内容请关注创想鸟。

相关推荐:

SQL Server 2008 处理隐式数据类型转换在执行计划中的增强

如何让MySQL中单句实现无限层次父子关系查询

带进度的SQL Server FileStream如何存取

知我AI 知我AI

一款多端AI知识助理,通过一键生成播客/视频/文档/网页文章摘要、思维导图,提高个人知识获取效率;自动存储知识,通过与知识库聊天,提高知识利用效率。

知我AI 26 查看详情 知我AI



以上就是讲解更新锁(U)与排它锁(X)的相关知识的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/383626.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月6日 12:31:58
下一篇 2025年11月6日 12:33:45

相关推荐

  • 如何处理C++中的”deadlock”线程阻塞错误?

    死锁的解决方法包括统一资源请求顺序、使用智能锁管理资源、避免持有并等待及检测调试死锁。具体措施为:1. 定义统一加锁顺序,避免循环等待;2. 使用 std::lock() 同时加多个锁,避免中间状态;3. 采用 std::lock_guard 或 std::unique_lock 自动管理锁生命周期…

    2025年12月18日 好文分享
    000
  • 什么是C++中的死锁?

    在c++++编程中,死锁是指两个或多个线程彼此等待对方释放资源,导致所有线程无法继续执行。死锁可以通过以下策略避免:1. 锁的顺序一致性,确保所有线程以相同顺序获取锁;2. 避免长时间持有锁,尽量减少锁的持有时间;3. 使用std::lock函数,同时尝试获取多个锁;4. 死锁检测和恢复,使用算法识…

    2025年12月18日
    000
  • 如何解决多线程编程中的死锁问题

    解决多线程编程中的死锁问题的方法包括:1. 避免共享资源;2. 使用死锁检测和恢复算法;3. 使用优先级继承;4. 使用超时。 如何解决多线程编程中的死锁问题 死锁是多线程编程中一种常见的问题,当多个线程同时等待对方释放资源时就会发生。这会导致所有线程都无限期地等待,无法继续执行。 解决死锁问题的常…

    2025年12月18日
    000
  • C++ 函数并发编程中的死锁预防和检测方法?

    C++ 函数并发编程中死锁预防和检测方法 在并发编程中,死锁是一种常见的陷阱,它会导致程序停滞。死锁发生在两个或多个任务等待彼此释放资源的情况下,从而形成循环依赖。 预防死锁 要预防死锁,可以采取以下措施: 避免环形等待:确保任务不会等待其他任务释放它们已经持有的资源。使用死锁检测机制:定期检查是否…

    2025年12月18日
    000
  • 如何在C++框架中避免死锁和性能下降?

    针对多线程 c++++ 框架中的死锁和性能问题,策略包括:处理死锁:避免循环等待、使用锁顺序、使用死锁检测;优化性能:最小化锁定时间、使用无锁数据结构、优化线程池。 如何在 C++ 框架中规避死锁和性能问题 在多线程 C++ 框架中,死锁和性能下降是常见的挑战。本文将提供策略和最佳实践来解决这些问题…

    2025年12月18日
    000
  • C++ 框架中的事件处理死锁问题分析与解决

    c++++ 框架中的事件处理死锁是由事件处理程序持有互斥锁时处理事件或发送消息引起的。解决方法有:只在事件处理程序中处理事件、在事件处理程序之外发送消息以及使用无锁数据结构。遵循这些最佳实践可以确保 c++ 框架中的应用程序可靠响应事件。 C++ 框架中的事件处理死锁问题分析与解决 简介 事件处理是…

    2025年12月18日
    000
  • 如何调试 C++ 程序中的死锁?

    死锁是一种并发编程中的常见错误,发生在多个线程等待彼此持有的锁时。可以通过使用调试器检测死锁,分析线程活动并识别涉及的线程和锁,从而解决死锁。解决死锁的方法包括避免循环依赖、使用死锁检测器和使用超时。在实践中,通过确保线程按相同的顺序获取锁或使用递归锁或条件变量可以避免死锁。 如何调试 C++ 程序…

    2025年12月18日
    000
  • C++ 多线程编程中死锁预防和检测机制

    多线程死锁预防机制包括:1. 锁顺序;2. 测试并设置。检测机制包括:1. 超时;2. 死锁检测器。文章举例共享银行账户,通过锁顺序避免死锁,为转账函数先请求转出账户再请求转入账户的锁。 C++ 多线程编程中的死锁预防和检测机制 在多线程环境中,死锁是一个常见的错误,可能导致程序停止响应。死锁发生在…

    2025年12月18日
    000
  • 如何解决 C++ 多线程编程中常见的死锁问题?

    如何解决 c++++ 多线程编程中的常见死锁问题?避免死锁的技术:加锁顺序:始终以相同的顺序获取锁。死锁检测:使用算法检测并解决死锁。超时:为锁设置超时值,防止线程无限期等待。优先级反转:分配不同的优先级,减少死锁可能性。 如何解决 C++ 多线程编程中的常见死锁问题 死锁概述 死锁是一种编程错误,…

    2025年12月18日
    000
  • 如何在 C++ 中处理并发编程中的死锁和饥饿问题?

    死锁:有序化资源和死锁检测;饥饿:优先级调度和公平锁。通过这些策略,可以在 c++++ 中解决死锁和饥饿问题,确保可靠性和效率。 如何在 C++ 中解决并发编程中的死锁和饥饿问题 并发编程经常会遇到两个常见的挑战:死锁和饥饿。解决这些问题对于确保应用程序的可靠性和效率至关重要。 死锁 死锁是指两个或…

    2025年12月18日
    000
  • C++ 函数如何解决并发编程中的死锁问题?

    在 c++++ 中,使用互斥量函数可以解决多线程并发编程中的死锁问题。具体步骤如下:创建一个互斥量;当线程需要访问共享变量时,获得互斥量;修改共享变量;释放互斥量。这样可以确保任何时刻只有一个线程访问共享变量,有效防止死锁。 利用 C++ 函数解决并发编程中的死锁问题 在多线程并行编程中,死锁是一种…

    2025年12月18日
    000
  • Golang并发编程中死锁识别与解决技巧

    死锁是因goroutine间循环等待资源导致的程序停滞,需通过统一加锁顺序、使用带缓冲通道或select超时机制来预防,结合go vet和堆栈分析定位问题。 Golang并发编程中的死锁,本质上是多个goroutine因争夺资源而相互等待,最终导致程序停滞。识别和解决这类问题,关键在于理解死锁发生的…

    2025年12月15日
    000
  • Golang并发编程中常见的死锁(deadlock)原因和避免策略

    死锁是多个goroutine因互相等待资源而无限阻塞的现象,常见于互斥锁的AB-BA循环等待或Channel无缓冲单向通信。解决方法包括统一加锁顺序、使用带缓冲Channel、select结合超时或default分支、确保WaitGroup的Add在Wait前完成,并避免sync.Once中初始化函…

    2025年12月15日
    000
  • Golang的并发编程有哪些常见陷阱 总结死锁和竞态条件的排查方法

    golang并发编程常见陷阱包括goroutine泄露、channel阻塞、竞态条件和死锁。1. goroutine泄露:因未正确退出机制导致goroutine永久阻塞,应使用context或select超时控制,并借助pprof分析排查;2. channel使用不当:无接收者或发送者的channe…

    2025年12月15日 好文分享
    000
  • Golang程序卡死怎么排查?Golang死锁问题分析与解决

    golang程序卡死的常见原因包括死锁、资源耗尽、无限循环、阻塞操作等。排查方法主要有:1. 使用pprof工具分析cpu、内存、goroutine状态;2. 通过godebug=scheddetail=1获取调度信息;3. 添加日志辅助定位问题;4. 审查涉及锁、channel、goroutine…

    2025年12月15日 好文分享
    000
  • PHP 函数并发编程的死锁问题

    摘要:php 中的函数并发编程可能会遇到死锁问题,即多个协程互相等待对方的锁,导致所有协程无法继续执行。解决方案之一是使用锁的顺序,即指定协程获取锁的固定顺序,以避免死锁。其他注意事项还包括避免长时间占有锁、避免使用嵌套锁和考虑使用无锁数据结构。 PHP 函数并发编程的死锁问题 简介 在 PHP 中…

    2025年12月9日
    000
  • Java中死锁如何避免 分析死锁产生的四个必要条件

    预防死锁最有效的方法是破坏死锁产生的四个必要条件中的一个或多个。死锁的四个必要条件分别是互斥、占有且等待、不可剥夺和循环等待;其中,互斥通常无法破坏,但可以减少使用;占有且等待可通过一次性申请所有资源来打破;不可剥夺可通过允许资源被剥夺打破;循环等待可通过按序申请资源解决。此外,reentrantl…

    2025年12月5日 java
    400
  • 热游情报:排面拉满!《黑神话》专访央视国庆节播出 《NIKKE》目标运营20年!

    大家周末好,我是x博士。 将由“新游动态”“全球热门”两个部分组成。 一句两句无法说清楚,还是跟X博士一起这周有哪些热点资讯吧。 划重点:排面!《黑神话》冯骥专访国庆当天在CCTV4播出 在10月1日国庆节当天,CCTV4的中国新闻栏目播出了《黑神话:悟空》制作人冯骥的专访,让玩家直呼“排面拉满”。…

    2025年12月2日 行业动态
    000
  • mysql死锁是怎么造成的

    造成%ignore_a_1%死锁的原因:两个或两个以上的进程在执行过程中争夺资源。mysql中有三种锁,分别是表级锁、行级锁和页面锁。其中,表级锁开销小、加锁快,不会出现死锁情况。 mysql是一种关系型数据库管理系统,使用的 SQL 语言是用于访问数据库的最常用标准化语言。在mysql的使用过程中…

    2025年12月2日 数据库
    000
  • mysql死锁是什么意思

    mysql死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环。InnoDB存储引擎能检测到死锁的循环依赖并立即返回一个错误。只有部分或完全回滚其中一个事务,才能打破死锁。 什么是死锁?它是如何产生的? 死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方占用…

    2025年12月2日 数据库
    000

发表回复

登录后才能评论
关注微信