译】SQL Server索引进阶第六篇:书签

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云数据库 RDS SQL Server,基础系列 2核4GB
简介:
索引设计是数据库设计中比较重要的一个环节,对数据库的性能其中至关重要的作用,但是索引的设计却又不是那么容易的事情,性能也不是那么轻易就获取到的,很多的技术人员因为不恰当的创建索引,最后使得其效果适得其反,可以说“成也索引,败也索引”。

本系列文章来自Stairway to SQL Server Indexes

 

书签是什么

    我们已经在前面提到过书签,但仅仅说了书签可以帮助SQL Server快速从非聚集索引条目导向到对应的行,本篇文章开始让我们对书签的探索更进一步.书签的内容实际上是取决于非聚集索引所在表是以堆还是聚集索引存放的。

     不论表是堆结构还是段结构,可以确定的是,表中每一行都是某一页的第N行,这个某一页又是某个数据库文件的第N页,这个某个数据库文件又是构成数据库的文件组的第N个文件,因此,数据库中的每一行,在指定时间都可以由三个数字进行定位:  文件号:页号:行号。这三个数字组合起来就是所谓的RID。很多显示SQL Server内部结构的工具软件都会将这三个数字通过冒号分隔进行显示。比如,文件1的第77页的第12行的RID就是1:77:12。

    通常来说,在堆上的行不会被改变位置。一旦它们被插入某个页中,它们就会一直呆在那。如果要用更严谨的技术术语来说的话:在堆上的行很少移动。如果行被移动的话,它们会在原来的位置留下指向其移动到的位置的指针。而由聚集索引组织的行,是可以被移动的,行在改动数据或是整理索引的时候要被移动位置。更多的细节会在本系列文章后续篇幅进行介绍。

    因为在堆上的行几乎很少移动,所以RID就可以唯一标识某一行。RID的值不仅仅不变,RID所表示行的物理位置也不会变。这使得RID的值更适宜作为书签。这也是为什么SQL Server在堆上建立的非聚集索引的书签都使用RID。

 

堆上的非聚集索引:基于RID的书签

    假如SalesOrderDetail是一个基于堆的表;表中的每行都不是有序的。下面让我们建立以ProductID/ModifiedDate为非聚集索引键并包含OrderQty, UnitPrice, LineTotal三个列的非聚集索引,如代码6.1所示。

CREATE NONCLUSTERED INDEX FK_ProductID_ModifiedDate 
ON 
Sales.SalesOrderDetail(ProductID, ModifiedDate) 
INCLUDE (OrderQty, 
UnitPrice, LineTotal) 

 

代码6.1 建立含有包含列的非聚集索引

  

    在上面索引中,部分数据的顺序如下所示。

index6

 

    上面建立的非聚集索引因为使用了RID作为书签,直接指向对应行所在的物理地址,因此十分高效。但虽然RID值用于键查找非常高效,但书签中包含的值和具体的用户数据无关。

     另一种与这种基于RID的书签不同的书签,是当非聚集索引所在的表包含聚集索引时出现的。更准确的说,这种书签是建立聚集索引上的非聚集索引的书签。

 

