探秘空值位图掩码(NULL bitmap mask)
这篇文章我想谈下空值位图掩码(NULL bitmap mask) ,并揭开它的神秘面纱。空值位图掩码是在存储引擎层为列是否存储NULL值进行编码。它是一个简单的位图掩码,如果值是1的话,表示这列有NULL值,如果是0的话,表示这列有具体的值(非NULL值)。
这样的解释听起来非常直接,但当我们进一步分析时,用这个方法还是有启发性的。首先我们来看看数据行的组合结构,这是存储引擎用来在磁盘上存储记录的结构。结构如下图所示:
这个格式被称为定长变量格式(FixedVar format),因为SQL Server总是先存储定长列(像INT,CHAR),再存储变长列(像VARCHAR)。从图中我们可以看到,SQL Server以存储2 bytes的状态位开始,接着用2 bytes存储由行头到定长列结尾长度(包含所有定长列数据)。然后用2 bytes存储列个数,紧随其他的就是真正的空值位图掩码(NULL bitmap mask)。
所有的一切第一眼看起来都很合理,但我们再仔细看下的话,我们就开始思考,并且你可能会问:为什么SQL Server在每条数据行里存储具体的列数?对于每条数据行,列数都不是一样的么?为什么SQL Server要存储这些冗余的数据?
第1个答案是非常简单和有逻辑性的:SQL Server需要列数来计算用作实际空值位图掩码(NULL bitmap mask)的字节数。小于等于8列的表需要1 byte,9到16列需要 2 bytes,17到24列需要3 bytes,以此类推。明白了么?但在表里每条记录的列数必须是一样的!
我们来看第2个用实例分析的技术性正确答案:首先,你要知道空值位图掩码(NULL bitmap mask)是用在数据引擎级别,即当前记录中的列数。这就是说SQL Server在物理行可以存储不同数量的列。额,好像说的有点含糊不清…………物理数据行列数和表元数据层(sys.dolumns)里列数并不一致。这些也是SQL Server内部的真正不同层级。
因此在什么情况下这些层级间会彼此不相等呢?很简单:当你往表里增加列的时候!如果你加的列是NULL还是NOT NULL,SQL Server会作出完全不同的区别。当你增加一个新的NULL列到表时,SQL Server只更新表元数据层,一点也不接触到存储引擎层。也就是说当你增加一个NULL列时,所有的记录物理存储上不发生任何改变。另一方面,当你增加一个NOT NULL列时,SQL Server会更新表元数据层,同时也会更新存储引擎层,这就是说,SQL Server会接触并重写表里的每一条记录,在那里你增加了一个NOT NULL列。这会带来性能上的巨大区别!因此SQL Server需要在每条数据记录里存储具体的列数,因为这里的列数不能和表元数据层的列数同步。
我们来拿具体的例子来详细分析下。这个例子我们创建了一个简单的含8列的表,SQL Server需要使用1 bytes来作为空值位图掩码(NULL bitmap mask)。
CREATE TABLE TestTable
(
Column1 INT IDENTITY(1, 1) NOT NULL,
Column2 CHAR(600) NOT NULL,
Column3 CHAR(600) NOT NULL,
Column4 CHAR(600) NOT NULL,
Column5 CHAR(600) NOT NULL,
Column6 VARCHAR(600) NOT NULL,
Column7 VARCHAR(600) NOT NULL,
Column8 VARCHAR(600) NOT NULL
)
GO
然后,我们往表里插入2条记录:
INSERT INTO TestTable VALUES
(
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600)
),
(
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600)
)
GO
我们通过DBCC PAGE命令查看下具体的数据页:
DBCC IND(ALLOCATIONDB, TestTable, -1)
GO
DBCC TRACEON(3604)
GO
DBCC PAGE(ALLOCATIONDB, 1, 24993, 1)
GO DBCC TRACEON(3604)
GO
DBCC PAGE(ALLOCATIONDB, 1, 24995, 1)
GO
可以看到,每条记录的长度是 4129 bytes(4204 bytes 数据+ 7 bytes 行开销+ 2 bytes 变长列个数 + 3 * 2 bytes 每个变长列结束位置的偏移量)。
现在我们往表里加一个新的NULL列:
ALTER TABLE TestTable ADD Column9 CHAR(600) NULL
GO
这是表里的第9列,也就是说SQL Server对于这个列数需要2 bytes。但是 SQL Server并不在存储引擎层改变物理数据行,因为我们只加了一个NULL列。SQL Server不需要在存储引擎层做任何处理。我们可以通过查看数据页来验证下:
记录还是同样4219 bytes的长度,但是我们逻辑上已经在表上加了1列。现在我们来更新表的1条记录,这样的话,新加列就有具体值了:
UPDATE TestTable SET Column9 = REPLICATE('', 600)
WHERE Column1 = 1
GO
当你查看表里第2条记录的数据页时,记录大小还是原来的4219 bytes。
DBCC TRACEON(3604)
GO
DBCC PAGE(ALLOCATIONDB, 1, 24995, 1)
GO
你现在创建了一个场景:SQL Server在数据行内部存储了不同长度的空值位图掩码(NULL bitmap mask)。这就是说你有定长列的表,在存储引擎级别,却有不同长度的行大小!很有趣,是不是?
现在我们删除表并重建,继续往表里插入2条记录:
DROP TABLE dbo.TestTable CREATE TABLE TestTable
(
Column1 INT IDENTITY(1, 1) NOT NULL,
Column2 CHAR(600) NOT NULL,
Column3 CHAR(600) NOT NULL,
Column4 CHAR(600) NOT NULL,
Column5 CHAR(600) NOT NULL,
Column6 VARCHAR(600) NOT NULL,
Column7 VARCHAR(600) NOT NULL,
Column8 VARCHAR(600) NOT NULL
)
GO INSERT INTO TestTable VALUES
(
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600)
),
(
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600),
REPLICATE('', 600)
)
GO
现在我们往表里增加一个NOT NULL列:
ALTER TABLE TestTable ADD Column9 CHAR(600) NOT NULL
DEFAULT REPLICATE('', 600)
GO
现在,SQL Server需要在存储引擎层改变每条记录,因为新列的默认值必须被增加(当你表里已经有记录存储时,新加列必须要定义一个默认值),而且SQL Server需要扩展空值位图掩码(NULL bitmap mask)。
DBCC IND(ALLOCATIONDB, TestTable, -1)
GO DBCC TRACEON(3604)
GO
DBCC PAGE(ALLOCATIONDB, 1, 24993, 1)
GO DBCC TRACEON(3604)
GO
DBCC PAGE(ALLOCATIONDB, 1, 24995, 1)
GO
当你处理大表,给表增加NOT NULL列时,这个现象会导致严重的性能问题。想象下我们往表里插入100万条记录。当我们增加NULL列时,SQL Server只需要几毫秒,因为只进行元数据修改操作。但当我们往表里增加NOT NULL列时,SQL Server待ALTER TABLE操作完成需要花费40秒!在处理大表,往表里增加NOT NULL列,这的确是个非常严重的性能降级!!
希望你现在已经理解了为什么SQL Server在存储引擎层对每条记录存储具体的列数,还有在SQL Server里,当你往大表里增加NOT NULL列,会出现严重的性能问题。
参考文章:
https://www.sqlpassion.at/archive/2011/06/29/the-mystery-of-the-null-bitmap-mask/
探秘空值位图掩码(NULL bitmap mask)的更多相关文章
- Flash 矢量图和位图性能对比 导出为位图/缓存为位图 export as bitmap / cache as bitmap
大家都知道Flash处理矢量图比位图要慢,而具体的性能上对比也有不少的前人已经做过.http://bbs.9ria.com/forum.php?mod=viewthread&tid=2282 ...
- 25-12 空值处理(null值)
--------------------空值处理--------------------- select * from TblStudent --查询所有年龄是null的同学学习信息 --null值无 ...
- JS 中判断空值 undefined 和 null
1.JS 中如何判断 undefined JavaScript 中有两个特殊数据类型:undefined 和 null,下节介绍了 null 的判断,下面谈谈 undefined 的判断. 以下是不正 ...
- SQL Server 存储(2/8):理解数据记录结构
在SQL Server :理解数据页结构我们提到每条记录都有7 bytes的系统行开销,那这个7 bytes行开销到底是一个什么样的结构,我们一起来看下. 数据记录存储我们具体的数据,换句话说,它存在 ...
- SQL Server :理解数据记录结构
原文:SQL Server :理解数据记录结构 在SQL Server :理解数据页结构我们提到每条记录都有7 bytes的系统行开销,那这个7 bytes行开销到底是一个什么样的结构,我们一起来看下 ...
- SQLSERVER中NULL位图的作用
SQLSERVER中NULL位图的作用 首先感谢宋沄剑提供的文章和sqlskill网站:www.sqlskills.com,看下面文章之前请先看一下下面两篇文章 SQL Server误区30日谈-Da ...
- bitmap位图法
位图法定义 位图法就是bitmap的缩写,所谓bitmap,是用每一位来存放某种状态,适用于大规模数据,但数据状态又不是很多的情况.通常是用来判断某个数据存不存在的. 例如,要判断一千万个人的状态,每 ...
- Android Bitmap(位图)详解
一.背景 在Android开发中,任何一个APP都离不开图片的加载和显示问题.这里的图片来源分为三种:项目图片资源文件(一般为res/drawable目录下的图片文件).手机本地图片文件.网络图片资源 ...
- Android笔记--Bitmap(三) 针对不用Android版本的位图管理
Bitmap(三) | Android不同版本的相应操作 在不同的Android版本中.位图的存储方式是不同的. 1.小于等于 Android 2.2 (API level 8) 垃圾收集器回收内存时 ...
随机推荐
- SQL Server如何提高数据库备份的速度
对于一个数据库完整备份来说,备份的速度很大程度上取决于下面两个因素:读磁盘数据.日志文件的吞吐量,写磁盘数据文件的吞吐量. 下图是备份过程中磁盘的变化情况: 读吞吐量 读吞吐量的大小取决于磁盘读取数据 ...
- jxl 2.6.12 与 jxl 2.3.0 稳定版性能比较
jxl 2.3.0 org.jxls:jxls:2.3.0 org.jxls:jxls-poi:1.0.9 org.jxls:jxls-jexcel:1.0.6 jxls-reader:2.0.2 j ...
- [leetcode]Find Minimum in Rotated Sorted Array @ Python
原题地址:https://oj.leetcode.com/problems/find-minimum-in-rotated-sorted-array/ 解题思路:话说leetcode上面的二分查找题目 ...
- Windows技巧 - 右键菜单【在此处打开bash】
实现效果如下图: 步骤如下:按住shift,在文件夹空白处(桌面也可以)鼠标右键,即可看到 Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROO ...
- 外包采用Gradle生成多套app打包
目的:可修改app名称.icon.包名.接口地址及其它 一. 修改基本配置(包名.版本号等) 配置module下的build.gradle 添加productFlavors例如: produ ...
- MVC的JsonResult用法
在Asp.net Mvc 2中由于对数据的保护,默认情况下request为post,所以在前端请求的时候则需要以post方式request action方法: public JsonResult Ge ...
- Notes on how to use Webots, especially how to make a robot fly in the air
How to create a new project Wizard - New project directory Scene Tree Scene tree is a representati ...
- A simple visualization of energy function and energy gap in hopfield nets
- 在 Excel 中使用正则表达式进行查找与替换
在 Excel 中,使用 Alt+F11 快捷键打开 VBA 项目窗口,在左侧的工作表名称上点右键,选择查看代码,即可出出现右侧的代码编辑窗口 在代码窗口中输入以下代码: Private Sub Re ...
- mongodb_查询操作使用_条件查询、where子句等(转)
<?php /* mongodb_查询操作使用_条件查询.where子句等(转并学习) 1.find()/findOne() mongodb数据库的查询操作即使用find()或者findO ...