Sql Server之旅——终点站 nolock引发的三级事件的一些思考

曾今有件事情让我记忆犹新,那年刚来携程不久,马上就被安排写一个接口,供企鹅公司调用他们员工的差旅信息,然后我就三下五除二的给写好

了,上线之后,大概过了一个月。。。DBA那边报告数据库出现大量锁超时,并且及时根据sql的来源将email发到了我们部门,指出sql读取时间过长,

并且缺少nolock,影响了大量机票订单入库,然后我就拿着sql去生产环境跑了下,22s。。。花擦。。。项目上线时间太久,版本已经不存在了,无法

回滚。。。原本准备撤下接口。。。看了下撤下接口跟加上nolock时间相差不多,最后决定先加上nolock,发布紧急单。。。然后再优化,DBA那边暂

时做手工解锁,发上去后,最后就是损失XXXX订单。。。定级为三级事件。然后就是追责,当然这个责任只能有老大们去承担了,出了这次由我引发的

事件,我得思考了,出了事情对我不见得全是坏事,起码这次会让我铭记如心,想想也搓,来携程之前根本就不会关注要不要给select指定nolock,这其

中也包括自己没遇到过大数据吧,也包括自己的能力有限,只知道有锁这个玩意,细说的话就啥也不知道了,后来才知道携程有个规则,就是很多业务产

线所写的select都必须指定nolock,懂一点的人可能会说nolock可以提升性能,如果你这样说,确实是这样,因为数据库的锁是有96字节开销的,没了

锁,也就没有你在profile中看到accquired和released痉挛了,当你看完我的事件之后,你可能会意识到,性能提升不是最关心的,最关心就是不要出现

死锁,锁等待。。。好了,言归正传,下面我们看看到底在数据库中可以指定多少个锁???

 

一:到底可以指定多少个锁

  这个问题有意思,我们不需要记,只要你装一个SQL Prompt,有了这个神器,你就知道到底有多少个?如下图:

1 DROP TABLE dbo.Person
2 CREATE TABLE Person(ID INT IDENTITY,NAME CHAR(4000) DEFAULT 'xxxxx')
3 INSERT INTO dbo.Person DEFAULT VALUES
4 go 6

一眼扫下去,还是蛮多的,不过你要注意了,那些所谓的XXXLock才是我们需要关注的,根据上面的图,我们大概把锁分个类。。。

粒度锁:PAGLOCK, TABLOCK, TABLOCKX, ROWLOCK, NOLOCK

模式锁:HOLDLOCK, UPDLOCK, XLOCK

接下来我从粒度锁说起:

1. NOLOCK

  都说nolock是无锁模式的,那到底是怎样的无锁呢???到这篇为止,你应该知道,如果不加nolock,我们的表,数据页是附加IS锁的,那接

下来我用profile看下两者有什么区别。 

 

从上图中,你会看到加上nolock之后,object上面附加了Sch-S锁,这个锁叫做“架构稳定锁”,很简单就是sql编译时附加的一把锁,目的就是

防止在编译时,有其他连接修改表结构,而这个锁只与Sch-M锁冲突,与其他锁都兼容,这说明什么?说明其他连接锁住了记录也没关系,我的

nolock不跟他们打交道,这样的话,就可能会读到脏数据,不过没关系,携程的很多业务是容许脏数据的,毕竟比锁等待,死锁要强得多,再说

nolock读到了其他连接未修改或者未提交的数据,这个概率也比较低,就算遇到了也没关系,一般不会招来客诉的,客人或许再刷下页面,数据

或许就正确了,对不对。。。

 

2.TABLOCK

  这个还是比较见名识义的,就是附加在table上的锁,也就是表锁了,很恐怖的。。。下面我举个Update的例子,看看前后对比。

在上面你有没有看到,X锁已经附加到OBJECT上面去了。。。这样的话,其他连接就动不了这个Object了,只能等待。。。

 

3.  PAGLOCK

  看了名字你应该也知道,就是附加到页面这个级别的锁,我也举一个Update的例子。

