sql server 索引阐述系列二 索引存储结构
"流光容易把人抛,红了樱桃,绿了芭蕉“ 转眼又年中了,感叹生命的有限,知识的无限。在后续讨论索引之前,先来了解下索引和表数据的内部结构,这一节将介绍页的存储,页分配单元类型,区的存储, 最后简要介绍下系统页存储类型,页中的数据结构。
1.1 页存储
页是 sql server存储数据的基本单位,大小为8kb, 它存储的类型包括表,索引数据,分配位图,可用空间信息等,页也是可以读写的最小I/0单位。也就是如只需访问一行数据,也会把整个页加载到内存中。一页大小是8192个字节,由三块组成分为页头,数据行 , 行偏移也叫页尾行指针。
页头:有96个字节,包含的信息如PageID(文件号及页面编号),NextPage下一个页面的文件号及页面编号等。
数据行:单个数据行最大为8060字节。
末尾行偏移: 36字节的数组,数组中的每一个值指向页中数据。当插入一行数据时,偏移量从右下角开始存储,插入第二行时偏移量为2存放于1的左边,如下图所示:
一个页存储有三种分配单元类型: in_row_data (行内数据), row_overflow_data( 行溢出数据), lob_data LOB(大型数据)。
分配单元类型 | 描述 |
in_row_data | 存放固定长度的列,以及可变长度的列。一行字节<= 8060字节的限制 |
row_overflow_data | 存放<=8000字节的varchar,nvarchar, varbinary类型数据。一行字节>= 8060 |
lob_data | 存放大型对象数据类型值 >=8000节字的varchar(max), nvarchar(max), varbinary(max), 以及xml或clr udt |
1.2 区
区是由8个物理上连续的页组成的单元,用来有效管理页,如是区内的8个页属于同一个表,则这种区叫统一区,如果区内8个页分别属于至少两个不同的表,则这种区叫混合区。当表或索引需要更多空间时,sqlserver会为对象分配一个新区,但对象不足64kb,通常只分配一个页,当drop或truncate表时将释放整个区。
1.3 存储的关系
每个数据表对应有一个objectID标识符, 表中有多个索引为 sys.indexes, 表和索引存储在多个分区中 sys.partitions,但至少有一个分区。每个分区下面有8个物理连续页, 页中使用最频繁的文件类型是Data数据。
下图展示表和索引存储结构以及对应的元数据查询。 分配单元可通过SELECT * FROM sys.system_internals_allocation_units视图查看
二. 索引与表存储关系演示
2.1 首先建一个RowText表
CREATE TABLE [dbo].[RowText]([a] [varchar](3000) NULL ) ON [PRIMARY]
2.2 根据表名,查到表的标识符objectID: 5575058。 这里显示了表的相关信息如创建时间,修改时间。
select * from sys.objects where name='RowText'
2.3 根据标识符objectID,找到表上的多个分区,这里只有一个区。找到标识hobt_id(是该区里堆或B树结构的标识): 72057594038976512
select * from sys.partitions where object_id=5575058
2.4 再根据hobt_id: 72057594038976512,找到页分配的单元, 这里现只显示了in_row_data 行内数据,信息包括了行内数据使用的总页数,已使用页数,数据页数
select * from sys.allocation_units where container_id=72057594038976512
三. 页分配单元演示
3.1 上面讲到页分配单元有三种类型,通过下面的脚本可以来查看分析。
SELECT CONVERT(VARCHAR(10),OBJECT_NAME(i.object_id)) AS table_name,i.Name AS index_name,i.index_id,i.type_desc AS index_type,partition_id,partition_www.120xh.cn number,p.rows,allocation_unit_id,a.type_desc AS www.yongshiyule178.com page_type_desc,a.total_pages,a.used_pages,a.data_pagesFROM sys.indexes www.baidu620.com i JOIN sys.partitions p ON i.object_id=p.object_id and i.index_id=p.index_id JOIN sys.allocation_www.leyou2.net units a www.leyouzaixan.cn ON p.partition_id=a.container_id WHERE i.object_id=object_id('RowText')
3.2 查看页存储的行内数据
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[RowText]') AND type in (N'U')) DROP TABLE [dbo].[RowText]go CREATE TABLE [dbo].[RowText]([a] [varchar](3000) NULL ) ON [PRIMARY]
insert into RowText select REPLICATE('a',3000) insert into RowText select REPLICATE('a',3000) insert into RowText select REPLICATE('a',3000)
当前一行数据3000字节,小于一行8000字节,所以存放为IN_ROW_DATA类型,新增三行共9000字节,大于了一页8060字节,所以产生了data_pages:2页。通过3.1脚本查看
3.2 查看页存储的行溢出数据
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[RowText]') AND type in (N'U')) DROP TABLE [dbo].[RowText] go create TABLE [dbo].[RowText]([a] [varchar](3000) NULL,[b] [varchar](6000) NULL ) ON [PRIMARY]
insert into RowText select REPLICATE('a',3000),REPLICATE('b',6000)
当前一行数据为9000字节,大于一行8000字节,所以分配了ROW_OVERFLOW_DATA类型,通过3.1脚本查看
3.3 查看页存储的LOB大型数据
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[RowText]') AND type in (N'U')) DROP TABLE [dbo].[RowText] go CREATE TABLE [dbo].[RowText]([a] [varchar](3000) NULL,[b] [varchar](6000) NULL,[c] [text] NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
insert into RowText select REPLICATE('a',3000),REPLICATE('b',6000),REPLICATE('c',12000)
当前表包含了text类型,所以分配了LOB_DATA大型数据,通过3.1脚本查看
最后简单总结下:在设计表字段时,一定要与业务相符合,尽量避免建大型字段,产生行溢出和LOB大型数据,从而造成页拆分,查询将需要跨页。原理是当一行超过8060字节时,这类型的值将被移动到一个称为行溢出分配单元页中,在原始页上保留一个24字节指针,指向行外数据,这样就完成了行跨页。 存放大型对象数据类型值时,在原始页上保留一个16字节指针,指向该大型对象值。
四 页单元文件类型
我们知道页文件主要是用来存储表和索引的数据,其实还包括其它的存储, 在sqlserver里页文件存储的类型也分9种,如下图所示
页类型 |
内容 |
Global Allocation Map (GAM) |
它用来标识相应的区是否已经被分区,能标识64000个区,也就是4G空间,如超过4G则分配另一个GAM页 bit=0 表示当前区已被数据使用 |
.Shared Global Allocation Map(SGAM) |
表示该区是否是混合区 bit=1 是混合区且至少有一个数据页被分配 |
Page Free Space |
存储该文件里所有页分配情况,和可用空间信息 |
Index Allocation Map |
表或索引使用该区的信息 |
Bulk Changed Map |
最后一条 backup log 语句之后大容量操作所修改区的信息 |
Differential Changed Map |
最后一条 backup database 语句之更改区的信息 |
Data |
用来存储数据 |
Row Overflow Page |
行溢出的数据 |
LOB |
存放大型对象数据:text,nvarchar(max),varchar(max)… |
五.数据页的结构
在sql server里有这么多页文件类型,哪个页里是怎么存储的,存储了什么内容呢?下面来查看一个数据页存储的信息
下面通过partition_id的标识符:72057594038976512 找到first_page 字段 第一页
select * from sys.system_internals_allocation_units where container_id=72057594038976512
由于存储关系,页码是以字节16进制显示0x720000000100,这里需要倒序后就是0x010000000072,
0x表示:文件组号
0072从16进制转成10进制为第114页(后四字节代表页的编号)
通过Dbcc page可以查看页的具体内容,使用之前dbcc page, 先打开dbcc traceon (3604) 告诉sql server将结果返回给客户端 ,dbcc Page 四个参数 如下:
dbid | dbname 该页的数据库ID或数据
Filenum 页面文件号
Pagenum 页面号
Printopt =1 对每条记录打印缓冲报头,页面报头,输出行偏移表
具体使用如下:
dbcc traceon (3604)
dbcc page('test',1,114,1)
数据页结构输出分为四个主要阶段:buffer, page header, data, offset table
Buffer: 用来标识该数据页在内存中的位置。
page header: 页头信息
data:存储的数据
offset table: 偏移量
感兴趣的朋友,可以再深入研究。这里粗略讲下。
sql server 索引阐述系列二 索引存储结构相关推荐
- sql server 索引阐述系列五 索引参数与碎片
原文:sql server 索引阐述系列五 索引参数与碎片 -- 创建聚集索引 create table [dbo].[pub_stocktest] add constraint [pk_pub_st ...
- SQL Server调优系列进阶篇(如何维护数据库索引)
前言 上一篇我们研究了如何利用索引在数据库里面调优,简要的介绍了索引的原理,更重要的分析了如何选择索引以及索引的利弊项,有兴趣的可以点击查看. 本篇延续上一篇的内容,继续分析索引这块,侧重索引项的日常 ...
- SQL Server 查询性能优化——覆盖索引(二)
在SQL Server 查询性能优化--覆盖索引(一) 中讲了覆盖索引的一些理论. 本文将具体讲一下使用不同索引对查询性能的影响. 下面通过实例,来查看不同的索引结构,如聚集索引.非聚集索引.组合索 ...
- SQL Server调优系列玩转篇三(利用索引提示(Hint)引导语句最大优化运行)
SQL Server调优系列玩转篇三(利用索引提示(Hint)引导语句最大优化运行) 原文:SQL Server调优系列玩转篇三(利用索引提示(Hint)引导语句最大优化运行) 前言 本篇继续玩转模块 ...
- SQL Server 2016新特性:列存储索引新特性
SQL Server 2016新特性:列存储索引新特性 行存储表可以有一个可更新的列存储索引,之前非聚集的列存储索引是只读的. 非聚集的列存储索引支持筛选条件. 在内存优化表中可以有一个列存储索引,可 ...
- Sql Server查询性能优化之索引篇【推荐】
Sql Server查询性能优化之索引篇[推荐] 这篇是索引系列中比较完整的,经过整理而来的 一 索引基础知识 索引概述 1.概念 可以把索引理解为一种特殊的目录.就好比<新华字典>为了加 ...
- SQL Server 查询性能优化——创建索引原则(一)
索引是什么?索引是提高查询性能的一个重要工具,索引就是把查询语句所需要的少量数据添加到索引分页中,这样访问数据时只要访问少数索引的分页就可以.但是索引对于提高查询性能也不是万能的,也不是建立越多的索引 ...
- sql server 2008学习4 设计索引的建议
索引设计的建议: 一.检查where子句和连接条件列 当一个查询提交到sql server时,查询优化器尝试为查询中引用的所有表查找最佳的数据访问机制, 一下是它所进行的方式. 1.优化器识别Wher ...
- SQL Server 解读【已分区索引的特殊指导原则】(3) - 非聚集索引分区
一.前言 在MSDN上看到一篇关于SQL Server 表分区的文档:已分区索引的特殊指导原则,如果你对表分区没有实战经验的话是比较难理解文档里面描述的意思.这里我就里面的一些概念进行讲解,方便大家的 ...
最新文章
- matlab有向图分割算法,基于万有引力搜索算法图像分割的MATLAB实现
- java的编译器怎么出来_怎样掌握ava编译器的使用,教程在这里,如何进行Java初级学习...
- 深入理解 Docker 网络原理
- Redhat 6 git 服务器配置(gitweb)
- Magento教程 10:如何修改网站文字?
- PHP - 验证用户名
- 编程语言python怎么读-Python和Go都很火,我要怎么选?
- [daily][device] linux挂载iphone
- python中pixels函数_Python+OpenCV3.3图像处理(一)
- Android谷歌地图地理编码,使用谷歌地图api iOS反向地理编码
- 666: 神奇的 SQL 之别样的写法 → 行行比较
- 菜花这样做,就着它吃光三碗饭
- 配置文件加密 HikariDataSource
- Windows下设备ID的查询
- 无线流媒体网关改变视频会议室
- 模拟信号标准三隔离信号分配器0-10V转4-20mA导轨式模块
- 这是你们要的临时接收短信的虚拟手机号
- 解压主播狂揽4000w+播放,快手美食赛道又添新风向?
- Epic Games CEO,《无尽之剑》创始人Tim Sweeney:下一代智能手机和平板电脑的游戏趋势
- Y-Combinator
热门文章
- 服务器域名解析步骤总结
- ./sbin/start-dfs.sh 开启NameNode 和 DataNode 守护进程报错
- 互联网产品经理的职责范围、能力要求
- 登录提示 ORA-28000 The account is locked.
- 【java】SSM框架浅薄理解
- 599.两个列表的最小索引总和
- 作为程序员,赚取额外收入的 4个简单副业!
- CCS中的IER和IFR寄存器:Symbol ‘IER‘ could not be resolved
- 嵌入式中的BSP---BSP到底是什么?
- 大端(Bid Endian)、小端(Little Endian)含义