最近有一个朋友问我一个关于给查询操作强制上X锁却不阻塞的问题。该查询写在一个存储过程中,代码如代码1所示:

   1: create PROC [dbo].[GetCityOrders]

   2:     @city NVARCHAR(10) ,

   3:     @num INT

   4: AS

   5:     SET NOCOUNT ON

   6:  

   7:     BEGIN TRY

   8:  

   9:         BEGIN TRAN

  10:  

  11:         SELECT TOP ( @num )

  12:                 id ,

  13:                 number ,

  14:                 price ,

  15:                 mid ,

  16:                 @city city

  17:         INTO    #cityorders

  18:         FROM    cmcc WITH ( XLOCK )

  19:         WHERE   prov = 0

  20:                 AND status = 0

  21:                 AND city = @city

  22:  

  23:         UPDATE  cmcc

  24:         SET     status = 100

  25:         WHERE   id IN ( SELECT  id

  26:                         FROM    #cityorders )

  27:  

  28:         SELECT  o.* ,

  29:                 c.attach

  30:         FROM    #cityorders o

  31:                 LEFT JOIN cmcc_attach c ON o.id = c.id

  32:  

  33:         DROP TABLE #cityorders

  34:  

  35:         COMMIT TRAN

  36:  

  37:     END TRY

  38:     BEGIN CATCH

  39:  

  40:         ROLLBACK

  41:  

  42:     END CATCH

代码1.

 

    该存储过程首先通过对查询操作加X锁,使得其他读取操作更新时不影响该部分加X锁的操作。乍一看没有任何问题,但是当业务上线后就发现,即使查询有了X锁,但实际上还是会有多个调用该存储过程的客户端同时读取到同一条数据的现象现象。

 

原因?

    为了验证原因,我们来做一个Demo测试,首先我们创建测试表,代码如代码2所示。

   1: CREATE TABLE dbo.DemoX

   2:     (

   3:       [key] INT PRIMARY KEY ,

   4:       [value] INT,

   5:     );

   6: GO

   7: INSERT  INTO dbo.DemoX

   8:         ( [key], value )

   9: VALUES  ( 1, 100 );

  10: GO

代码2.创建测试DEMO

 

    接下来,对该DemoX表进行Select操作,并查看锁。如代码3所示。

   1: BEGIN TRAN

   2: SELECT  [key],value

   3: FROM    dbo.DemoX D WITH (XLOCK);

   4:  

   5: SELECT  L.resource_type,

   6:         L.request_mode,

   7:         L.request_status,

   8:         L.resource_description,

   9:         L.resource_associated_entity_id

  10: FROM    sys.dm_tran_current_transaction T

  11: JOIN    sys.dm_tran_locks L

  12:         ON  L.request_owner_id = T.transaction_id;

代码3.使用X锁提示查语句

 

    在代码3中显式指定了X锁,并查看上锁情况,可以看出X锁以及对应父对象上的意向锁都正常存在,如图1所示。

图1.

 

      我们再开另外一个窗口运行一个普通的Select,结果如图2所示。

图2.

 

为什么没有阻塞

    理论上来说,第二个查询应该会被阻塞,因为第二个查询所需加的S锁和第一个查询的X锁不兼容。后来在网上找打StackOverFlow的一篇博文:“http://stackoverflow.com/questions/4609217/sql-server-the-misleading-xlock-optimizations”,找到了答案。

    在SQL Server中,默认的已提交读为了保证不读脏数据(既在内存中修改,还未落盘的数据),会对需要查找的数据上S锁,但如果发现数据并不是脏数据,则会优化跳过加S锁的步骤,代码3中的查询语句强制使用了X锁提示,但未进行任何数据修改,所以不存在脏数据,因此后续查询就通过优化放弃使用S锁,从而不阻塞,导致了意料之外的结果。

 

解决办法

   SQL Server对于该特性的优化仅仅对行锁生效,如果在指定查询时使用页锁提示,则会按照语句,对阻塞后续查询,代码如代码4所示。

   1: SELECT  [key],value

   2: FROM    dbo.DemoX D WITH (PAGLOCK,XLOCK);

代码4.

    但显而易见,该方法会降低并发,如果有可能,请不要对Select操作使用X锁提示,否则请加上页锁提示。

    另一个办法是使用CTE进行表更新,将代码1中的代码两部分合二为一,数据在更新时会导致脏数据,因此不会出现跳过S锁的情况。

一次意外的X锁不阻塞问题的更多相关文章

  1. PHP自带Session隐患(session文件独占锁引起阻塞)

    PHP自带Session隐患(session文件独占锁引起阻塞) PHP默认的会话处理器是session.save_handler = files(即文件).如果同一个客户端同时并发发送多个请求(如a ...

  2. 第十六章——处理锁、阻塞和死锁(3)——使用SQLServer Profiler侦测死锁

    原文:第十六章--处理锁.阻塞和死锁(3)--使用SQLServer Profiler侦测死锁 前言: 作为DBA,可能经常会遇到有同事或者客户反映经常发生死锁,影响了系统的使用.此时,你需要尽快侦测 ...

  3. 使用Interlocked在多线程下进行原子操作,无锁无阻塞的实现线程运行状态判断

    巧妙地使用Interlocked的各个方法,再无锁无阻塞的情况下判断出所有线程的运行完成状态. 昨晚耐着性子看完了clr via c#的第29章<<基元线程同步构造>>,尽管这 ...

  4. erl_0017 《硝烟中的erlang》 读书笔记004 “锁和阻塞”

    如果某个进程需要持续地接收新任务,那么其在执行耗时过长的锁或者阻塞操作时,就会出现问题. 最为常见的例子之一就是:某个进程使用了TCP socket,阻塞在了接收新的连接或者等待消息上面.在执行此类阻 ...

  5. 一种在获取互斥锁陷入阻塞时可以被中断的 lock

    经过上篇的实例 线程在陷入阻塞时,在sychronized获取互斥锁陷入阻塞时,我们是无法进行中断的,javase5中提供了一种解决的办法 ReentrantLock ,我们常常用到的是它的lock( ...

  6. sqlserver 锁与阻塞

    DDL/索引重建 会申请 Sch-M锁 with (nolock) 会申请 Sch-S锁 with (nolock)会阻塞 sch-M, 同样Sch-M也会 阻塞with (nolock) 索引重建2 ...

  7. Sql Server 优化----SQL语句的执行方式与锁以及阻塞的关系

    阻塞原因之一是不同的Session在访问同一张表的时候因为不兼容锁的原因造成的, 当前执行的SQL语句是否被阻塞(或者死锁),不仅跟当前表上的已有的锁有关,也会跟当前执行的SQL语句的执行方式有关 简 ...

  8. Oracle中的阻塞锁SQL(阻塞在哪个数据上)

        SELECT (   '节点 '           || a.inst_id           || ' session '           || a.sid           || ...

  9. mysql5.6创建索引导致锁表阻塞查询

    结论:添加索引时,若果有对该表的慢查询,会导致索引添加延时等待   添加索引语句:alter table tb_name add index idx_xx(col_name);   执行添加索引的SQ ...

随机推荐

  1. Websocket Component

    As of Camel 2.10, the Websocket component supports SSL/TLS configuration through the Camel JSSE Conf ...

  2. <Android>文件下载

    使用HTTP协议下载文件 创建一个URL对象 通过URL对象,创建一个HttpURLConnection对象 调用getInputStream()方法得到InputStream对象 从InputStr ...

  3. Ubuntu12.04下编译OpenCv2.4.9程序

    引用地址http://blog.163.com/huai_jing@126/blog/static/171861983201311103411229/ 方法1:直接命令编译: g++ main.cpp ...

  4. Core Audio(二)

    用户模式音频组件 在windows vista中,core audio apis充当用户模式音频子系统的基础,core audio apis作为用户模式系统组件的一个thin layer,它用来将用户 ...

  5. dom4j解析xml的增加信息

    想要在xml中增加信息,那么就要先找到你要加信息的节点 前三行是固定模式,主要是找到xml文件的地址,并且得到根节点,再从根节点中便利出来movie的所有节点之后用集合接收 SAXReader rea ...

  6. JDK和环境配置

    1. JASE : J2SE 这个就是我们现在在学的东西,他是一切Java的核心基础 JAME :J2ME : 他是Java的一个微型版,主要用来做移动开发 JAEE :J2EE Java企业版本,主 ...

  7. Windows下Oracle安装图解----oracle-win-64-11g 详细安装步骤

    一. Oracle 下载 官方下地址 http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.htm ...

  8. 关于 REST

    REST(Representational State Transfer)是一种轻量级的 Web Service 架构风格,可以翻译成“表述性状态转移”,实现和操作明显比 SOAP 和 XML-RPC ...

  9. TableLayout表格布局详解

    一.Tablelayout简介 Tablelayout类以行和列的形式对控件进行管理,每一行为一个TableRow对象,或一个View控件.当为TableRow对象时,可在TableRow下添加子控件 ...

  10. 利用IIS应用请求转发ARR实现IIS和tomcat整合共用80端口

    现在网上流传的实现iis和tomcat共享80端口的方法是基于isapi_redirect插件实现的, 我的实现方法不同, 原理相似,具有更好的优点. 先说下基于isapi_redirect缺点,ja ...