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

本文涉及的产品
云数据库 RDS SQL Server,独享型 2核4GB
简介:

自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内部架构和内部如何存储数据是多么重要!! 


本文转自Woodytu博客园博客,原文链接:http://www.cnblogs.com/woodytu/p/4578564.html,如需转载请自行联系原作者

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情: https://www.aliyun.com/product/rds/sqlserver
相关文章
|
3天前
|
存储 网络协议 C语言
一文带你秒懂 字节序(byte order),比特序(bit order),位域(bit field)
一文带你秒懂 字节序(byte order),比特序(bit order),位域(bit field)
55 0
2 字节的 UTF-8 序列的字节 2 无效 解决方法
2 字节的 UTF-8 序列的字节 2 无效 解决方法: 用记事本打开xml文件,另存为 编码 选择 UTF-8,保存替换掉之前的文件,解决问题博客内容仅代表个人观点,如发现阐述有误,麻烦指正,谢谢!
4062 0
|
3天前
|
关系型数据库 MySQL
innodb_buffer_pool_size 配置文件设置的值和查询的值怎么不一致
您可以配置缓冲池大小 脱机或在服务器运行时。中描述的行为 本节适用于这两种方法。更多信息 关于在线配置缓冲池大小,请参阅在线配置 InnoDB 缓冲池大小。InnoDB 当增加或减少innodb_buffer_pool_size时, 操作以块的形式执行。块大小由 innodb_buffer_pool_chunk_size 配置选项定义,该选项的缺省值为 。有关更多信息,请参阅配置 InnoDB 缓冲池区块大小。128M 缓冲池大小必须始终等于 innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances 的倍数或倍数。 如果将in
|
3天前
|
关系型数据库 Shell OceanBase
您的ulimit参数"max user processes"的当前值为4096,而OceanBase安装OCP 4.2.1时要求该值不能小于655350
您的ulimit参数"max user processes"的当前值为4096,而OceanBase安装OCP 4.2.1时要求该值不能小于655350
111 2
|
7月前
|
存储 Go
1bit等于多少字节?换算方法详解
1bit等于多少字节?换算方法详解
271 0
|
7月前
|
网络安全
解决访问项目时发生SSL 接收到一个超出最大准许长度的记录。 错误代码:SSL_ERROR_RX_RECORD_TOO_LONG的错误~
解决访问项目时发生SSL 接收到一个超出最大准许长度的记录。 错误代码:SSL_ERROR_RX_RECORD_TOO_LONG的错误~
651 0
LeetCode 1346. 检查整数及其两倍数是否存在 Check If N and Its Double Exist
LeetCode 1346. 检查整数及其两倍数是否存在 Check If N and Its Double Exist
|
SQL 关系型数据库 MySQL
MGR修改max_binlog_cache_size参数导致异常
MGR修改max_binlog_cache_size参数导致异常
137 0
MGR修改max_binlog_cache_size参数导致异常
|
小程序
小程序Error: 系统错误,错误码:80200,main package source size 2590KB exceed max limit 2MB
小程序Error: 系统错误,错误码:80200,main package source size 2590KB exceed max limit 2MB
1292 0
小程序Error: 系统错误,错误码:80200,main package source size 2590KB exceed max limit 2MB
|
存储 中间件
中间件事务码R3AC1里Block Size的含义
在中间件事务码R3AC1可以为一个中间件的适配器对象维护Block size的大小。
131 1
中间件事务码R3AC1里Block Size的含义