SQLSERVER 的四个事务隔离级别到底怎么理解?
一:背景
1. 讲故事
在有关SQLSERVER的各种参考资料中,经常会看到如下四种事务隔离级别。
- READ UNCOMMITTED
- READ COMMITTED
- SERIALIZABLE
- REPEATABLE READ
随之而来的是大量的文字解释,还会附带各种 脏读, 幻读, 不可重复读 常常会把初学者弄得晕头转向,其实事务的本质就是隔离,落地就需要锁机制,理解这四种隔离方式的花式加锁,应该就可以入门了,那如何可视化的观察 锁 过程呢?这里借助 SQL Profile 工具。
二:四种事务隔离方式
1. 测试数据准备
还是用上一篇创建的 post 表,脚本如下:
CREATE TABLE post(id INT IDENTITY,content char(4000))
GO
INSERT INTO dbo.post VALUES('aaa')
INSERT INTO dbo.post VALUES('bbb')
INSERT INTO dbo.post VALUES('ccc');
INSERT INTO dbo.post VALUES('ddd');
INSERT INTO dbo.post VALUES('eee');
INSERT INTO dbo.post VALUES('fff');
有了测试数据之后,我们按照隔离级别 高 -> 低 的顺序来观察吧。
2. SERIALIZABLE 事务
事务串行化 其实很好理解,如果要在 C# 中找对应那就是 ReaderWriterLock,读写事务是完全排斥的,接下来把 SQLSERVER 的隔离级别调整为 SERIALIZABLE。
SET TRAN ISOLATION LEVEL SERIALIZABLE
GO
BEGIN TRAN
SELECT * FROM dbo.post WHERE id=3
COMMIT
打开 profile,选择 lock:Acquired, lock:Released,SQL:StmtStarting 选项,开启观察。

从图中可以清楚的看到,SQLSERVER 直接对 post 附加了 S 锁,在 COMMIT 之后才真正的释放,在 S 锁期间, Insert 和 Update 引发的 X 锁是进不来的,所以就会存在相互阻塞的情况,也许这就是串行化的由来吧。
sqlserver 是一个支持多用户并发的数据库程序,如果锁粒度这么粗,必定给并发带来非常大的负面影响,不过文章开头的那三个指标 脏读, 幻读, 不可重复读 肯定都是不会出现的。
2. REPEATABLE READ 事务
什么叫 可重复读 呢?简而言之就是同一个 select 查询执行二次,不会出现记录修改的情况,在真实场景中两次 select 查询期间,可能会有其他事务修改了记录,如果当前是 REPEATABLE READ 模式,这是被禁止的,接下来的问题是如何落地实现呢?我们来看看 SQLSERVER 是如何做到的,参考sql 如下:
SET TRAN ISOLATION LEVEL REPEATABLE READ
GO
BEGIN TRAN
SELECT * FROM dbo.post WHERE id=3
COMMIT

这个图可能有些朋友看不懂,我稍微解释一下吧,数据库由数据页Page组成,数据页由记录RID 组成,有了这个基础就好理解了, SQLSERVER 会在事务期间把 1:489:0 也就是 id=3 这个记录全程附加 S 锁,直到事务提交才释放 S 锁,在事务期间任何对它修改的 X 锁都无法对其变更,从而实现事务期间的 可重复读 功能,如果大家不明白可以再琢磨琢磨。
这里有一个细节需要大家注意一下,可重复读 的场景下会出现 幻读 的情况,幻读就是两次查询出的结果集可能会不一样,比如第一次是 3 条记录,第二次变成了 5 条记录,为了方便理解我来简单演示一下。
- 会话1
SET TRAN ISOLATION LEVEL REPEATABLE READ
GO
BEGIN TRAN
SELECT * FROM dbo.post WHERE id >3
WAITFOR DELAY '00:00:05'
SELECT * FROM dbo.post WHERE id >3
COMMIT
- 会话2
在会话1 执行的 5s 期间执行 会话2 语句。
BEGIN TRAN
INSERT INTO dbo.post(content) VALUES ('gggggg')
COMMIT
稍等片刻之后,会发现多了一个 记录7 ,截图如下:

3. READ COMMITTED
提交读 是目前 SQLSERVER 默认的隔离级别,它是以不会出现 脏读 为唯一目标,何为脏读,简而言之就是读取到了别的事务未提交的修改数据,这个数据有可能会被其他事务在后续回滚掉,如果真的被其他事务 回滚 了,那你读到了这样的数据就是 错误 的数据,可能会给你的系统带来非常隐蔽的 bug,为了说明这个现象,我们用两个会话来测试一下帮助大家理解。
- 会话1
在这个会话中,将 id=3 的记录修改成 zzzzz
BEGIN TRAN
UPDATE dbo.post SET content='zzzzz' WHERE id=3
WAITFOR DELAY '00:00:05'
ROLLBACK
- 会话2
这个会话中,重复执行sql查询。
BEGIN TRAN
SELECT * FROM dbo.post WITH(NOLOCK) WHERE id =3 -- 脏读啦
WAITFOR DELAY '00:00:05'
SELECT * FROM dbo.post WITH(NOLOCK) WHERE id =3 -- 正确的数据
COMMIT

为了实现脏读这里加了 nolock 关键词,从图中明显的看到,获取的 zzzzz 数据是错误的,在一些和钱打交道的系统中是被严厉禁止的。
有了这些基础再理解 可提交读 可能会容易些,是不是很好奇 SQLSERVER 是如何实现的呢? 参考 sql 如下:
SET TRAN ISOLATION LEVEL READ COMMITTED
GO
BEGIN TRAN
SELECT * FROM dbo.post WHERE id =3
COMMIT

从加锁流程看,SQLSERVER 会逐一扫描数据页附加 IS 锁,扫完马上就释放,不像前面那样保持到 COMMIT 之后,如果找到记录所在的 Page 时,会对下面的所有记录附加 S 锁,这个时候 X 锁就进不来了,这就是它的实现原理,大家可以把刚才的 脏读 的sql中的 nolock 去掉试试看,两次读取结果都是一样的。
4. READ UNCOMMITTED
本质上来说 READ UNCOMMITTED 和 nolock 的效果是一样的,会引发脏读现象,主要是因为 READ UNCOMMITTED 根本就不会对表记录使用任何锁,参考sql如下:
SET TRAN ISOLATION LEVEL READ UNCOMMITTED
GO
BEGIN TRAN
SELECT * FROM dbo.post WHERE id =3
COMMIT
接下来观察 sqlprofile 的输出。

