SQL Server索引结构的具体使用

目录
  • 名词介绍
  • 索引表
  • 数据页

索引是数据库的基础,只有先搞明白索引的结构,才能搞明白索引运行的逻辑

本文通过 索引表、数据页、执行计划、IO统计、B+Tree 来尽可能的介绍 SQL 语句中 WHERE 部分,和 SELECT 部分 的运行逻辑

名词介绍

B+Tree:一种数据结构

  • 数据页:数据库保存数据的最小单位。(SQL Server一个数据页的大小是 8K,一个表中所有的数据都被保存到一个个的数据页中)
  • 索引组织表:大白话一张表有聚集索引就是索引组织表(把表中的数据页以 B+Tree 的方式组织起来)
  • 索引表:一个索引对应一张索引表,索引表中每条数据都对应一张数据页。

通过DBCC IND(数据库, 表名, 索引Id) 命令可以获取到表中指定索引的索引表信息

通过DBCC PAGE(数据库, 1, 数据页Id, 3) 命令可以获取到某个数据页中的数据

B+Tree结构

准备数据

DROP TABLE Org_User
-- 创建测试表
CREATE TABLE Org_User(Id INT,UserName NVARCHAR(50),Age INT)
-- 创建聚集索引和非聚集索引
CREATE CLUSTERED INDEX Org_User_Id ON Org_User(Id)
CREATE NONCLUSTERED INDEX Org_User_Name ON Org_User(UserName)

CREATE TABLE #Temp(Id INT)
INSERT INTO #Temp VALUES(1)
INSERT INTO #Temp VALUES(2)
INSERT INTO #Temp VALUES(3)
INSERT INTO #Temp VALUES(4)
INSERT INTO #Temp VALUES(5)
INSERT INTO #Temp VALUES(6)
INSERT INTO #Temp VALUES(7)
INSERT INTO #Temp VALUES(8)
INSERT INTO #Temp VALUES(9)
INSERT INTO #Temp VALUES(10)

-- 批量插入10W条数据
INSERT  INTO dbo.Org_User
SELECT T1.Id, 'UserName_' + CONVERT(NVARCHAR(20), T1.Id) AS 'UserName', T1.Id + 10 AS 'Age' FROM
(
    SELECT TOP 100000 Id = ROW_NUMBER() OVER (ORDER BY T1.Id)
    FROM #Temp AS T1
    CROSS JOIN #Temp AS T2
    CROSS JOIN #Temp AS T3
    CROSS JOIN #Temp AS T4
    CROSS JOIN #Temp AS T5
    ORDER BY T1.Id
) AS T1

SELECT name, index_id,type_desc FROM SYS.INDEXES WHERE object_id = OBJECT_ID('Org_User');

SELECT  index_id ,
        index_type_desc ,
        index_depth ,
        page_count
FROM    sys.dm_db_index_physical_stats(DB_ID('Core2022'), OBJECT_ID('Org_User'), NULL, NULL, NULL)

在 sys.dm_db_index_physical_stats 这张系统表中

index_depth 表示索引的深度 (对应上图B+Tree就是树的高度)

page_cout 表示索引数据页的数量 (对应上图B+Tree就是叶子节点的数量)

这里获取索引信息主要是为了 index_id

索引表

DBCC IND(Core2022, Org_User, 1)

DROP TABLE dbcc_ind
-- 创建一张表用来保存索引表信息
CREATE TABLE dbcc_ind
(
    PageFID NUMERIC(20),
    PagePID NUMERIC(20),
    IAMFID NUMERIC(20),
    IAMPID NUMERIC(20),
    ObjectID NUMERIC(20),
    IndexID NUMERIC(20),
    PartitionNumber NUMERIC(20),
    PartitionID NUMERIC(20),
    iam_chain_type VARCHAR(100),
    PageType NUMERIC(20),
    IndexLevel NUMERIC(20),
    NextPageFID NUMERIC(20),
    NextPagePID NUMERIC(20),
    PrevPageFID NUMERIC(20),
    PrevPagePID NUMERIC(20)
)

