Sql Isolation Level
隔离性(Isolation):与数据库中的事务隔离级别以及锁相关,多个用户可以对同一数据并发访问而又不破坏数据的正确性和完整性。但是并行事务的修改必须与其他并行事务的修改相互独立,隔离。但是在不同的隔离级别下,事务的读取操作可能得到的结果是不同的。
隔离级别用于决定如何控制并发用户读写数据的操作。读操作默认使用共享锁,写操作需要使用排它锁。
共享锁(S):读锁,可以并发读取数据但不能修改数据,也就是说当数据资源上存在共享锁时,所有的事务都不能对这个资源进行修改,直到数据读取完成,共享锁释放。
排它锁(X):独占锁,写锁,同一资源同一时间只允许一个事务进行操作,防止同时对同一资源进行多重操作。
更新锁(U):防止出现死锁的情况,两个事务对同一资源先进行读取再修改的情况下,使用共享锁排它锁有时会出现死锁现象,而使用更新锁则可以避免死锁的出现。资源的更新锁一次只能分配给一个事务,如果需要对资源进行修改,更新锁会变成排它锁,否则变成共享锁。
意向锁: SQL SERVER需要在层次结构中的底层资源上(如行,列)获取共享锁,排它锁,更新锁。例如表级放置了意向共享锁,就表示事务要对表的页或行上使用共享锁。在表的某一行上放置意向锁,可以防止其他事务获取其他不兼容的锁。意向锁可以提高性能,因为数据引擎不需要检测资源的每一列每一行,就能判断是否可以获取到该资源的兼容锁。意向锁包括三种类型:意向共享锁(IS),意向排它锁(IX),意向排他共享锁(SIX).
架构锁:防止修改表结构时,并发访问的锁
大容量更新锁:允许多个线程将大容量数据并发的插入到同一个表中,在加载的同时,不允许其他进程进行访问。
READ UNCOMMITED
指定语句可以读取已由其他事务修改但尚未提交的行。
在READ UNCOMMITED级别运行的事务,不会发出共享锁来防止其他事务修改当前事务读取的数据。READ UNCOMMITED也不会被排它锁阻塞,排它锁会禁止当前事务读取其他事务已修改但尚未提交的行。设置此选项后,可以读取未提交的修改,这种读取称为脏读。
READ UNCOMMITTED 造成数据脏的问题。
脏读:在读取到未提交的事务数据,由于各种原因造成事务回滚,此时读取的数据为无效的数据及脏数据。
示例:
未执行update之前Query1
执行update
Query2查询结果
Query1 rollback transaction,导致Query2读到未提交的数据
READ COMMITTED
指定语句不能读取已由其他事务修改但尚未提交的数据。这样可以避免脏读。其他事务可以在当前事务各个语句之间更改数据,从而产生不可重复读取和虚拟数据。该选项是SQL Server默认设置。
READ COMMITTED 解决数据脏读的问题,但避免不了不可重复读(在同一事务中多次查询),更新值丢失的问题(由于事务对锁的控制没有和事务生命周期一致,导致查询结束后释放对共享锁的控制,而此时另一个事务的修改操作,获取排它锁,并对数据进行了修改,导致第一个事务查询出的值是更新前的值。REPEATABLE READ可重复读可以解决此问题,但容易造成死锁),原因是对数据的修改需要排他锁,对数据的查询需要共享锁,而二者相互排斥,读操作(Select)在读取有排它锁的数据时,会一直阻塞,直到排它锁被释放(修改数据的提交或者回滚)后,获取到共享锁后才能查询到数据。
a,解决脏读问题
Query1执行修改数据,未提交或者回滚,Query2查询一直阻塞(等待排它锁释放后获得共享锁),直至Query提交后者回滚(释放排它锁)。
Query1
Query2
b、不可重复读问题:
分析:大概率发生在高并发下,当一个事务有多个查询时,在中间某个查询结束释放了共享锁,此时恰巧其他事务获取排它锁修改了数据,提交事务释放排它锁,当前事务获取共享锁继续执行查询,可能出现多次查询结果不一致的情况。
如果共享锁的生命周期和当前事务保持一致,那么其他事务就不会获得排它锁(共享锁和排它锁是互斥),因此数据就可以重复读了。
c、更新值丢失
分析:由于事务对锁的控制没有和事务生命周期一致,导致查询结束释放对共享锁的控制,而此时另一个事务抢先获得排它锁修改数据,提交事务释放排它锁,当前修改事务继续用之前获取到值执行修改操作,导致数据更细错误。
REPEATABLE READ
指定语句不能读取已由其他事务修改但尚未提交的数据,并且指定,其他任何事务都不能在当前事务完成之前修改当前事务读取的数据。
对事务中的每个语句所读取的全部数据都设置了共享锁,并且该共享锁一直保持到事务完成为止。这样可以防止其他事务修改当前事务读取的任何行。其他事务可以插入与当前事务所发出语句的搜索条件相匹配的新行。如果当前事务随后重试执行该语句,它会检索新行,从而产生虚拟读取。由于共享锁一直保持到事务结束,而不是在每个语句结束时释放,因此并发级别低于默认的READ COMMITTED隔离级别,此选项只在必要时使用。
REPEATABLEREAD可重复读,可以解决不可重复读的问题,也可以解决数据更新丢失的问题(但负面影响会造成死锁);缺点是无法避免幻读。可重复读获取共享锁的生命周期是同事务生命周期一致的,只要事务不提交,将一直保持共享锁,其他事务无法获得排它锁进而无法对事务进行更改。
幻读:在可重复读级别下运行的事务,读操作获得的共享锁将一直保持到事务结束。因此可以保证在事务中第一次读取某些行后,还可以重复读取这些行。但是,事务只锁定查询第一次运行找到的那些行,而不是锁定查询结果范围外的其他行。因此在同一事务进行第二次读取之前,如果其他事务插入了新行,而且新行满足读操作过滤条件,新行也会出现在第二次读操作返回的结果中。这些新行称之为幻影,这种读操作称之为幻读。
分析:可重复读会在满足过滤条件的数据上加上过滤锁,但是无法再新加的数据上添加共享锁,因而隔离级别可重复读无法避免幻读的问题,SERIALIZABLE 可序列化的隔离模式可以解决该问题。
SERIALIZABLE
语句不能读取已由其他事务修改但尚未提交的数据。
任何事务都不能在当前事务完成之前修改由当前事务读取的数据。
在当前事务完成之前,其他事务不能使用当前事务中任何语句读取键值插入新行。
SERIALIZABLE可序列化:读操作共享锁生命周期与事务保持一致,读操作不仅锁定了满足查询条件的那些行,还锁定了可能满足查询条件的行。
SNAPSHOT
指定事务 中任何语句读取的数据都将是在事务开始时便存在的数据的事务上一致的版本。事务只能识别在其开始之前提交的数据修改。在当前事务中执行的语句将看不到在当前事务开始以后由其他事务所作的数据修改。其效果就好像事务中国你的语句获得了已提交数据的快照,因此该数据在事务开始时就存在。
除非正在恢复数据库,否则SNAPSHOT事务不会在读取数据时请求锁。读取数据的SNAPSHOT事务不会阻止其他事务写入数据。写入数据的事务也不会阻止SNAPSHOT事务读取数据。
在数据库恢复的回滚阶段,如果尝试读取由其他正在回滚的事务锁定的数据,则 SNAPSHOT 事务将请求一个锁。 在事务完成回滚之前,SNAPSHOT 事务会一直被阻塞。 当事务取得授权之后,便会立即释放锁。
必须将 ALLOW_SNAPSHOT_ISOLATION 数据库选项设置为 ON,才能开始一个使用 SNAPSHOT 隔离级别的事务。 如果使用 SNAPSHOT 隔离级别的事务访问多个数据库中的数据,则必须在每个数据库中将 ALLOW_SNAPSHOT_ISOLATION 都设置为 ON。
不能将通过其他隔离级别开始的事务设置为 SNAPSHOT 隔离级别,否则将导致事务中止。 如果一个事务在 SNAPSHOT 隔离级别开始,则可以将它更改为另一个隔离级别,然后再返回 SNAPSHOT。 事务在第一次访问数据时启动。
在 SNAPSHOT 隔离级别下运行的事务可以查看由该事务所做的更改。 例如,如果事务对表执行 UPDATE,然后对同一个表发出 SELECT 语句,则修改后的数据将包含在结果集中。
SNAPSHOT快照:通过保存一份读副本来减少因另一个事务修改相同数据导致的阻塞问题,这也预示了一个事务不能看到另一个事务的改动。