前言
今天被领导批了!
为什么呢?事情的起因是这样的:今天产品有个需求,需要线上所有数据的某某情况,然后领导就然后帮忙写个sql语句,然后给dba跑。我心想这很简单啊,就啪啪啪很快写好了sql语句,然后自信的发给他了,对的然后就被领导批了。他说线上好几百万条数据,然后现在其他的系统和用户也都在使用,你就这么写不怕死锁吗?我战战兢兢地说,那写个事务?他给了我一个白眼,说你加个with(nolock)不就行了。然后给我演示了一遍,我心想啥?这个命令这么牛吗?就这么加着就能够避免死锁?然后就查了查,然后发现果然厉害啊,今天就来说道说道这个厉害的with(nolock)!
什么是with(nolock)
先来说说什么是with(nolock),就是它是干嘛的。其实看名字我们应该就能够知道就是不锁呗!对的,在sql server数据库中,往往为了性能在进行数据查询的时候,都会在查询语句的表的后面加上一个with(nolock)或者nolock也行,它的目的就是为了避免查询锁定表,进而提高查询的速度。
with(nolock)其实是表提示(表提示table hint指的是通过指定锁定方法、一个或多个索引、查询处理操作(如表扫描或索引查找)或其他选项,表提示可在数据操作语言 (DML) 语句执行期间覆盖查询优化器的默认行为。)中的一种。它等同于数据库的4种事务隔离级别中的READ UNCOMMITTED(读未提交)。
脏读、不可重复读、幻读
我们知道当多个用户同一时间访问同一数据库资源,这就是并发,而如果并发用户中有对数据资源进行了修改,那么极有可能产生以下的几种情况:
脏读:指当一个事务正在访问数据,并且对数据进行了修改,而这种数据还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据还没有提交那么另外一个事务读取到的这个数据就被称之为脏数据。依据脏数据所做的操作肯能是不正确的。
不可重复读:指的是在一个事务内,多次读同一数据。在这个事务还没有执行结束,另外一个事务也访问该同一数据,那么在第一个事务中的两次读取数据之间,由于第二个事务的修改第一个事务两次读到的数据可能是不一样的,这样就发生了在一个事物内两次连续读到的数据是不一样的,这种情况被称为是不可重复读。
幻读:一个事务先后读取一个范围的记录,但两次读取的纪录数不同,我们称之为幻象读(两次执行同一条 select 语句会出现不同的结果,第二次读会增加一数据行,并没有说这两次执行是在同一个事务中)
数据库的四种隔离级别
为了解决上述的三种情况,数据库有四种隔离级别,它们分别是分别为 Read uncommitted(读未提交),Read committed(读已提交),Repeatable read(可重复读),Serizable(序列化)能够解决的情况如下:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
Read uncommitted | √ | √ | √ |
Read committed | √ | √ | |
Repeatable read | √ | ||
Serializable |
关于事务的隔离级别更多的内容可以参考之前的一篇文章事务的隔离级别,这里就不在重复赘述!
前面说到with(nolock)等同于数据库的事务隔离级别中的READ UNCOMMITTED(读未提交),在这情况下,查询是不会加锁的,也由于查询的不加锁,所以一致性是最差的,可能会产生"脏读"、"不可重复读"、"幻读"的情况!
with(nolock)的特点和应用场景
with(nolock)其实也不能无脑使用,因为使用with(nolock)有利也有弊,所以在决定使用之前,我们需要知道with(nolock)的特点,以及其具体的合适的应用场景。
特点:
1、使用with(nolock)查询时不受其它排他锁阻塞;
2、with(nolock)不发布共享锁来阻止其他事务修改当前事务读取的数据;
应用场景
1、业务允许脏读情况出现涉及的表的业务逻辑;
2、如果是跟我前面一样查询数据量特别大的表,但又不考虑数据的安全性,只追求性能的提示,可以使用
3、数据不经常修改的表,比如基础数据表、历史数据表,因为这样会减少锁定表的时间来加快查询的速度。
总结
使用with(nolock)会提高数据的查询速度,但是并非使用with(nolock)后数据库库不会产生任何锁。实质上,使用了with(nolock)后,数据库依然对该表对象生成Sch-S(架构稳定性)锁以及DB类型的共享锁。它的作用是不考虑任何锁,可以提升一定性能,但会引起脏读,会包含未提交事务的数据,可以考虑使用场景选择使用!