--DROP PROC proc_dbcc_ind
-- 创建存储过程
CREATE PROC proc_dbcc_ind
AS
DBCC IND(Core2022,Org_User,1)

-- 把索引表中的数据批量插入到 dbcc_ind 中
INSERT INTO dbcc_ind
EXEC proc_dbcc_ind
SELECT
    PagePID, -- 改行数据对应的数据页
    IndexLevel, -- 表示改行数据的级别 0叶子节点,1分支节点,=2根节点,仅限该Demo
    NextPagePID, -- 当前节点的后继节点 (后面的那个数据页)
    PrevPagePID -- 当前节点的前驱节点 (前面的那个数据页)
FROM dbcc_ind
SELECT
    PagePID,
    IndexLevel,
    NextPagePID,
    PrevPagePID
FROM dbcc_ind
WHERE IndexLevel = 0
ORDER BY NextPagePID

对 DBCC IND 中的数据进行一个总结

通过观察叶子节点的数据可以得到,每个节点都有一个前驱指针和后继指针,构成了一个双向链表

通过 IndexLevel 这个字段区分 根节点、分支节点、叶子节点

通过 NextPagePID 和 PrevPagePID 两个字段把相同深度的节点构成了一个双向链表

数据页

DBCC TRACEON(3604) — 打开跟踪标记,不打开的话 DBCC PAGE 只能查看分支节点中的数据,不能查看叶子节点中的数据

根节点

分支节点

叶子节点

非聚集索引的叶子节点

对索引表和根节点对应的数据页,分支节点对应的数据页,叶子节点对应的数据页进行总结

聚集索引

  叶子节点中保存的是 Org_User 表中的数据

  根节点和分支节点中保存的是指向下一级节点的条件

  索引表中同级的节点都有一个前驱和后继指针,这两个指针把同级的节点构建成了一个双向链表

非聚集索引

  根节点和分支节点与聚集索引一直,都是指向下一级节点的条件

  叶子节点有区别包含 创建非聚集索引是指定的Key、指向该行数据实际地址的Key、保证索引唯一的Key

    UserName 就是创建索引时指定的,如果创建时指定多个,这里也会有多个

    Id 这个是指向这行数据真实地址的指针表结构不同这个Key也不一样

      索引组织表:这个Key就是创建聚集索引时指定的 Key

      堆表:就值这个行数据所在堆表的地址

    UNIQUIFIER 如果创建索引时指定该索引时唯一索引,那么这里就不会有这个字段,否则就会有这个字段用来区分重复的数据

通过索引表,找到 Id = 66666 的这行数据所在的数据页    

对上图进行解释

拿着 66666 从根节点指向的数据页开始找

66666 > 36017 所以就跳转到 491 这个数据页

66511 < 66666 ≤ 66669 所以就跳转到 2755 这个数据页

因为 2755 这个数据页已经是叶子节点了,直接在里面搜索 66666

就找到了这一行数据

SET STATISTICS IO ON
SELECT * FROM Org_User WHERE Id = 66666

回表

因为这条SQL返回的字段是 Select *

非聚集索引里面没有 Age 这个字段

因此根据 UserName_66666 从非聚集索引中找到这条数据之后,根据 Id 到聚集索引里面在查一次,找到 Age 这个字段

覆盖索引

Select Id,UserName 非聚集索引里面这两个字段都有,所以就没有必要在查询聚集索引了

举一个例子

SET STATISTICS IO ON
SELECT * FROM [Org_User] WHERE Id >= 1 AND Id <= 10
SELECT * FROM [Org_User] WHERE Id IN (1,2,3,4,5,6,7,8,9,10)

-- 上面这两个SQL只有在 Id 为 Int 类型的时候才等价,在等价的前提下
-- 第一个SQL的效率要远超于第二个SQL

/*
SET STATISTICS IO ON (开启后输出的内容)
(10 行受影响)
表 'Org_User'。扫描计数 1,逻辑读取 3 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

(10 行受影响)
表 'Org_User'。扫描计数 10,逻辑读取 30 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

很明显 第一个SQL只有3次逻辑读,而第二个有30次逻辑读

*/

