对于大于8046 bytes的行,RCSI/SI事务隔离级别无效

自SQL Server 2005起,我们有了READ COMMITTED SNAPSHOT ISOLATION level (RCSI) 和SNAPSHOT ISOLATION level (SI)两个事务隔离级别。当你使用这些事务隔离级别时,读操作(SELECT语句)在读的时候不需要S锁(共享锁),写操作(UPDATE,DELETE语句)会对记录进行版本控制,这些改变会写入TempDb。它们就会生成一个版本链,记录的最新版本(存在数据库里的数据页里)指向存在TempDb里的页,下图可以帮助我们理解这个情况。

为了使这个机制有效,SQL Server需要在数据库内部的数据页上的每条记录,增加14 bytes长的指针。这就是说,每条记录增加了14 bytes的长度。或许你已经知道,当你使用定长数据类型时,SQL Server内部的记录长度不能超过8060 bytes。这就意味着,当你启用RCSI/SI隔离级别时,会导致记录超过现有的8060 bytes。我们来看一个简单的例子:

 1 USE master
 2 GO
 3
 4 -- Create a new database
 5 CREATE DATABASE VersionStoreRestrictions
 6 GO
 7
 8 -- Enable RCSI
 9 ALTER DATABASE VersionStoreRestrictions SET READ_COMMITTED_SNAPSHOT ON
10 GO
11
12 -- Use it
13 USE VersionStoreRestrictions
14 GO
15
16 -- Create a table where each record is 8047 bytes large
17 CREATE TABLE TableB
18 (
19    Column1 CHAR(40),
20    Column2 CHAR(8000)
21 )
22 GO

从代码里我们可以看到,这里我创建了带2个CHAR列,总长为8040 bytes的表。SQL Server为每条记录内部需要至少7 bytes的开销。这里数据页上的1条记录需要8047 bytes。因为我们在数据库级别启用了RCSI数据隔离级别,SQL Server需要增加额外的14 bytes作为行版本指针(Row Version Pointe),这就把表里的每条记录长度扩展到8061 bytes。对于SQL Server来说,这就意味着每条记录太长了(多出1 byte)。我们在表里插入1条记录看看:

1 -- Insert a initial row
2 INSERT INTO TableB VALUES (REPLICATE(‘A‘, 40), REPLICATE(‘A‘, 8000))
3 GO

现在当你尝试去更新这个记录(SQL Server尝试在TempDb里对这条记录进行版本控制),SQL Server会报下列错误:

1 UPDATE TableB
2 SET Column1 = REPLICATE(‘B‘, 40)
3 GO

这个错误信息非常有意义,因为数据库上下文信息是错误的(SSMS显示你还在master数据库)。但是当你在UPDATE语句加上表架构时,你就能拿回实际的错误信息:

1 UPDATE VersionStoreRestrictions.dbo.TableB
2 SET Column1 = REPLICATE(‘B‘, 40)
3 GO
4  

哇噢,这是个内部错误,因为SQL Server使用的缓存只有8060 bytes 大,现在我们尝试在那个缓存里保存8061 bytes——瞧!这在SQL Server内部是个bug!你可以在自SQL SERVER 2005以后的版本里验证这个BUG,也就说,这个BUG已经存在好几年了(SQL Server 2012已经修正这个BUG,但在页里面的确存储了预期的8061 bytes,我测试的版本是SQL Server 2008R2)。

当你对数据库启用RCSI/SI数据隔离级别时,你就要留意这个BUG了,因为这意味这RCSI/SI在任何情况下都无效了。当在你的数据库里有1个表超过8046 bytes限制,那你真的是有麻烦了!通过这个危险的BUG(nasty bug),你也会理解,知道SQL Serve内部架构和内部如何存储数据是多么重要!!

时间: 2024-10-26 11:51:47

对于大于8046 bytes的行,RCSI/SI事务隔离级别无效的相关文章

设置SQLServer的行版本控制隔离级别