在聚集索引上的非聚集索引:基于键值的书签

    如果表是基于聚集索引的,则表内数据可以在表移动。因此,对于聚集索引来说,RID并不能一直不变的定位一个相同的行。因此必须用另外的方法定位行,这个方法就是使用聚集索引的索引键。

    使用聚集索引键作为书签可以使得当数据在页中的行改变时,不需要非聚集索引的书签的值进行变动,因此非聚集索引的键就可以用于去找底层表的数据,意思是根据书签取数据不再基于物理位置,而是基于聚集索引查找。

    然而,以聚集索引键作为非聚集索引的书签最好要聚集索引键满足如下标准:

 

   索引应该具有唯一性. 每一个索引条目书签都应该使得书签可以通过聚集索引的键值唯一的确认表中的一行,如果你创建的聚集索引键值不唯一,SQL Server将会为有重复键值的每一行自动加上一个叫uniquifier的东西使得每一行唯一。这个uniquifier对客户端是透明的。对于是否可以允许聚集索引键重复,你需要考虑以下两点:

    生成uniquifier增加SQL Server插入操作的额外负担,在插入时SQL Server还需要判断插入的值在表中是否唯一,如果不唯一生成uniquifier值再进行插入。
    uniquifier本身对业务数据来说是没有意义的,但是这个uniquifier本身不仅仅需要占用聚集索引键的空间,还同时占用非聚集索引书签的空间。
 

   索引键应该短.索引键所占的字节数应该短.因为这个键还会占用非聚集索引书签的空间。比如Contact表中以Last name / first name / middle name / street组合作为索引键看上去不错,但如果表中存在多个非聚集索引的话情况就有些微妙了。n个非聚集索引使得Last name / first name / middle name / street这些字段被存储在n+1个位置。

    索引键最好不要变动.也就是索引键的值最好不要变动。对于聚集索引键的修改会使得基于这个聚集索引的所有非聚集索引同样进行修改。所以对于聚集索引的一次update会造成n个非聚集索引书签的update+1个聚集索引键值本身的update。

 

    AdventureWorks的设计团队在设计SalesOrderDetail表的聚集索引时就是完全遵循上面的建议。它们选择SalesOrderID / SalesOrderDetailID作为聚集索引键完全满足了窄,短和唯一的要求。将SalesOrderID作为索引键最左边的一列,尽管SalesOrderDetailID是唯一的,这两列组合在一起进行聚集。以SalesOrderID / SalesOrderDetailID作为主键和聚集索引键,就不再需要单独建立SalesOrderDetailID的非聚集索引了。

    现在我们创建和列表6.1所示的非聚集索引一样的索引。唯一的不同是现在这个版本是基于聚集索引而不是堆的。非聚集索引的部分数据如下:

 

index62

 

我们现在有了两个版本的非聚集索引,分别是创建在堆上的非聚集索引和创建在聚集索引上的非聚集索引,唯一的不同就是它们的书签值。

 

哪种更好

     上面两种聚集索引是不是一种要比另一种更好呢?或许吧,但是也要看具体情况。基于RID的书签允许快速的找到底层表中行所在的物理位置,而基于聚集索引的书签找到底层表的行就慢多了,但这个书签还可以作为包含列使用,书签列同时也常常会被当作外键。

     所以对于上面哪种非聚集索引更好的真正答案是”都不是”。当在表上建立索引时,最重要的选择只是使用哪些列作为索引键。一旦你确定了聚集索引列(基于文中所示的三点建议),剩下的非聚集索引所带来的影响就交给SQL Server来处理吧。

 

小结

     非聚集索引包含了索引键列,包含列和书签。书签的值根据所在表是堆还是聚集索引既可以是RID也可以是聚集索引键。对于表上聚集索引的最好选择要基于文中所给的三点指南。



索引设计是数据库设计中比较重要的一个环节,对数据库的性能其中至关重要的作用,但是索引的设计却又不是那么容易的事情,性能也不是那么轻易就获取到的,很多的技术人员因为不恰当的创建索引,最后使得其效果适得其反,可以说“成也索引,败也索引”。

本系列文章来自Stairway to SQL Server Indexes

 

书签是什么

    我们已经在前面提到过书签,但仅仅说了书签可以帮助SQL Server快速从非聚集索引条目导向到对应的行,本篇文章开始让我们对书签的探索更进一步.书签的内容实际上是取决于非聚集索引所在表是以堆还是聚集索引存放的。

     不论表是堆结构还是段结构,可以确定的是,表中每一行都是某一页的第N行,这个某一页又是某个数据库文件的第N页,这个某个数据库文件又是构成数据库的文件组的第N个文件,因此,数据库中的每一行,在指定时间都可以由三个数字进行定位:  文件号:页号:行号。这三个数字组合起来就是所谓的RID。很多显示SQL Server内部结构的工具软件都会将这三个数字通过冒号分隔进行显示。比如,文件1的第77页的第12行的RID就是1:77:12。

    通常来说,在堆上的行不会被改变位置。一旦它们被插入某个页中,它们就会一直呆在那。如果要用更严谨的技术术语来说的话:在堆上的行很少移动。如果行被移动的话,它们会在原来的位置留下指向其移动到的位置的指针。而由聚集索引组织的行,是可以被移动的,行在改动数据或是整理索引的时候要被移动位置。更多的细节会在本系列文章后续篇幅进行介绍。

    因为在堆上的行几乎很少移动,所以RID就可以唯一标识某一行。RID的值不仅仅不变,RID所表示行的物理位置也不会变。这使得RID的值更适宜作为书签。这也是为什么SQL Server在堆上建立的非聚集索引的书签都使用RID。

 

