一:背景

1. 讲故事

上一篇写完 SQLSERVER 的四个事务隔离级别到底怎么理解? 之后,有朋友留言问什么时候可以把 snapshot 隔离级别给补上,这篇就来安排,快照隔离级别看起来很魔法,不过在修车之前,得先看下怎么开车。

二:snapshot 隔离详解

1. snapshot 之前的困境

在了解 snapshot 之前先看看没有它会存在什么样的困境?还是用上一篇的 post 表做案例,参考sql 如下。


CREATE TABLE post(id INT IDENTITY,content char(3))
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');

大家都知道 SQLSERVER 的默认隔离级别是 READ COMMITTED,在下面的场景中 会话2 会被 会话1 阻塞


---- 会话1 ---- BEGIN TRAN
UPDATE post SET content='zzz' WHERE id=1 ---- 会话2 ---- BEGIN TRAN
SELECT * FROM post WHERE id=1;

那如何缓解呢?有一个粗暴的方法就是加 nolock 可以解决这个问题。


BEGIN TRAN
SELECT * FROM post (NOLOCK) WHERE id=1;

但加上 nolock 也不是一种完美的解决方案,如果 会话1 在后续操作中 ROLLBACK 了,那对 会话2 来说就是脏读,那如何解决 既要....又要.... 的问题呢?这就引入了 snapshot 隔离级别,接下来看下怎么玩的。

2. snapshot 的简单使用

要想使用 snapshot 隔离级别,需要打开数据库的 ALLOW_SNAPSHOT_ISOLATION 开关,为了方便测试,我们把数据库 删除重建。


DROP DATABASE MyTestDB
CREATE DATABASE MyTestDB
ALTER DATABASE MyTestDB SET ALLOW_SNAPSHOT_ISOLATION ON
USE MyTestDB
CREATE TABLE post(id INT IDENTITY,content char(3))
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的执行中设置快照隔离级别,参考 sql 如下:


SET TRAN ISOLATION LEVEL SNAPSHOT
BEGIN TRAN
SELECT * FROM post WHERE id=1;

从图中看果然解决了 既要 .... 又要 的问题,既没有阻塞,也没有脏读,哈。。。

3. snapshot 是什么原理

要探究个明白得从底层的数据页说起了,可以用 DBCC PAGEDBCC PAGE 命令观察。


DBCC TRACEON(3604)
DBCC IND(MyTestDB,post,-1)
DBCC PAGE(MyTestDB,1,240,3)

从图中可以看到,数据页上每一个 Slot 指向的表记录尾部会有一些空间来存储 Version Information 记录的版本信息,比如上面的 事务号时间戳,版本指针,目前看 Version Pointer: Null 指向的是 NULL,有了这些基础之后,重新将刚才的两个会话开启再次观察 240号 数据页。

从图中可以清晰的看到,会话1已经将内存页修改成了 zzz,会话2 读取的 aaa 肯定就是 3:8:0 指向的版本记录了。

有些朋友可能就有疑问了,这个 3:8:0 是怎么看出来的?其实就是记录中的 00000800 00000300 这一段,看不习惯的话可以用 windbg 附加一下。

接下来的一个问题是 3:8:0 到底指向的是哪里?如果看过 MSDN 上的说明应该知道它指向的是 TempDB 数据库,接下来用 DBCC PAGE 去看下是不是我的 aaa 记录。


DBCC PAGE(tempdb,3,8,2)

输出结果如下:


PAGE: (3:8)
Memory Dump @0x000000203ABF8000 000000203ABF8000: 01020000 2000fe00 00000000 00000100 00000000 .... ...............
000000203ABF8014: 00000100 07000080 451fb900 08000000 03000000 ........E...........
000000203ABF8028: 25000000 78010000 50000000 00000000 00000000 %...x...P...........
000000203ABF803C: 00000000 01000000 00000000 00000000 00000000 ....................
000000203ABF8050: 00000000 00000000 00000000 00000000 26010059 ................&..Y
000000203ABF8064: 0000008b 03000000 00010000 00000000 00050000 ....................
000000203ABF8078: 00000000 00000050 00000000 010b0000 00220000 .......P........."..
000000203ABF808C: 00815c00 00000000 00000000 00140000 0050000b ..\..............P..
000000203ABF80A0: 00010000 00616161 02000000 00000000 00000080 .....aaa............
000000203ABF80B4: 03000000 00000000 00000000 381f0000 00000000 ............8.......
...
000000203ABF9FF4: 00000000 0b01d000 be006000 ..........`. OFFSET TABLE: Row - Offset
0 (0x0) - 96 (0x60)

从右边的asc码看果然就是我的 aaa,如果大家对整个流程有点懵的话,画个图大概就像下面这样。

快照级别事务 的存储原理有了一定的认识之后,接下来从锁的角度观察下为什么能避开阻塞,将二个会话重新执行下,用 SQL Profile 观察下加锁过程。

从图中可以看的非常清楚, 会话1在1:240:0 记录上获取到了 X 锁,会话2 压根就没在表记录上附加任何锁,直接提取表记录的 Version Pointer 指向的 Slot,完美避开 X 锁,也就不存在锁互斥啦。。。

三:总结

从储存机制和加锁过程可以看到如下特点:

  • 开启 ALLOW_SNAPSHOT_ISOLATION 之后,每条记录都会有一个 版本信息,浪费了大量的数据页空间。

  • tempdb 是一个极其宝贵的服务器级别共享空间,被所有的数据库共享,遇到高并发的情况下可能会引发大量的 闩锁 等待造成的语句阻塞,所以一定要慎用,尽可能的减轻 tempdb 的负担。

SQLSERVER 快照隔离级别 到底怎么理解?的更多相关文章

  1. 数据库的快照隔离级别(Snapshot Isolation)

    隔离级别定义事务处理数据读取操作的隔离程度,在SQL Server中,隔离级别只会影响读操作申请的共享锁(Shared Lock),而不会影响写操作申请的互斥锁(Exclusive Lock),隔离级 ...

  2. 转:数据库的快照隔离级别(Snapshot Isolation)

    数据库的快照隔离级别(Snapshot Isolation)   隔离级别定义事务处理数据读取操作的隔离程度,在SQL Server中,隔离级别只会影响读操作申请的共享锁(Shared Lock),而 ...

  3. SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因

    原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...

  4. SQLServer 事务隔离级别与锁的申请和释放

    脏读:当一个事务开始更新数据,但是这个事务并没有完全提交,这个时候第二个事务开始读取数据,把第一个事务所更改的数据读了出来, 第二个事务读取的数据时临时的,因为有可能第一个事务最终有可能做回滚操作 不 ...

  5. Sqlserver事务隔离级别详解

    sqlserver存储方式   页    sqlserver是以页的形式存储数据,每个数据页的大小为8KB,sqlserver会把空间分为多个页,sqlserver与数据交互单位最小的io操作就是页级 ...

  6. Mysql隔离级别 sql示例理解

    前言 事务要解决的是多线程并发修改数据库的问题.Mysql innodb 引擎支持事务.类似 Java 中的各种锁,例如乐观锁(CAS),读写锁,悲观锁.事务也有很多级别. 每个隔离级别要解决的问题都 ...

  7. spring事务的隔离级别(透彻理解)

    1.spring 事务这个东西,是轮子,每个service,都需要用到.所以干脆就做在框架层实现. 2.spring是怎么给你的service方法加事务的呢?jdk动态代理,会针对每个service类 ...

  8. sqlserver默认隔离级别下并发批量update同一张表引起的死锁

    提到死锁,最最常规的场景之一是Session1 以排它锁的方式锁定A表,请求B表,session2以排它锁的方式锁定B表,请求A表之类的,访问顺序不一致导致死锁的情况本文通过简化,测试这样一种稍显特殊 ...

  9. SqlServer——事务—隔离级别

    隔离实际上是通过锁来实现的,作用于整个事务,它通常在事务开始前指定,如 SET TRANSACTION ISOLATION LEVEL READ Committed,指定后面的事务为 已提交读:而锁是 ...

  10. SQLServer 事务隔离级别

    MSSQL 事务级别 分类: 数据库2012-12-28 11:17 1050人阅读 评论(0) 收藏 举报 事务 级别 等级优化数据库 一个系统项目做大了,就会遇到性能问题.数据库的优化将是解决性能 ...

随机推荐

  1. scrapy传递 item时的 数据不匹配 和一些注意事项

    item 在传递数据时需要拷贝内存地址 yield scrapy.Request( url=title_url, callback=self.parse_detail, # 用深拷贝的方式 复制子对象 ...

  2. 【ELK解决方案】ELK集群+RabbitMQ部署方案以及快速开发RabbitMQ生产者与消费者基础服务

    前言: 大概一年多前写过一个部署ELK系列的博客文章,前不久刚好在部署一个ELK的解决方案,我顺便就把一些基础的部分拎出来,再整合成一期文章.大概内容包括:搭建ELK集群,以及写一个简单的MQ服务. ...

  3. RabbitMq发布确认

    RabbitMq发布确认 发布确认原理 生产者将信道设置成 confirm 模式,一旦信道进入 confirm 模式,所有在该信道上面发布的消息都将会被指派一个唯一的 ID(从 1 开始),一旦消息被 ...

  4. 2022-11-04 Acwing每日一题

    本系列所有题目均为Acwing课的内容,发表博客既是为了学习总结,加深自己的印象,同时也是为了以后回过头来看时,不会感叹虚度光阴罢了,因此如果出现错误,欢迎大家能够指出错误,我会认真改正的.同时也希望 ...

  5. tomcat报Address localhost:1099 is already in use

    idea运行tomcat报Address localhost:1099 is already in use 解决方案: 电脑桌面->ctrl+shift+esc 打开任务管理器,选择详细信息,找 ...

  6. 在 Solidity 中 ++i 为什么比 i++ 更省 Gas?

    前言 作为一个初学者,"在 Solidity 中 ++i 为什么比 i++ 更省 Gas?" 这个问题始终在每个寂静的深夜困扰着我.也曾在网上搜索过相关问题,但没有得到根本性的解答 ...

  7. 原来 GitHub 不仅能学代码,还有这些东西

    我是风筝,公众号「古时的风筝」,专注于 Java技术 及周边生态. 文章会收录在 JavaNewBee 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在里面. 大家好,我是风筝. 今天介绍几 ...

  8. vim快捷键及命令大全

    定位光标: G 将光标定位到文本末尾行首 gg 将光标定位到文本启始位置 0 (这个是零)定位到光标所在行行首 $ 定位到光标所在行行尾 数字G 跳转到第n行 移动光标: h 向左移动 l 向右移动 ...

  9. html网页图片加载失败的友好处理方式

    网络环境总是多样且复杂的,一张网页图片可能会因为网路状况差而加载失败或加载超长时间,也可能因为权限不足或者资源不存在而加载失败,这些都会导致用户体验变差,所以我们需要对图片加载失败时的情况进行一个弥补 ...

  10. 【实时数仓】Day02-DWD、DIM层数据准备:各层职能、行为日志DWD层、业务日志DWD层及分流(Phoenix和HBASE)

    一.需求分析及实现思路 1.分层需求 建立数仓目的:增加数据计算的复用性 可以从半成品继续加工而成 从kafka的ODS层(数据一开始就读到了kafka)读用户行为数据和业务数据,并写回到kafka的 ...