Inside T-SQL Index (1)

Inside T-SQL Index (1)

一、前言

关于索引的设计优化等文章,网上不计其数,大都是各自根据自己的经验或心得写出的一些建议,本文不提供任何产品环境下的建议,因为才是Programming的本质,所以我只提供一些参考.毕竟每个数据库实例处的环境都不同.博友们认真读哦,呵呵.本文都是依据SQL Server 2005为大前提背景的.

二、了解索引

索引是与表或视图关联的磁盘上结构,可以加快从表或视图中检索行的速度。索引包含由表或视图中的一列或多列生成的键。这些键存储在一个结构(B 树)中,使 SQL Server 可以快速有效地查找与键值关联的行。

SQL Server索引大类型来讲主要有两大类,聚集索引和非聚集索引.那么他们之间有什么区别,以及他们之间的特征.

a)   聚集索引:首先聚集索引的概念就是,将我们关系数据中无序的堆(有序就叫表了,

的概念其实类似于高等数学中的集合,无序/唯一等)按照一定顺序组织起来的索引,所以一个集合也可能有一种真实的物理顺序,所以聚集索引在表中有且仅有一个.那么聚集索引是如何存储在SQL Server 引擎中的呢?首先,我们要知道数据库最小的存储单元就是数据库也Page,8kb的页,同样的我们的表和索引一样以堆或者B(一种数据结构,每个页级都存在一个都链接在一个双向链表)存在,数据库为了更好的管理数据页,那么将页以3种方式存储,

IN_ROW_DATA

用于存储堆分区或索引分区。

LOB_DATA

用于存储大型对象 (LOB) 数据类型,例如 xmlvarbinary(max) varchar(max)

ROW_OVERFLOW_DATA

用于存储超过 8,060 字节行大小限制的 varcharnvarcharvarbinary sql_variant 列中存储的可变长度数据。

我们通过下面的T-SQL语句获取相关的信息:

USE AdventureWorks;

GO

SELECT o.name AS table_name,p.index_id, i.name AS index_name , au.type_desc AS allocation_type, au.data_pages, partition_number

FROM sys.allocation_units AS au --分配单元

JOIN sys.partitions AS p ON au.container_id = p.partition_id --分区

JOIN sys.objects AS o ON p.object_id = o.object_id --对象表

JOIN sys.indexes AS i ON p.index_id = i.index_id AND i.object_id = p.object_id --索引表

WHERE o.name = N'DatabaseLog' OR o.name = N'Currency'

ORDER BY o.name, p.index_id;

table_name  index_id index_name   allocation_type  data_pages partition_number

Currency    1        PK_Currency_CurrencyCode IN_ROW_DATA         1           1

Currency    3        AK_Currency_Name         IN_ROW_DATA         1           1

DatabaseLog 0        NULL                     IN_ROW_DATA         160         1

DatabaseLog 0        NULL                     ROW_OVERFLOW_DATA   0           1

DatabaseLog 0        NULL                     LOB_DATA            49          1

(5 row(s) affected)

对于这个结果,以及B树索引,我们应该怎么理解呢?请博友认真看下面的解释:

首先表或者索引都可以存在于不同的分区,分区的概念,可以参考:

ms-help://MS.MSDNQTR.v90.chs/udb9/html/7d913c8d-b79b-4b1f-93b9-098dd33b07a8.htm简单理解分区就是,我们的数据可以存在于不同的物理磁盘上面,他们都由属于不同的文件组,且这些文件组中的数据文件可以位于不同的磁盘上.

那么不同分区的同一个关系数据对象,可以理解为逻辑上是同一个对象,只不过安放在不同的位置而已.

那么就如图中的堆或B树他们的数据页可以存储在IN_ROW_DATA(属于一般字段类型),LOB(大型字段类型),ROW_OVERFLOW_DATA(VARCHAR,NVARCHAR等在2005,都可以超过8kb大小).

因此上图就很好理解了.

SQL Server 中,索引是按 B 树结构进行组织的。索引 B 树中的每一页称为一个索引节点B 树的顶端节点称为根节点。索引中的底层节点称为叶节点。根节点与叶节点之间的任何索引级别统称为中间级。在聚集索引中,叶节点包含基础表的数据页(因为数据库都是按照不同的Page来访问里面的数据库的.页是最小的数据存储级别,所以作为底层节点是可以理解的.也就是说到底层节点的时候就是我们通过索引获取最终数据的时候)。根节点和中间级节点包含存有索引行的索引页。每个索引行包含一个键值和一个指针,该指针指向 B 树上的某一中间级页或叶级索引中的某个数据行(我们理解为,双向链表,一个指向其他人,一个可能指向一个叶节点,最终访问数据)。每级索引中的页均被链接在双向链接列表中。

通过中间级,我们可以访问2X树的上下节点,当然在2X树的底部就是数据页了,因此我们就能获取对应的数据行.

 

b)   非聚集索引:

非聚集索引具有独立于数据行的结构,也就是说我们创建的非聚集索引实质上是不会影响行的物理输出顺序的. 非聚集索引包含非聚集索引键值,并且每个键值项都有指向包含该键值的数据行的指针。当我们的表没有聚集索引的时候,那么我们可能就通过键值(所谓的非聚集索引列)的指针指向源表的数据行,如果表没有聚集索引(),那么通过指针指向RID来查找数据(RID 其实就是文件ID+Page ID+页上面行数ID组成的RID).如果有聚集索引,那么就直接指向聚集索引的数据行.

