事物中的锁简单说就是S共享锁,X排它锁

X锁在所有事物中,一旦加上,持续到COMMIT才释放

 

未提交读 (READ UNCOMMITTED):读取时候不会加共享锁,直接读取

已提交读(READ COMMITTED):读取时候加S锁,读完立马释放S锁,会在事物完成前就立马释放S锁

可重复读(REPEATABLE READ):读取时候加S锁,一直持续到事物提交才会释放S锁,如果中间有数据插入,改模式会读取到插入的数据,会出现幻像

 

 RepeatableRead 幻读是在数据库中经常出现的一种问题,它是由于事务的隔离级别导致的。在 Repeatable Read 隔离级别下,事务在读取数据时会加锁,确保其他事务不能修改该行数据,这就保证了事务内部的读取是一致性的。
但是在某些情况下,即使数据被锁定,也可能会导致幻读的问题。比如在一个事务中查询了某个范围的数据,然后另外一个事务插入了一条符合该范围的数据,此时再次查询这个范围的数据,就会出现幻读的情况。
在 Repeatable Read 隔离级别下,只锁定了读取的数据行,而没有锁定范围内的数据页,所以当其他事务插入新数据时,事务仍然可以读取到该范围内的数据。为了避免幻读,可以使用 Serializable 隔离级别来解决,它会锁定整个范围,确保事务之间的读写操作都是串行的

 

可序列化(SERIALIZABLE):读取时候加S锁,一直持续到事物提交才会释放S锁,但是数据库底层会有机制确保该隔离级别下

所有的操作都是串行,不会出现幻像,因为中间有数据插入,该模式下也读取不到,应该是该模式比REPEATABLE READ

还多了范围锁。该隔离级别在读写数据时会锁住整张表。

测试方法,创建一个表,CREATE TABLE [dbo].[Test]

(
[id] [int] IDENTITY(1,1) NOT NULL,//设置主键
[price] [float] NOT NULL,
)
 
//下面语句可以找出来加锁的事物
 
select    
   *,  
    OBJECT_NAME(resource_associated_entity_id) tableName    
from    
    sys.dm_tran_locks   
where    
    resource_type='OBJECT' 
//end

在测试时候会有死锁,用kill pid;来结束;

 

但是一定要设置:

ALTER DATABASE   [databaseName] SET READ_COMMITTED_SNAPSHOT off;

或者

ALTER DATABASE [databaseName]  SET READ_COMMITTED_SNAPSHOT on  with rollback immediate

//禁用行版本控制模式,就可以测试以上所有的事物特性,和理解的是一样的;如果该选项打开,那么将会用行版本来控制,参考mysql 或者sql server 的MVCC实现机制来理解

sqlserver新版本还有还有MYSQL ,ORACLE等都实现了MVCC机制,在该机制下READ COMMITTED,REPEATABLE READ读取数据时候不会加S锁,而是用了行版本控制来实现一样的效果,但是占用资源会更少,以上流程都是按照数据库标准的悲观锁来设计的,基本主流的的数据库都是按照这个模式来做的。

行版本控制需要使用下面指令来启动行版本控制:

ALTER DATABASE   [databaseName] SET READ_COMMITTED_SNAPSHOT off;

或者

ALTER DATABASE [databaseName]  SET READ_COMMITTED_SNAPSHOT on  with rollback immediate

 

Read Committed Snapshot和Snapshot Isolation都是通过行版本控制来实现的;

SNAPSHOT快照:SNAPSHOT和READ COMMITTED SNAPSHOT两种隔离(可以把事务已经提交的行的上一版本保存在TEMPDB数据库中)

SNAPSHOT隔离级别在逻辑上与SERIALIZABLE类似
READ COMMITTED SNAPSHOT隔离级别在逻辑上与 READ COMMITTED类似

 

不过在快照隔离级别下读操作不需要申请获得共享锁,所以即便是数据已经存在排他锁也不影响读操作。而且仍然可以得到和SERIALIZABLE与READ COMMITTED隔离级别类似的一致性;如果目前版本与预期的版本不一致,读操作可以从TEMPDB中获取预期的版本。

如果启用任何一种基于快照的隔离级别,DELETE和UPDATE语句在做出修改前都会把行的当前版本复制到TEMPDB中,而INSERT语句不需要在TEMPDB中进行版本控制,因为此时还没有行的旧数据

无论启用哪种基于快照的隔离级别都会对更新和删除操作产生性能的负面影响,但是有利于提高读操作的性能因为读操作不需要获取共享锁;

MVCC机制

MVCC

MVCC即多版本并发控制,使用了双版本号来解决数据的隔离问题。(“create”一个版本号,“delete”一个版本号,修改操作拆分为“delete”和“create”)每个事务在开始对每张表增删改查操作时都会生成一个版本号,每个事务只能查到“create”小于本版本号和“delete”大于本版本号的数据。这样,增删查操作就完全可以并发进行了,只有修改操作是一定要排队的。这样,就算没有共享锁也解决了不可重复读问题,因为其他事务修改后,数据的版本号比我大,我不会读到。