首页 > 数据库 > > 细说SQLServer索引原理
表默认只有一个分区(sql server的分区表技术,可以将表进行水平拆分,这样就会产生多个分区)。 分区里面就是存储的数据,有两种存储形式:堆或B+树,具体结构下面细说。、
页是数据存储的最小单位。 页类型分为:数据页、索引页、Log_mixed_page、Lob_tree_page、IAM页面
一个数据页可以存储8K(8192字节,减去96字节的头)大小的数据。数据页里面就是数据行,数据行不能跨页。
疑问:那一行数据可以超过8K吗,超过8K不就跨页了吗?
sql server 2000会有这个限制。sql server 2005 突破了每行8K的限制 但是sql server列的大小,仍不能超过8K(比如你不能定义varchar(9000) 或者 nvarchar(5000));
如果一行数据超出了8K,那么超出8K的字段会存到溢出页上,原数据行上有个指针指向到溢出页。
有人可能会说 varchar(max)、nvarchar(max)、text、image这种类型,其实不然,这种类型是LOB类型。
LOB(large object)是一种用于存储大对象的数据类型,每个LOB可以有2GB。LOB列可以跨多页,并且页不一定是连续的。
区(又叫扩展区)是页的集合,一个区包括了8个页,区大小是64K。 注意:这里的区,不是表分区。每个表默认只有一个表分区。
堆是一个没有聚集索引的表。表中的数据不按任何字段排序。 用"索引分配映射(IAM)"页将堆的页面联系在一起。如下图所示:
堆内的数据页和行没有任何特定的顺序;页面也不链接在一起,数据页之间唯一的逻辑连接是记录在IAM页内的信息, 页面与页面之间没有什么紧密的联系;用IAM页查找数据页集合中的每一页。 从数据存储管理上来讲,用堆去管理一个超大的表格是比较吃力的,经常使用的表格上都建立聚集索引。
可以通过扫描 IAM 页对堆进行表扫描或串行读操作来找到容纳该堆的页的扩展盘区。 因为 IAM 按扩展盘区在数据文件内存在的顺序表示它们,所以这意味着串行堆扫描连续沿每个文件进行。 使用 IAM 页设置扫描顺序还意味着堆中的行一般不按照插入的顺序返回。
MSSQL的索引存储结构是B+树,这是一种平衡多叉树。
1,因为页容量是固定的,所以B+树能容纳更多的索引值,那么索引深度就相对较浅,查找性能会更好。
2,b+树叶级节点之间,是个双向链表,范围查找很快。
当一个表上加了聚集索引后,其结构即成了一个B+树,数据记录成了B+树的一部分。 数据的物理顺序按索引字段的顺序来排列,因为物理排列顺序肯定是只有一种的,所以表上只能添加一个聚集索引。
下图是一个单字段聚集索引的存储结构图(假设是在Name上加的聚集索引)
从根节点开始查找:
>= ‘Bennet’ 且 < ‘Karsen’ 的数据 --> 进入索引页1007 (Greene的记录应该再查找此页)
>= ‘Karsen’ 且 < ‘Smith’ 的数据 --> 进入索引页1009
>= ‘Smith’ 且 < xxxxxxx 的数据 --> 进入索引页1062
再从中间节点索引页1007查找:
>= ‘Bennet’ 且 < ‘Greane’ 的数据 --> 进入数据页1132
>= ‘Greane’ 且 < ‘Hunter’ 的数据 --> 进入数据页1133 (Greene的记录应该再查找此页)
>= ‘Hunter’ 且 < xxxxxxxx 的数据 --> 进入数据页1127
最后从数据页1133中取得Name=Greene的这行记录
复制代码1,根节点里面的存储索引键值是如何决定的?为什么是zhangsan,而不是lisi或者其他?
PS:即第一条记录肯定是在根节点里面的,下面的DBCC分析也佐证了这点。
假设某表里1亿行数据,并且这1亿行数据刚好构成了1000万个数据页,
聚集索引字段是个Int型字段(Int类型为4字节),一个索引页只能存储8K(8060字节)数据的:
索引的层数(即索引深度)是由索引键的大小和数量决定的。
在索引行里会有多个索引键的值,如下图所示。下图截取自DBCC分析的内容
非聚集索引和聚集索引的区别是:叶级不再是数据页,即数据不再是索引结构的一部分。
非聚集索引的叶级存储的内容是什么呢? 可以分为两种情况来讨论:堆表上的非聚集索引、聚集表(即有聚集索引的表)上的非聚集索引 其叶级内容是不一样的。
注意,如果表没有聚集索引,建立了非聚集索引,那么非聚集索引使用的是行号,如果此时你又添加了聚集索引,那么所有的非聚集索引引用的RID都要改为聚集索引键。这对性能的消耗是非常大的,因此最好先建立聚集索引,在建立非聚集索引。
如果使用RID定位的话,如果数据页发生了页拆分,那么新拆分出来的半页数据,索引里面原来对应这个半页数据行的RID,全部要更新为新的RID。
这个过程的效率是十分低下的,因为这半页数据行的非聚集索引,一般都不是在一起的,都是分散在个索引页里面的,查找起来效率很低。 而存储聚集索引键值的话,即使聚集索引发生了数据页拆分,对非聚集索引也没有影响。
因为不再是表扫描,而是使用索引查找,呈几何式提高效率
如果修改了索引字段的值,才会导致索引更新。如果没有修改索引字段的话,效率我理解应该是不会降低的
删除了数据,需要更新索引,从而降低效率。
PS: 对修改和删除操作来说,虽然更新索引对效率有一定影响,但是要UPDATE或DELETE一行的前提是必须找到一行,因此索引实际上对于有复杂WHERE条件的UPDATE或DELETE也有帮助的。在使用索引定位一行的有效性通常能弥补更新索引所带来的额外开销,除非索引设计不合理。
索引查找是从最左侧的字段开始匹配的。如果一个组合索引,查询时只使用中间和后面的字段,那么是无法使用到索引的
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69999831/viewspace-3170490/ ,如需转载,请注明出处,否则将追究法律责任。