同样的,非聚集索引页具有B树结构

c)     唯一索引,一种约束性质的索引,主要让表或者试图的某些列在行之间是唯一的.他可以是聚集的也可以是非聚集的,看你创建他的时候是否写了CLUSTERED关键字了呵呵

d)     全文索引,查询复杂词的一种索引,SQL Server的一个组件,以后陆续和大家说明吧.

e)     XML索引,将一般的BLOB大型的二进制对象拆分成多个小型的节点对象

f)      包含行索引(覆盖索引),是一种非聚集索引,他主要将非键列保存在B树的叶级.

三、设计索引

 

聚集索引与聚集索引具有相同的 B 树结构,它们之间的显著差别在于以下两点:

基础表的数据行不按非聚集键的顺序排序和存储。

非聚集索引的叶层是由索引页而不是由数据页组成。因为是保留对RID和聚集索引的指针了.哈哈.

这里还给大家说一个东西,叫做 覆盖性索引 是2005引入的,很多朋友都知道如何使用他,主要将一些大字段INCLUDE到索引中去就好了,可是其中的缘由又是为什么呢?

因为可以通过将包含性列(称为非键列)添加到索引的叶级别中来扩展非聚集索引的功能。键列存储在非聚集索引的所有级别,而非键列仅存储在叶级别..因为您的大字段类型,他添加到了叶级别(底层节点),那么我们在SELECT 查询的时候,指定的一些大字段类型列,可以直接通过索引就获取数据了,而不用重新去访问基础表的数据,可以较少许多IO操作.

四、索引设计

索引的最优过程,并不一定是靠经验,有时候还是要去做测试.我们应该要实验多个索引而不要犹豫.

一般来说,我们可以通过包含实际的执行计划来分析查询所涉及使用的索引.而每种运算符都有不同的意义. 不要总是将索引的使用等同于良好的性能,或者将良好的性能等同于索引的高效使用。如果只要使用索引就能获得最佳性能,那查询优化器的工作就简单了。但事实上,不正确的索引选择并不能获得最佳性能。因此,查询优化器的任务是只在索引或索引组合能提高性能时才选择它,而在索引检索有碍性能时则避免使用它。

当然也可以使用SET showplan_text(xml,all) on ,SET STATISTICS XML/IO/TIME ON等来查看具体的执行的东西.

下面的话,先和大家讲讲一些常用的查询语句,博友们自己去分析MSDN:

SELECT * FROM sys.dm_exec_requests--block_session_id>1 来查询阻塞的会话

 

SELECT * FROM sys.dm_exec_sessions--当前所有会话

 

SELECT  * FROM sys.dm_exec_connections--当前所有连接

 

SELECT * FROM sys.dm_exec_cached_plans--缓存的计划

 

SELECT * FROM sys.dm_exec_query_plan(--需要handle作为参数,改函数用来查看执行计划.)

 

SELECT * FROM syscacheobjects--缓存的执行计划

 

SELECT * FROM sys.dm_exec_cached_plans A CROSS APPLY sys.dm_exec_query_plan(A.plan_handle) B--通过缓存计划查看具体的执行计划

 

SELECT * FROM sys.dm_exec_cached_plans A CROSS APPLY sys.dm_exec_sql_text(A.plan_handle) B--通过缓存计划,查看当前的执行SQL语句

 

SELECT * FROM sys.dm_exec_connections a CROSS APPLY sys.dm_exec_sql_text(most_recent_sql_handle) --当前的所有执行SQL语句

 

SELECT TOP 5 total_worker_time/execution_count AS [Avg CPU Time],

Plan_handle, query_plan

FROM sys.dm_exec_query_stats AS qs--这个是动态试图,一个聚合查询信息

CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle)

ORDER BY total_worker_time/execution_count DESC;

--平均执行时间以及执行计划

当然结合MSDN与实际,我们从下面5个方面来设计索引:

l  了解你的数据库特性,他是OLTP处理联机事务处理多(修改操作和查询操作),还是做为决策系统DSS基于查询多(数据库级别)

l  如果是企业版本的数据库使用ONLINE,来提供并发访问,因为修改或者创建索引不会产生表锁(数据库级别)

l  了解查询特性,关联关系,查询字段的特点来建立合适的索引(SQL语句级别)

l  尽可能的使用不同的磁盘存储索引,将源表与索引分开,可以使磁头同步操作查询,尽可能对索引进行合适的分区.(服务器级别)

l  了解列的特性,一般索引青睐于NOT NULL,INT,UNIQUE这种类型的列.(表级别)

常规索引设计指南:

ms-help://MS.MSDNQTR.v90.chs/udb9/html/e347ab5f-45a6-4ba8-898a-f83b4ccda028.htm

五、结语

这些东西要慢慢消费,我会在后面的文章讲关于一些Index的优化以及实现.大家继续关注吧.我们回顾下上文,主要是讲了索引的技术知识,希望这些知识让你重新认识索引

posted @ 2009-08-31 13:57  Sai~  阅读(2693)  评论(10编辑  收藏