1 BEGIN TRAN
2 UPDATE dbo.Person SET NAME='aaaaa' WHERE ID=6
3 
4 BEGIN TRAN
5 UPDATE dbo.Person WITH(PAGLOCK) SET NAME='bbbbb' WHERE ID=4

从上面两个图中,你应该可以看到,原来附加到RID上面的U锁,由于PagLock的提升,现在要附加到Page上面了,这个就是所谓的数据页锁。

 

4.TABLOCKX, ROWLOCK

   这两个我就不细说了,TABLOCKX就是直接附加在table上的X锁,你可以通过select看一下。

ROWLOCK的话,默认情况下就是ROWLOCK,比如默认的Update,你会发现RID上被附加的U锁,这个就是行锁。

 

5.UPDLOCK

 这个锁还是蛮有意思的,它就是update锁,如果你select下,它会呈现update的锁痉挛效果。

  

6. XLOCK

  知道了UPDLOCK锁,我想XLOCK你也应该明白了。。。它就是delete锁,即排他锁,我可以让select带上排他锁。

 

7.HOLDLOCK

  最后一个我也没闹明白,据说是让语句在整个事务中持有锁,然后我就用select和update调试一下。

1 SELECT * FROM dbo.Person(HOLDLOCK)
2 UPDATE dbo.Person WITH(HOLDLOCK) SET NAME='bbbbb' WHERE ID=4

从图中可以看到,HOLDLOCK不管是在select还是Update中,都是对表持有锁,没心情研究了,明天可以回家了。。。留给大家观察吧。

 

最后祝大家新年愉快,阖家欢乐,我也终于在年前完成了这个系列,也祝贺祝贺自己。