堆上的非聚集索引:基于RID的书签

    假如SalesOrderDetail是一个基于堆的表;表中的每行都不是有序的。下面让我们建立以ProductID/ModifiedDate为非聚集索引键并包含OrderQty, UnitPrice, LineTotal三个列的非聚集索引,如代码6.1所示。

CREATE NONCLUSTERED INDEX FK_ProductID_ModifiedDate 
ON Sales.SalesOrderDetail(ProductID, ModifiedDate)
INCLUDE (OrderQty, UnitPrice, LineTotal)

 

代码6.1 建立含有包含列的非聚集索引

  

    在上面索引中,部分数据的顺序如下所示。

index6

 

    上面建立的非聚集索引因为使用了RID作为书签,直接指向对应行所在的物理地址,因此十分高效。但虽然RID值用于键查找非常高效,但书签中包含的值和具体的用户数据无关。

     另一种与这种基于RID的书签不同的书签,是当非聚集索引所在的表包含聚集索引时出现的。更准确的说,这种书签是建立聚集索引上的非聚集索引的书签。

 

在聚集索引上的非聚集索引:基于键值的书签

    如果表是基于聚集索引的,则表内数据可以在表移动。因此,对于聚集索引来说,RID并不能一直不变的定位一个相同的行。因此必须用另外的方法定位行,这个方法就是使用聚集索引的索引键。

    使用聚集索引键作为书签可以使得当数据在页中的行改变时,不需要非聚集索引的书签的值进行变动,因此非聚集索引的键就可以用于去找底层表的数据,意思是根据书签取数据不再基于物理位置,而是基于聚集索引查找。

    然而,以聚集索引键作为非聚集索引的书签最好要聚集索引键满足如下标准:

 

   索引应该具有唯一性. 每一个索引条目书签都应该使得书签可以通过聚集索引的键值唯一的确认表中的一行,如果你创建的聚集索引键值不唯一,SQL Server将会为有重复键值的每一行自动加上一个叫uniquifier的东西使得每一行唯一。这个uniquifier对客户端是透明的。对于是否可以允许聚集索引键重复,你需要考虑以下两点:

  •     生成uniquifier增加SQL Server插入操作的额外负担,在插入时SQL Server还需要判断插入的值在表中是否唯一,如果不唯一生成uniquifier值再进行插入。
  •     uniquifier本身对业务数据来说是没有意义的,但是这个uniquifier本身不仅仅需要占用聚集索引键的空间,还同时占用非聚集索引书签的空间。

 

   索引键应该短.索引键所占的字节数应该短.因为这个键还会占用非聚集索引书签的空间。比如Contact表中以Last name / first name / middle name / street组合作为索引键看上去不错,但如果表中存在多个非聚集索引的话情况就有些微妙了。n个非聚集索引使得Last name / first name / middle name / street这些字段被存储在n+1个位置。

    索引键最好不要变动.也就是索引键的值最好不要变动。对于聚集索引键的修改会使得基于这个聚集索引的所有非聚集索引同样进行修改。所以对于聚集索引的一次update会造成n个非聚集索引书签的update+1个聚集索引键值本身的update。

 

    AdventureWorks的设计团队在设计SalesOrderDetail表的聚集索引时就是完全遵循上面的建议。它们选择SalesOrderID / SalesOrderDetailID作为聚集索引键完全满足了窄,短和唯一的要求。将SalesOrderID作为索引键最左边的一列,尽管SalesOrderDetailID是唯一的,这两列组合在一起进行聚集。以SalesOrderID / SalesOrderDetailID作为主键和聚集索引键,就不再需要单独建立SalesOrderDetailID的非聚集索引了。

    现在我们创建和列表6.1所示的非聚集索引一样的索引。唯一的不同是现在这个版本是基于聚集索引而不是堆的。非聚集索引的部分数据如下:

 