只有搞明白了索引运行的逻辑,结合执行计划等工具,才能搞明白什么情况下那些SQL更好

谣言:

  COUNT(*) 和 COUNT(列) 谁快,谁慢

  首先这两种写法都不等价 COUNT(*) 是所有的数据 COUNT(列) NULL值不参与运算,所以如果COUNT的某一列中包含了NULL值算出来的数据可能就有问题了

  查询速度

    COUNT(*) 更块

    COUNT(列) 会受偏移量和字段中数据的大小影响

      (通过 SET STATISTICS TIME ON 可以非常简单的得出结论)

  SQL语句 大表写前面,小表写后面

    当前数据库都会对SQL进行优化,所以无所谓谁在前,谁在后

  IN 与 EXISTS 谁好谁坏

    当前数据库都会对SQL进行优化,所以无所谓谁好,谁坏

  这些坑人的谣言还有很多,有些在老版本的数据库是对的,在当前的数据库中已经过时了。

到此这篇关于SQL Server索引结构的具体使用的文章就介绍到这了,更多相关SQL Server 索引结构内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SQL Server 索引结构及其使用(二) 改善SQL语句第1/3页

    比如: select * from table1 where name=''zhangsan'' and tID > 10000 和执行: select * from table1 where tID > 10000 and name=''zhangsan'' 一些人不知道以上两条语句的执行效率是否一样,因为如果简单的从语句先后上看,这两个语句的确是不一样,如果tID是一个聚合索引,那么后一句仅仅从表的10000条以后的记录中查找就行了:而前一句则要先从全表中查找看有几个name=''zhan

  • SQL Server 索引结构及其使用(一)--深入浅出理解索引结构第1/4页

    一.深入浅出理解索引结构 实际上,您可以把索引理解为一种特殊的目录.微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引.簇集索引)和非聚集索引(nonclustered index,也称非聚类索引.非簇集索引). 下面,我们举例来说明一下聚集索引和非聚集索引的区别: 其实,我们的汉语字典的正文本身就是一个聚集索引.比如,我们要查"安"字,就会很自然地翻开字典的前几页,因为"安"的拼音是"an",而按照

  • SQLSERVER的非聚集索引结构深度理解

    我们知道SQLSERVER的数据行的存储有两种数据结构:A: 堆   B :B树(binary 二叉树) 数据按照这种两种的其中一种来排序和存储,学过数据结构的朋友应该知道二叉树,为什麽用二叉树,因为方便用二分查找法来快速 找到数据.如果是堆,那么数据是不按照任何顺序排序的,也没有任何结构,数据页面也不是首尾相连的,不像B树,数据页面 使用双向链表首尾相连.堆表只依靠表里的IAM页(索引分配映射页)将堆的页面联系在一起,IAM里记录了页面编号,页面位置 除非表里有聚集索引,如果没有的话那么表里的

  • SQL Server索引结构的具体使用

    目录 名词介绍 索引表 数据页 索引是数据库的基础,只有先搞明白索引的结构,才能搞明白索引运行的逻辑 本文通过 索引表.数据页.执行计划.IO统计.B+Tree 来尽可能的介绍 SQL 语句中 WHERE 部分,和 SELECT 部分 的运行逻辑 名词介绍 B+Tree:一种数据结构 数据页:数据库保存数据的最小单位.(SQL Server一个数据页的大小是 8K,一个表中所有的数据都被保存到一个个的数据页中) 索引组织表:大白话一张表有聚集索引就是索引组织表(把表中的数据页以 B+Tree 的

  • 优化 SQL Server 索引的小技巧

    在本文中,我将说明如何用SQL Server的工具来优化数据库索引的使用,本文还涉及到有关索引的一般性知识. 关于索引的常识 影响到数据库性能的最大因素就是索引.由于该问题的复杂性,我只可能简单的谈谈这个问题,不过关于这方面的问题,目前有好几本不错的书籍可供你参阅.我在这里只讨论两种SQL Server索引,即clustered索引和nonclustered索引.当考察建立什么类型的索引时,你应当考虑数据类型和保存这些数据的column.同样,你也必须考虑数据库可能用到的查询类型以及使用的最为频

  • SQL Server 索引介绍

    一,索引的概述 1,概念: 数据库索引是对数据表中一个或多个列的值进行排序的结构,就像一本书的目录一样,索引提供了在行中快速查询特定行的能力. 2,优缺点: 2.1优点: 1,大大加快搜索数据的速度,这是引入索引的主要原因. 2,创建唯一性索引,保证数据库表中每一行数据的唯一性. 3,加速表与表之间的连接,特别是在实现数据的参考完整性方面特别有意义. 4,在使用分组和排序子句进行数据检索时,同样可以减少其使用时间. 2,2缺点: 1,索引需要占用物理空间,聚集索引占的空间更大. 2,创建索引和维

  • SQL Server索引的原理深入解析

    前言 此文是我之前的笔记整理而来,以索引为入口进行探讨相关数据库知识(又做了修改以让人更好消化).SQL Server接触不久的朋友可以只看以下蓝色字体字,简单有用节省时间:如果是数据库基础不错的朋友,可以全看,欢迎探讨. 索引的概念 索引的用途:我们对数据查询及处理速度已成为衡量应用系统成败的标准,而采用索引来加快数据处理速度通常是最普遍采用的优化方法. 索引是什么:数据库中的索引类似于一本书的目录,在一本书中使用目录可以快速找到你想要的信息,而不需要读完全书.在数据库中,数据库程序使用索引可

  • SQL Server Page结构深入分析

    SQL Server存储数据的基本单元是Page,每一个Page的大小是8KB,数据文件是由Page构成的.在同一个数据库上,每一个Page都有一个唯一的资源标识,标识符由三部分组成:db_id,file_id,page_id,例如,15:1:8733,15是数据库的ID,1是数据文件的ID,8733是Page的编号,Page的编号从0依次递增.8个连续的Page组成一个区(Extent),数据文件中已分配(Allocated)的空间被分割成区的整数倍.一次磁盘IO操作作用于Page级别,而空间

  • SQL Server索引超出了数组界限的解决方案

    有开发的同事反映远程登录SQL Server操作报错,索引超出了数组界限等 如下图 线上数据库版本为SQL Server2012 R2,检查后发现开发人员SSMS版本为2008,版本与服务器不一致,(开发人员要求登录数据库服务器操作,果断拒绝了)建议在本地打上SP3或者直接安装2012的SSMS,安装好后问题解决. 补充:SQL–"索引超出了数组界限" 一般是由于使用的客户端版本低于数据库版本引起的 如果没有多个SQL版本的话 可以尝试的解决办法: 1.重启电脑,再试试 2.重装SQL

  • SQL Server 索引和视图详解

    目录 索引 1. 什么是索引 2. 索引分类 聚集索引 非聚集索引 其他类型索引 3. 创建索引 4. 适合的创建索引的列 5. 不适合创建索引的列 视图 1. 什么是视图 2. 创建视图准则 3. 创建视图 4. 修改视图 5. 加密视图 总结 索引 1. 什么是索引 索引就是数据表中数据和相应的存储位置的列表,利用索引可以提高在表或视图中的查找数据的速度. 2. 索引分类 数据库中索引主要分为两类:聚集索引和非聚集索引.SQL Server 2005还提供了唯一索引.索引视图.全文索引.xm

  • Sql Server 索引使用情况及优化的相关Sql语句分享

    复制代码 代码如下: --Begin Index(索引) 分析优化的相关 Sql -- 返回当前数据库所有碎片率大于25%的索引 -- 运行本语句会扫描很多数据页面 -- 避免在系统负载比较高时运行 -- 避免在系统负载比较高时运行 declare @dbid int select @dbid = db_id() SELECT o.name as tablename,s.* FROM sys.dm_db_index_physical_stats (@dbid, NULL, NULL, NULL,

随机推荐