分类:  sql server
24
2
(请您对文章做出评价)
« 上一篇: Sql Server之旅——第十四站 深入的探讨锁机制
» 下一篇: asp.net mvc 之旅—— 第一站 从简单的razor入手
posted @  2015-02-14 23:59  一线码农 阅读( 4602) 评论( 43编辑  收藏

  
#1楼 2015-02-15 00:15  Microshaoft   
insert 时的锁?
  
#2楼 [ 楼主2015-02-15 00:27  一线码农   
@ Microshaoft
是的,慢速的select阻碍了insert和update,导致超时。
  
#3楼 2015-02-15 08:46  麻将我会   
@ 一线码农
这的确是个大的话题
  
#4楼 2015-02-15 08:54  YoMe   
楼主在广州?在天河?
  
#5楼 [ 楼主2015-02-15 09:29  一线码农   
@ 麻将我会
确实是个话题
  
#6楼 [ 楼主2015-02-15 09:29  一线码农   
@ Yowe
上海呢
  
#7楼 2015-02-15 09:42  王清培   
支持,这么晚,够拼的。
  
#8楼 2015-02-15 09:46  #山鸡   
谢谢楼主,又学习了
  
#9楼 [ 楼主2015-02-15 09:51  一线码农   
@ 王清培
今年最后一篇了,就拼一下下了
  
#10楼 [ 楼主2015-02-15 09:52  一线码农   
@ #山鸡
感谢支持
  
#11楼 2015-02-15 10:07  有梦想不放弃   
你好,楼主,为什么我的profile看不到锁的情况呢,我的数据库是sql2008 r2
  
#12楼 [ 楼主2015-02-15 10:25  一线码农   
@ 有梦想不放弃
参考我的第十三篇,那一篇有说的
  
#13楼 2015-02-15 11:35  新生活一号   
我来告诉你HOLDLOCK的作用,HOLDLOCK锁会 排斥X锁(并且仅和x锁排斥) 具体应用场景是 update delete时会 阻塞默认select 使等待
如A用户正在update delete数据(会上x锁),事务未提交
B用户select 到对应的时间就会等待 因为(HOLDLOCK锁排斥x所)
如果B用户不要等待就select with(nolock) 这样就不发布HOLDLOCK锁,不被阻塞

总结:HOLDLOCK只排斥X锁 ,用来实现避免读到脏数据的技术实现
更新锁UPDLOCK 排斥自身,也排斥 X锁 但与HOLDLOCK锁容易,所以对记录加UPDLOCK 更新锁 也不会阻塞默认的select 这个正常的思维印想是不同的
X锁排斥一切所
  
#14楼 [ 楼主2015-02-15 11:42  一线码农   
@ 新生活一号
我觉得HOLDLOCK严格来说算是一个状态锁,它把其他锁的状态维持到整个事务的生命周期,比如UpdLock,XLock这些。
X锁还是有兼容的,比如意向锁I,在profile上面你会看到很多IX这样的复合锁。
感谢你的分享。
  
#15楼 2015-02-15 12:15  新生活一号   
@ 一线码农
引用 @新生活一号
我觉得HOLDLOCK严格来说算是一个状态锁,它把其他锁的状态维持到整个事务的生命周期,比如UpdLock,XLock这些。
X锁还是有兼容的,比如意向锁I,在profile上面你会看到很多IX这样的复合锁。
感谢你的分享。

-----------
是呀我的理解 HOLDLOCK 就是用来实现事务的 事务隔离级别就是以HOLDLOCK锁来判断是否在进行中
  
#16楼 2015-02-15 13:37  我是大菠萝   
@ 一线码农
引用 @新生活一号
我觉得HOLDLOCK严格来说算是一个状态锁,它把其他锁的状态维持到整个事务的生命周期,比如UpdLock,XLock这些。
X锁还是有兼容的,比如意向锁I,在profile上面你会看到很多IX这样的复合锁。
感谢你的分享。

IX是意向排他锁,是单独的一种锁形式,而不是所谓的复合锁;
其意义是在粒度更粗对象上放置,来提示其他非兼容性锁;具体简单的例子
比如你事务中update一条数据但未提交,这时候你的DDL语句会被阻塞,其真实原因并不是因为DDL遇到update的U锁,而是在page和object级有IX锁;
如果没有IX锁,就要判断每一行是否有X锁,这样的开销太大;
  
#17楼 [ 楼主2015-02-15 13:41  一线码农   
@ 新生活一号
是的,感谢分享,新年快乐
  
#18楼 [ 楼主2015-02-15 13:49  一线码农   
@ 我是大菠萝
引用 @一线码农
引用引用@新生活一号
我觉得HOLDLOCK严格来说算是一个状态锁,它把其他锁的状态维持到整个事务的生命周期,比如UpdLock,XLock这些。
X锁还是有兼容的,比如意向锁I,在profile上面你会看到很多IX这样的复合锁。
感谢你的分享。

IX是意向排他锁,是单独的一种锁形式,而不是所谓的复合锁;
其意义是在粒度更粗对象上放置,来提示其他非兼容性锁;具体简单的例子
比如你事务中update一条数据但未提交,这时候你的DDL语句会被阻塞,其真实原因并不是因为DDL遇到update的U锁,而是在page和object级有IX锁;
如果没有IX锁,就要判...

@我是大菠萝
感谢你的建议,新年快乐,update锁还是蛮有意思的,IX锁只有在引擎发现了目标数据页的目标记录,这时候才会将数据页的默认IU锁转化为IX锁,如果这时候第二个update过来了,发现page上有了IX锁,就会阻塞。

PS: 现在在公司,开不了profile,不然可以验证验证。o(∩_∩)o
  
#19楼 2015-02-15 14:10  阿水   
启用行版本控制还有这个问题吗?2005开始支持行版本控制了!
  
#20楼 [ 楼主2015-02-15 14:13  一线码农   
@ 阿水
这个倒没有研究过,据说是保存在tempdb里面的,RID里面保存着当时的快照指针...感谢分享。
  
#21楼 2015-02-15 14:14  阿水   
@ 一线码农
行版本控制就是为了解决S锁的问题, 但是至于性能是否提升就不知道了,要看具体环境。
  
#22楼 [ 楼主2015-02-15 14:16  一线码农   
@ 阿水
刚看了昵称才知道是你这位大牛啊,新年快乐o(∩_∩)o
  
#23楼 2015-02-15 14:20  阿水   
@ 一线码农
新年快乐!
  
#24楼 2015-02-15 14:57  Cheney Shue   
等到上线才发现问题,测试要被炒鱿鱼了
  
#25楼 2015-02-15 15:51  深蓝医生   
在电商订单处理中,是否需要先 updlock 住商品的库存表,然后插入订单表,最后更新库存表?
  
#26楼 2015-02-15 16:36  Code的那些事   
马丹,受教,以前从没有加这个with
  
#27楼 2015-02-15 18:06  有想法的IT民工   
帅哥,我猜到你是谁,我和你一个部门的,哈哈。
  
#28楼 2015-02-15 19:05  往来往去   
在同程SELECT语句必须加上WITH(NOLOCK)
  
#29楼 2015-02-15 23:59  风浪   
大神, 我想问一下, 这么大的系统, select都用with(nolock)怎保证数据的完整性? 能帮忙解答一下吗, 谢谢!
  
#30楼 2015-02-16 09:55  阿水   
@ 风浪
严格讲不能保证数据完整性,因为会发生脏读,但是有的时候为了避免共享锁引起的阻塞,有些场景下会使用 nolock。
  
#31楼 2015-02-16 11:51  风浪   
@ 阿水
嗯, 都是这样做, 如果是小系统,数据业务不忙, 读脏机率小, 可以忽略不计,但像ctrip这样的平台数据需要完整性很强的, 而且是大系统, 数据业务这么繁忙, 如果真的发生读脏(我感觉机率还是很高的), 有没有解决的办法呢?
  
#32楼 2015-02-17 00:05  Cheney Shue   
@ 深蓝医生
引用 在电商订单处理中,是否需要先 updlock 住商品的库存表,然后插入订单表,最后更新库存表?


不可能lock数据库表,压力是不能转到数据库,库存一致性在应用层解决。
  
#33楼 2015-02-17 00:06  Cheney Shue   
@ 风浪
引用 @阿水
嗯, 都是这样做, 如果是小系统,数据业务不忙, 读脏机率小, 可以忽略不计,但像ctrip这样的平台数据需要完整性很强的, 而且是大系统, 数据业务这么繁忙, 如果真的发生读脏(我感觉机率还是很高的), 有没有解决的办法呢?


银行数据库的acid要求更高,大多是ibm的解决方案,认准ioe就对了。
  
#34楼 2015-02-17 10:16  W2N   
看到三级事件的标题,就觉得是不是ctrip
  
#35楼 2015-02-17 16:35  家有良田三亩八   
先收藏,开年看
  
#36楼 2015-02-26 16:29  11点后要睡觉   
先收藏,开年看
  
#37楼 2015-02-27 14:43  万剑齐发   
携程啊,偶的目标...
  
#38楼 [ 楼主2015-03-02 17:07  一线码农   
@ 有想法的IT民工
请问,你是???
  
#39楼 2015-03-05 20:56  Learning hard   
请问楼主参考那本书籍学习的?
  
#40楼 2015-03-06 11:22  undefined   
帅哥,我猜到你是谁,我和你不是一个部门的,哈哈。
  
#41楼 2015-03-24 23:42  蒋攀   
终于把这个系列看完了,受益匪浅
  
#42楼 2015-03-29 10:51  pursuer.chen   
楼主的博客更新速度让人敬佩,不断学习的精神也值得学习,受教。
  
#43楼 2015-07-18 23:16  undefined   
@ 风浪
引用 @阿水
嗯, 都是这样做, 如果是小系统,数据业务不忙, 读脏机率小, 可以忽略不计,但像ctrip这样的平台数据需要完整性很强的, 而且是大系统, 数据业务这么繁忙, 如果真的发生读脏(我感觉机率还是很高的), 有没有解决的办法呢?


脏读不实时, 实时不脏读。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值