- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
上一篇写完 SQLSERVER 的四个事务隔离级别到底怎么理解? 之后,有朋友留言问什么时候可以把 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 隔离级别,接下来看下怎么玩的.
要想使用 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;
从图中看果然解决了 既要 .... 又要 的问题,既没有阻塞,也没有脏读,🐂哈。。.
要探究个明白得从底层的数据页说起了,可以用 DBCC PAGE 和 DBCC 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快照隔离级别到底怎么理解?的文章就讲到这里了,如果你想了解更多关于SQLSERVER快照隔离级别到底怎么理解?的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
在我的 Windows 类库(由 MVC 网站使用)中,我安装了 NugetPackage Microsoft.SqlServer.Types (Spatial)。 现在,我正在使用 ado.net
测试sql: 复制代码代码如下: SET STATISTICS IO ON SET STATISTICS TIME ON SELECT COUNT(1)&n
我正在从 SqlConnection 构建 DbContext。当我使用它时,我收到以下错误: The Entity Framework provider type 'System.Data.Enti
我使用dotNet 4.5创建了WCF服务。数据库层是使用Entity Framework 6构建的。 我使用IIS 8托管了该服务。它运行正常。 现在,我需要使用Windows窗体客户端使用该服务,
我正在尝试从 SqlServer 1 上的 sql 数据库中导出一些表。在我们的内部网 LAN(就在我旁边)中有一个我制作的临时 Sql Server,称为 SqlServer 2。 我不想备份整个数
Error 1 Copying file bin\EntityFramework.SqlServer.xml to obj\Debug\Package\PackageTmp\bin\Entit
我正在尝试使用 SMO 通过 Powershell 恢复数据库,但是当我尝试定义和使用服务器对象时,出现以下错误: Cannot convert argument "srv", with value:
出于某种原因,我需要将我的表列之一从“NOT NULL”更新为“NULL”。命令很简单: ALTER TABLE TBLOGDOCMESSAGE ALTER COLUMN PROCESSID BIGI
我想知道我正在尝试做的事情是否可行。我相信它是在 TSQL 中使用 PIVOT 函数,但对 PIVOT 函数没有足够的经验来知道从哪里开始。 基本上,我正在尝试采用以下名为 #tmpbudgetdat
我正在尝试将子查询作为带有条件的列。 我从 SQL Server 得到的错误是: 子查询返回了 1 个以上的值。当子查询跟随 =、!=、、>= 或当子查询用作表达式时,这是不允许的。 我正在选择更多的
我有一个正在查询的 SQL 服务器数据库,我只想在特定行为空时获取信息。我使用了一个 where 语句,例如: WHERE database.foobar = NULL 它不返回任何东西。但是,我知道
1、拼接字符串(整个字符串不分割)步骤: 首先在字符串的前后加单引号; 字符串中的变量以'''+@para+'''在字符串中表示; 若在执行时存在类型转换错误,则应用相应的类型
说明: 收缩日志的原因有很多种,有些是考虑空间不足,有些则是应用程序限制导致的。 ?
Rand()函数是系统自带的获取随机数的函数,可以直接运行select rand() 获取0~1之间的float型的数字。 如果想要获取0~100之间的整数随机数,可以这样使用 select ro
将以下内容保存为 openSql.bat 双击运行即可 复制代码 代码如下: @echo ========= SQL Server Ports =================== @ech
新增的APPLY表运算符把右表表达式应用到左表表达式中的每一行。它不像JOIN那样先计算那个表表达式都可以,APPLY必选先逻辑地计算左表达式。这种计算输入的逻辑顺序允许吧右表达式关联到左表表达式。
SQL Server本身提供了这么一个DMV来返回这些信息,它就是sys.dm_exec_sessions 。 比如在我的机器上做一下查询: 复制代码 代码如下: SELECT * FROM
尽管从技术上讲,其它排名函数的计算与ROW_NUMBER类似,但它们的的实际应用却少很多。RANK和DENSE——RANK主要用于排名和积分。NTILE更多地用于分析。 先创建一个示例表: 复制
1、文件和文件组的含义与关系 每个数据库有一个主数据文件.和若干个从文件。文件是数据库的物理体现。 文件组可以包括分布在多个逻辑分区的文件,实现负载平衡。文件组允许对文件进行分组,以便于管理和数据的
我想很多人都知道,在oracle里面,存储过程里面可以传入数组(如int[]),也就是说,可以传多条记录到数据,从而一起更新。减少数据库的请求次数。 但SqlServer呢?bulk Insert这
我是一名优秀的程序员,十分优秀!