可以看到 READ UNCOMMITTED 只会对表和堆表结构这种架构附加锁,不会对表中记录附加任何锁,也就会引发 脏读 现象。
三:总结
其实 SQLSERVER 还有带版本的 SNAPSHOT 隔离级别,在真实场景中往往会给 TempDB 造成很大的压力,这里就不介绍了。
相信通过 Profile 观察到的加锁动态过程,会让大家有更深入的理解。
SQLSERVER 的四个事务隔离级别到底怎么理解?的更多相关文章
- mysql锁及四种事务隔离级别笔记
前言 数据库是一个共享资源,为了充分利用数据库资源,发挥数据 库共享资源的特点,应该允许多个用户并行地存取数据库.但这样就会产生多个用户程序并 发存取同一数据的情况,为了避免破坏一致性,所以必须提供并 ...
- mysql四种事务隔离级别
mysql事务并发问题 ACID什么的就不啰嗦了.mysql多个事务并发的时候,可能会出现如下问题: 1. 更新丢失 即两个事务同时更新某一条数据,后执行的更新操作会覆盖先执行的更新操作,导致先执行的 ...
- MySQL的四种事务隔离级别
本文实验的测试环境:Windows 10+cmd+MySQL5.6.36+InnoDB 一.事务的基本要素(ACID) 1.原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做 ...
- MySQL四种事务隔离级别详解
本文实验的测试环境:Windows 10+cmd+MySQL5.6.36+InnoDB 一.事务的基本要素(ACID) 1.原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做 ...
- [转载] MySQL的四种事务隔离级别
本文实验的测试环境:Windows 10+cmd+MySQL5.6.36+InnoDB 一.事务的基本要素(ACID) 1.原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做 ...
- Mysql InnoDB的四个事务隔离级别和(分别逐级解决的问题)脏读,不可重复读,虚读
MySqlInnoDB的事务隔离级别有四个:(默认是可重复读repeatable read) 未提交读 read uncommit : 在另一个事务修改了数据,但尚未提交,在本事务中SELECT语句可 ...
- MySQL的四种事务隔离级别【转】
本文实验的测试环境:Windows 10+cmd+MySQL5.6.36+InnoDB 一.事务的基本要素(ACID) 1.原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做 ...
- MySQL 四种事务隔离级别详解及对比--转
http://www.jb51.net/article/100183.htm 接的隔离级别.它的语法如下: ? 1 SET [SESSION | GLOBAL] TRANSACTION ISOLATI ...
- SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因
原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...
- Mysql 四种事务隔离级别
一.前提 时过一年重新拾起博文记录,希望后面都能坚持下来. 接着之前MySql的学习,先记录下这篇. 以下都是基于mysql8 innodb存储引擎进行分析的. 二.事务的ACID特性 A(Atomi ...
随机推荐
- 论文笔记 - An Explanation of In-context Learning as Implicit Bayesian Inference
这位更是重量级.这篇论文对于概率论学的一塌糊涂的我简直是灾难. 由于 prompt 的分布与预训练的分布不匹配(预训练的语料是自然语言,而 prompt 是由人为挑选的几个样本拼接而成,是不自然的自然 ...
- CodeQL(1)
前言 开始学习使用CodeQL,做一些笔记,可供参考的资料还是比较少的,一个是官方文档,但是Google翻译过来,总觉得怪怪的,另一个就是别人的一个资源整合,其中可供参考的也不是很多,大多也是官方文档 ...
- cookies和session总结
1.作为基础知识,但是也是容易被我们忽略的知识. 2.从我的一次面试中,面试官问到,session是什么?和cookies有什么关系,当时我以为很简单,便顺口回答到,session是为了解决http无 ...
- 前端学习 Nginx
前端学习 nginx 本篇主要讲解 nginx 常用命令.基础概念(正向/反向代理.负载均衡.动静分离.高可用).配置文件结构,并通过简单的实验来体验反向代理和负载均衡,最后说一下 nginx 原理. ...
- <一>关于运算符重载
C++的运算符重载:使对象的运算表现得和编译器内置类型一样 如下代码,如果T是整形,那很好理解,但是如果 T 是一个 Student 类, a + b ?怎么操作,两个学生类怎么相加? 这个就是我们要 ...
- 一文讲透CabloyJS全栈框架的来龙去脉
本文受众 咱们做软件开发,就好比是建造一幢幢房屋,一座座桥梁,既可以是北方宫殿的巍峨,也可以有南方庭院的雅致,更可以是横跨群山的峻险与孤悬.那么,不同的语言.不同的框架也都由其内在的秉质吸引着一批粉丝 ...
- JS逆向实战9——cookies DES加密混淆
cookie加密 DES 混淆 目标网站:aHR0cHM6Ly90bGNoZW1zaG9wLnlvdXpoaWNhaS5jb20vbWFpbi90ZW5kP05vdGljZUNhdGVJZD0xJk5 ...
- 【面试题总结】Java并发-多线程、JUC详述(思维导图)
〇.整体目录 一.多线程 1.实现方式 2.内存图 3.线程状态 4.实现线程同步 5.并发编程 二.JUC 1.概述与volatile关键字 2.ThreadLocal类 3.CAS方法 4.ato ...
- CyclicBarrier循环屏障源码解析(基于jdk11)
目录 CyclicBarrier循环屏障源码解析(基于jdk11) 1.1 CyclicBarrier概述 1.2 CyclicBarrier原理 1.2.1 基本结构(jdk11) 1.2.2 aw ...
- this关键字在JAVA和JS中的异同
this在JS中的用法 由于js中this 是在运行期进行绑定的,所以js中的 this 可以是全局对象.当前对象或者任意对象,这完全取决于函数的调用方式.JavaScript 中函数的调用有以下几种 ...