index62

 

我们现在有了两个版本的非聚集索引,分别是创建在堆上的非聚集索引和创建在聚集索引上的非聚集索引,唯一的不同就是它们的书签值。

 

哪种更好

     上面两种聚集索引是不是一种要比另一种更好呢?或许吧,但是也要看具体情况。基于RID的书签允许快速的找到底层表中行所在的物理位置,而基于聚集索引的书签找到底层表的行就慢多了,但这个书签还可以作为包含列使用,书签列同时也常常会被当作外键。

     所以对于上面哪种非聚集索引更好的真正答案是”都不是”。当在表上建立索引时,最重要的选择只是使用哪些列作为索引键。一旦你确定了聚集索引列(基于文中所示的三点建议),剩下的非聚集索引所带来的影响就交给SQL Server来处理吧。

 

小结

     非聚集索引包含了索引键列,包含列和书签。书签的值根据所在表是堆还是聚集索引既可以是RID也可以是聚集索引键。对于表上聚集索引的最好选择要基于文中所给的三点指南。



本文转自CareySon博客园博客,原文链接http://www.cnblogs.com/CareySon/archive/2012/09/07/2674940.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
相关文章
|
14天前
|
SQL 索引
【YashanDB知识库】字段加上索引后,SQL查询不到结果
【YashanDB知识库】字段加上索引后,SQL查询不到结果
|
25天前
|
SQL 关系型数据库 OLAP
云原生数据仓库AnalyticDB PostgreSQL同一个SQL可以实现向量索引、全文索引GIN、普通索引BTREE混合查询,简化业务实现逻辑、提升查询性能
本文档介绍了如何在AnalyticDB for PostgreSQL中创建表、向量索引及混合检索的实现步骤。主要内容包括:创建`articles`表并设置向量存储格式,创建ANN向量索引,为表增加`username`和`time`列,建立BTREE索引和GIN全文检索索引,并展示了查询结果。参考文档提供了详细的SQL语句和配置说明。
35 1
|
5月前
|
SQL 存储 关系型数据库
如何巧用索引优化SQL语句性能?
本文从索引角度探讨了如何优化MySQL中的SQL语句性能。首先介绍了如何通过查看执行时间和执行计划定位慢SQL,并详细解析了EXPLAIN命令的各个字段含义。接着讲解了索引优化的关键点,包括聚簇索引、索引覆盖、联合索引及最左前缀原则等。最后,通过具体示例展示了索引如何提升查询速度,并提供了三层B+树的存储容量计算方法。通过这些技巧,可以帮助开发者有效提升数据库查询效率。
612 2
|
5月前
|
SQL Oracle 关系型数据库
SQL优化-使用联合索引和函数索引
在一次例行巡检中,发现一条使用 `to_char` 函数将日期转换为字符串的 SQL 语句 CPU 利用率很高。为了优化该语句,首先分析了 where 条件中各列的选择性,并创建了不同类型的索引,包括普通索引、函数索引和虚拟列索引。通过对比不同索引的执行计划,最终确定了使用复合索引(包含函数表达式)能够显著降低查询成本,提高执行效率。
|
5月前
|
SQL 关系型数据库 MySQL
如何确认SQL用了索引:详细技巧与方法
在数据库管理中,索引是提高SQL查询性能的重要手段
1208 5
|
5月前
|
SQL 存储 关系型数据库
SQL默认索引是什么:深入解析与技巧
在SQL数据库中,索引是一种用于提高查询性能的重要数据结构
|
5月前
|
SQL 存储 关系型数据库
SQL默认索引是什么
在SQL数据库中,索引是一种用于提高查询性能的数据结构
|
5月前
|
SQL 关系型数据库 MySQL
如何确认SQL用了索引
在数据库管理和优化过程中,确认SQL查询是否使用了索引是一个至关重要的步骤
|
5月前
|
SQL 关系型数据库 MySQL
如何确认SQL查询是否使用了索引:详细步骤与技巧
在数据库管理和优化中,确认SQL查询是否有效利用了索引是提升性能的关键步骤
|
5月前
|
索引
SQL_创建和管理索引
SQL_创建和管理索引
45 0