1.--查询数据库状态 select name,user_access,user_access_desc,snapshot_isolation_state,snapshot_isolation_state_desc,is_read_committed_snapshot_on from sys.databases 2. 查看当前数据库的隔离级别 DBCC Useroptions -- isolation level 这项的值就代表当前的隔离级别 2. 更改数据库与乐观锁有关的参数 (必须关闭除了当

【转修正】sql server行版本控制的隔离级别

在SQL Server标准的已提交读(READ COMMITTED)隔离级别下,一个读操作会和一个写操作相互阻塞.未提交读(READ UNCOMMITTED)虽然不会有这种阻塞,但是读操作可能会读到脏数据,这是大部分用户不能接受的.有些关系型数据库(例如Oracle)使用的是另一种处理方式.在任何一个修改之前,先对修改前的版本做一个复制[WX1] ,后续的一切读操作都会去读这个复制的版本,修改将创建一个新的版本.在这种处理方式下,读.写操作不会相互阻塞.使用这种行版本控制机制的好处,是程序的并发

关于ORACLE的串行化隔离级别--来自ORACLE概念手册

为了描述同时执行的多个事务如何实现数据一致性,数据库研究人员定义了被 称为串行化处理(serializability)的事务隔离模型(transaction  isolation model).当所有事务都采取串行化的模式执行时,我们可以认为同一时间只有 一个事务在运行(串行的),而非并发的 以串行化模式对事务进行隔离的效果很好,但在此种模式下应用程序的效率将 大大降低.将并行执行的事务完全隔离意味着即便当前只存在一个对表进行查 询(query)的事务,其他事务 也不能再对此表进行插入(inse

PostgreSQL串行化隔离级别(SSI)的能力与实现

https://zhuanlan.zhihu.com/p/37087894 PostgreSQL9.1是第一个采用Serializable Snapshot Isolation(SSI)实现串行化隔离级别的生产级数据库. 本文的目标是学习与分析SSI的设计思路,以及在PG中的实现与优化.首先介绍了隔离级别以及实现其的两个基本并发控制机制,给出了PG的SI未达到串行化的案例,分析原因并给出直观的解决思路,其次阐述了SSI的技术思路与关键环节,最后就PG内核中SSI的实现与优化思路进行了分析. 1.

可串行化隔离级别里的锁升级

在今天的文章里我会讨论下可串行化(SERIALIZABLE)隔离级别里会有的锁升级(Lock Escalations),还有你如何避免.在上个月的7月14日,我已经介绍了SQL Server里锁升级(Lock Escalations)的基本概念还有为什么需要它们.因此请你回到这个文章来理解下这个非常重要的概念. 可串行化(SERIALIZABLE)隔离级别 可串行化(SERIALIZABLE)隔离级别用来阻止所谓的幻影记录(Phantom Records).为了阻止它们,SQL Server使用

CentOS7.2 - 安装vnc,最小化安装vnc,命令行安装vnc,3级别安装vnc

系统版本: CentOS Linux release 7.2.1511 (Core)  内核版本: 3.10.0-327.el7.x86_64 系统架构: x64位 版权作者:[email protected] 编写时间: #最小化安装(level 3)安装VNC #centos7.2安装vnc,最小化安装vnc,命令行安装vnc,3级别安装vnc systemctl stop firewalld systemctl stop iptables.service setenforce 0 #依赖包

RR隔离级别下锁情况(探究gap锁和行锁)

!!!我的数据库演示版本为5.5,以后会追加最新数据库的演示版本 间隙锁(GAP Lock)时InnoDB在可重复读下的隔离级别下为了解决幻读问题引入的锁机制.幻读存在的问题是因为在新增或者更新时如果进行查询,会出现不一致的现象,这时单纯的使用行锁无法满足我们的需求,我们需要对一定范围的数据加锁,防止幻读. 可重复读隔离级别就是数据库通过行锁和间隙锁(不要搞混gap lock和next-key lock)共同组成来实现的,在网上抄了一下加锁的规则,然后自己进行一下验证: 1.加锁的基本单位是(n

laravel命令行config:publish对workbench包无效

http://www.zroran.com/it/php/laravel/8.html 当我在学习laravel扩展包开发的时候遇到个蛋疼的问题,按照手册中的教程,开发阶段就在workbench中创建的包,而这时看到手册中关于级联配置这块,通过php artisan config:publish vendor/package 可以将包里的默认配置文件复制到app/config/packages/vendor/package 目录中,但是当我执行这条命令的时候报错:  [InvalidArgume

MySql命令行控制事务

新建表t1 CREATE TABLE `t1` (   `a` int(11) NOT NULL,   `b` int(11) DEFAULT NULL,   PRIMARY KEY (`a`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 查看mysql系统级别的事务隔离级别: mysql> SELECT @@global.tx_isolation; +-----------------------+ | @@global.tx_isolation | +--