[转]SQL Server 中WITH (NOLOCK)浅析
2015-11-19 10:12
405 查看
[code]BEGINTRAN
UPDATETESTSETNAME='Timmy'WHEREOBJECT_ID=1;
--ROLLBACK
[/code]
[/code]
打开会话窗口2,执行下面脚本,你会发现执行结果一直查询不出来(其实才两条记录)。当前会话被阻塞了
[code]
[code]SELECT*FROMTEST;
[/code]
[/code]
打开会话窗口3,执行下面脚本,查看阻塞情况,你会发现在会话2被会话1给阻塞了,会话2的等待类型为LCK_M_S:“当某任务正在等待获取共享锁时出现”
[code]
[code]
SELECTwt.blocking_session_idASBlockingSessesionId
,sp.program_nameASProgramName
,COALESCE(sp.LOGINAME,sp.nt_username)ASHostName
,ec1.client_net_addressASClientIpAddress
,db.nameASDatabaseName
,wt.wait_typeASWaitType
,ec1.connect_timeASBlockingStartTime
,wt.WAIT_DURATION_MS/1000ASWaitDuration
,ec1.session_idASBlockedSessionId
,h1.TEXTASBlockedSQLText
,h2.TEXTASBlockingSQLText
FROMsys.dm_tran_locksAStl
INNERJOINsys.databasesdb
ONdb.database_id=tl.resource_database_id
INNERJOINsys.dm_os_waiting_tasksASwt
ONtl.lock_owner_address=wt.resource_address
INNERJOINsys.dm_exec_connectionsec1
ONec1.session_id=tl.request_session_id
INNERJOINsys.dm_exec_connectionsec2
ONec2.session_id=wt.blocking_session_id
LEFTOUTERJOINmaster.dbo.sysprocessessp
ONSP.spid=wt.blocking_session_id
CROSSAPPLYsys.dm_exec_sql_text(ec1.most_recent_sql_handle)ASh1
CROSSAPPLYsys.dm_exec_sql_text(ec2.most_recent_sql_handle)ASh2
[/code]
[/code]
此时查看会话1(会话1的会话ID为53,执行脚本1前,可以用SELECT@@spid查看会话ID)的锁信息情况,你会发现表TEST(ObjId=1893581784)持有的锁信息如下所示
打开会话窗口4,执行下面脚本.你会发现查询结果很快就出来,会话4并不会被会话1阻塞。
SELECT*FROMTESTWITH(NOLOCK)
从上面模拟的这个小例子可以看出,正是由于加上WITH(NOLOCK)提示后,会话1中事务设置的排他锁不会阻碍当前事务读取锁定数据,所以会话4不会被阻塞,从而提升并发时查询性能。
2:WITH(NOLOCK)不发布共享锁来阻止其他事务修改当前事务读取的数据,这个就不举例子了。
本质上WITH(NOLOCK)是通过减少锁和不受排它锁影响来减少阻塞,从而提高并发时的性能。所谓凡事有利也有弊,WITH(NOLOCK)在提升性能的同时,也会产生脏读现象。
如下所示,表TEST有两条记录,我准备更新OBJECT_ID=1的记录,此时事务既没有提交也没有回滚
[code][code]BEGINTRAN
UPDATETESTSETNAME='Timmy'WHEREOBJECT_ID=1;
--ROLLBACK
[/code]
[/code]
此时另外一个会话使用WITH(NOLOCK)查到的记录为未提交的记录值
假如由于某种原因,该事务回滚了,那么我们读取到的OBJECT_ID=1的记录就是一条脏数据。
脏读又称无效数据的读出,是指在数据库访问中,
WITH(NOLOCK)使用场景
什么时候可以使用WITH(NOLOCK)?什么时候不能使用WITH(NOLOCK),这个要视你系统业务情况,综合考虑性能情况与业务要求来决定是否使用WITH(NOLOCK),例如涉及到金融或会计成本之类的系统,出现脏读那是要产生严重问题的。关键业务系统也要慎重考虑。大体来说一般有下面一些场景可以使用WITH(NOLOCK)
1:基础数据表,这些表的数据很少变更。
2:历史数据表,这些表的数据很少变更。
3:业务允许脏读情况出现涉及的表。
4:数据量超大的表,出于性能考虑,而允许脏读。
另外一点就是不要滥用WITH(NOLOCK),我发现有个奇怪现象,很多开发知道WITH(NOLOCK),但是有不了解脏读,习惯性的使用WITH(NOLOCK)。
WITH(NOLOCK)与NOLOCK区别
为了搞清楚WITH(NOLOCK)与NOLOCK的区别,我查了大量的资料,我们先看看下面三个SQL语句有啥区别
SELECT*FROMTESTNOLOCK
SELECT*FROMTEST(NOLOCK);
SELECT*FROMTESTWITH(NOLOCK);
上面的问题概括起来也就是说NOLOCK、(NOLOCK)、WITH(NOLOCK)的区别:
1:NOLOCK这样的写法,其实NOLOCK其实只是别名的作用,而没有任何实质作用。所以不要粗心将(NOLOCK)写成NOLOCK
2:(NOLOCK)与WITH(NOLOCK)其实功能上是一样的。(NOLOCK)只是WITH(NOLOCK)的别名,但是在SQLServer2008及以后版本中,(NOLOCK)不推荐使用了,"不借助WITH关键字指定表提示”的写法已经过时了。具体参见MSDN
2.1至于网上说WITH(NOLOCK)在SQLSERVER2000不生效,我验证后发现完全是个谬论。
2.2在使用链接服务器的SQL当中,(NOLOCK)不会生效,WITH(NOLOCK)才会生效。如下所示
消息4122,级别16,状态1,第1行
Remotetable-valuedfunctioncallsarenotallowed.
WITH(NOLOCK)会不会产生锁
很多人误以为使用了WITH(NOLOCK)后,数据库库不会产生任何锁。实质上,使用了WITH(NOLOCK)后,数据库依然对该表对象生成Sch-S(架构稳定性)锁以及DB类型的共享锁,如下所示,可以在一个会话中查询一个大表,然后在另外一个会话中查看锁信息(也可以使用SQLProfile查看会话锁信息)
不使用WTIH(NOLOCK)
使用WITH(NOLOCK)
从上可以看出使用WITH(NOLOCK)后,数据库并不是不生成相关锁。对比可以发现使用WITH(NOLOCK)后,数据库只会生成DB类型的共享锁、以及TAB类型的架构稳定性锁.
另外,使用WITH(NOLOCK)并不是说就不会被其它会话阻塞,依然可能会产生SchemaChangeBlocking
会话1:执行下面SQL语句,暂时不提交,模拟事务正在执行
[code][code]BEGINTRAN
ALTERTABLETESTADDGradeVARCHAR(10);
[/code]
[/code]
会话2:执行下面语句,你会发现会话被阻塞,截图如下所示。
[code]
[code]SELECT*FROMTESTWITH(NOLOCK)
[/code]
[/code]
相关文章推荐
- MySQL5.7的安装步骤
- MySQL自增锁
- Oracle EBS Form 发布到Server端的注意事项
- SQL SERVER 2008 R2配置管理器出现“远程过程调用失败”【0x800706be】的解决办法
- 活用与符号(||),实现sql多条件组查询
- postgresql 时区配置,系统主机与数据库时间不一致
- Oracle 创建自增列
- SQLITE删除表中所有数据方法
- oracle 导入导出
- 在C#中使用官方驱动操作MongoDB
- 查看mysql的安装信息
- 读取数据库并将其中英文内容翻译成中文的过程
- 【转】【CUBE】Oracle分组函数之CUBE魅力
- [转]【ROLLUP】Oracle分组函数之ROLLUP魅力
- 《Oracle数据库的SQL分页模板》
- Oracle触发器使用
- SQL高性能查询优化语句(总结)
- CentOS 6上的redis搭建实战记录(转)
- Redis常用命令
- oracle去重