最近在项目中进行压力测试遇到了数据库的死锁问题,简言之,如下的代码在 SERIALIZABLE 隔离级别造成了死锁:

SELECT @findCount=COUNT(id) FROM MyTable
WHERE [fk_related_id]=@Argument
 
IF (@findCount > 0)
BEGIN
    ROLLBACK TRANSACTION
    RETURN ERROR_CODE
END
 
INSERT INTO MyTable ([fk_related_id],…)
VALUES (@Argument,…)
 
COMMIT TRANSACTION
RETURN SUCCESS_CODE

在搞清楚这个问题的过程中做了不少的实验,与各位共享。这一篇是开篇,主要说明的是 SQL Server 的四种(其实还有别的)经典的事务隔离级别,以及在不同的隔离级别下锁的使用手段,以及所带来的不同的数据一致性。

SQL Server 中锁的种类(Schema操作就暂时不涉及了)

锁类型

描述

(Shared Lock)

用于只读操作数据锁定

(Update Lock)

用于数据的更新,在数据真正的需要更新的时候会申请升级为X锁。

X(Exclusive Lock)

独占锁,用于数据的更改。

Key-Range Lock(稍后讨论)

仅仅在 Serializable 隔离级别保护数据,以避免任何有可能使得本事务第二次读取信息产生错误的数据插入操作

各个事务隔离级别下锁的使用

SQL Server 中有四种事务隔离级别,具体的大家去参建 MSDN。下面列出在不同的事务隔离级别下这些锁是如何使用的:

隔离级别

读数据锁状态

写数据锁状态

锁持有时间

Read Uncommitted

不获得任何锁

不获得任何锁

 

Read Committed

数据获得S锁

对于 INSERT、DELETE、UPDATE的执行,获得X锁;对于UPDATE的标记,获得U锁;

读完即释放,并不持有至事务结束。

Repeatable Read

数据获得S锁

对于 INSERT、DELETE、UPDATE的执行,获得X锁;对于UPDATE的标记,获得U锁;

持有至事务结束

Serializable

数据获得S锁,同时获得Key-Range锁。

对于 INSERT、DELETE、UPDATE的执行,获得X锁;对于UPDATE的标记,获得U锁,同时获得Key-Range锁。

持有至事务结束

我们可以利用这些知识形象说明各个隔离级别下的数据一致性:

Read Uncommitted 级别

在搞清楚这个问题的过程中做了不少的实验,与各位共享。这一篇是开篇,主要说明的是 SQL Server 的四种(其实还有别的)经典的事务隔离级别,以及在不同的隔离级别下锁的使用手段,以及所带来的不同的数据一致性。

SQL Server 中锁的种类(Schema操作就暂时不涉及了)

锁类型

描述

(Shared Lock)

用于只读操作数据锁定

(Update Lock)

用于数据的更新,在数据真正的需要更新的时候会申请升级为X锁。

X(Exclusive Lock)

独占锁,用于数据的更改。

Key-Range Lock(稍后讨论)

仅仅在 Serializable 隔离级别保护数据,以避免任何有可能使得本事务第二次读取信息产生错误的数据插入操作

各个事务隔离级别下锁的使用

SQL Server 中有四种事务隔离级别,具体的大家去参建 MSDN。下面列出在不同的事务隔离级别下这些锁是如何使用的:

隔离级别

读数据锁状态

写数据锁状态

锁持有时间

Read Uncommitted

不获得任何锁

不获得任何锁

 

Read Committed

数据获得S锁

对于 INSERT、DELETE、UPDATE的执行,获得X锁;对于UPDATE的标记,获得U锁;

读完即释放,并不持有至事务结束。

Repeatable Read

数据获得S锁

对于 INSERT、DELETE、UPDATE的执行,获得X锁;对于UPDATE的标记,获得U锁;

持有至事务结束

Serializable

数据获得S锁,同时获得Key-Range锁。

对于 INSERT、DELETE、UPDATE的执行,获得X锁;对于UPDATE的标记,获得U锁,同时获得Key-Range锁。

持有至事务结束

我们可以利用这些知识形象说明各个隔离级别下的数据一致性:

Read Uncommitted 级别

(1)脏读

SQL server事务进行行锁 sqlserver事务锁表_数据

(2)更新丢失

SQL server事务进行行锁 sqlserver事务锁表_事务隔离级别_02

(3)不可重复读

SQL server事务进行行锁 sqlserver事务锁表_事务隔离级别_03

(4)幻读

SQL server事务进行行锁 sqlserver事务锁表_SQL server事务进行行锁_04

Read Committed 级别

(1)脏读

SQL server事务进行行锁 sqlserver事务锁表_隔离级别_05

(2)更新丢失

SQL server事务进行行锁 sqlserver事务锁表_隔离级别_06

(3)不可重复读

SQL server事务进行行锁 sqlserver事务锁表_SQL server事务进行行锁_07

(4)幻读

SQL server事务进行行锁 sqlserver事务锁表_数据_08

Repeatable Read 级别

(1)脏读

SQL server事务进行行锁 sqlserver事务锁表_事务隔离级别_09

(2)更新丢失

SQL server事务进行行锁 sqlserver事务锁表_事务隔离级别_10

(3)不可重复读

SQL server事务进行行锁 sqlserver事务锁表_隔离级别_11

(4)幻读

SQL server事务进行行锁 sqlserver事务锁表_隔离级别_12

Serializable 级别

(1)脏读

SQL server事务进行行锁 sqlserver事务锁表_数据_13

(2)更新丢失

SQL server事务进行行锁 sqlserver事务锁表_数据_14

(3)不可重复读

SQL server事务进行行锁 sqlserver事务锁表_数据_15

(4)幻读

SQL server事务进行行锁 sqlserver事务锁表_SQL server事务进行行锁_16

我们从上图可以比较直观的看到以下的结论


 

脏读

更新丢失

不可重复读

幻读

Read Uncommitted

可能

可能

可能

可能

Read Committed

不可能

可能

可能

可能

Repeatable Read

不可能

不可能

不可能

可能

Serializable

不可能

不可能

不可能

不可能