索引除了提高性能,还能维护数据库。

索引是一种存储结构,主要以B-Tree形式存储信息。

B-Tree的定义:

1.每个节点最多只有m个节点(m>=2)

2.除了根节点和叶子节点外的每个节点上最少有m/2个子节点

3.如果根节点不是叶子节点,那么最少有两个子节点,整个B-Tree只能有一个根节点

4.带有k个子节点的非叶子节点包好k-1个键

5.每个叶子节点都出现在同一层

二:索引的主要类型

1.堆:堆的定义就是一堆数据,堆是有顺序的,在没有改动的情况下,他的顺序就是数据插入时的顺序,一旦数据改动,数据的顺序就会发生改变,而且不容易组织,所以没有聚集索引的数据表称为堆表,即使上面有非聚集索引,也还是堆表

2.聚集索引:每个表只能有一个聚集索引,能使用表按照创建时的首列顺序存放数据

3.非聚集索引:他和聚集索引一样,是B-Tree结构,不影响数据存储,只有堆和聚集索引才影响数据的存储,绝大部分索引的变种都是非聚集索引

4.列存储索引:用于应对大数据的查询操作

5.XML索引、空间索引、全文索引

三:索引元数据

sys.indexes:用于提供当前数据库中每个索引的信息,每个表、索引或者表变量都有对应一行

sys.index_columns:提供了索引内包含的列,也就是索引键

四:SQL Server 存储基础

按照数据页来存储数据和对象(不包括日志),索引也是一种对象,所以也以页的形式存储在数据库中。

2.区:8个连续的页会组合成一个区,区不能小于8个页

3.页的组织:

堆、B-Tree、列式存储

(1).堆表:SQLServer默认的数据存储结构为堆表,任何没有聚集索引的表都成为堆表

  1. --创建索引演示库
  2. IF DB_ID('Index_Demo') IS NOT NULL
  3. DROP DATABASE Index_Demo
  4. GO
  5. --创建一个堆表
  6. IF OBJECT_ID('Heap_Demo','U') IS NOT NULL
  7. DROP TABLE Heap_Demo
  8. CREATE TABLE Heap_Demo
  9. (
  10. id INT IDENTITY(1,1),
  11. Name VARCHAR(10)
  12. )
  13. GO
  14. SELECT * FROM sys.indexes
  15. WHERE object_id=OBJECT_ID('Heap_Demo')

2.B-Tree意为平衡树,聚集索引和非聚集索引都使用B-Tree组织索引

以树形层次结构组织页面的,在这个结构中,页面有固定的顺序,页与页之间由双向链表用于组织

3.列存储结构:实际上是一个非聚集索引类型的存储结构,与传统的行存储索引不同,行存储需要存放表中的整行到索引中,而列存储只需要存储所需的列到索引中。可以进行页压缩

五:页碎片

SQL Server 可以在一个页中存放多条数据,但是当存放的数据超过8kb时,就会发生分页,根据表的页组织情况会有两种分页形式:Forwarded records和页拆分

Forwarded records这种分页形式仅发生在堆结构中。当一行数据被更新,并且大小已经无法放入该数据页时,会把这个数据页移到堆中的新数据页,并在新旧页中分别添加一个指针,标识这个数据在新旧页中的位置

从旧页指向新页的指针叫forwarded record pointer,存放在旧页中。

从新页指向旧页的指针叫back pointer存放在新页中

--创建测试环境

  1. IF OBJECT_ID (N'HeapForwardedRecords','U') IS NOT NULL
  2. DROP TABLE HeapForwardedRecords
  3. CREATE TABLE HeapForwardedRecords
  4. (
  5. RowID INT IDENTITY(1,1),
  6. DATA varchar(2500)
  7. );
  8. INSERT INTO HeapForwardedRecords (
  9. data
  10. )
  11. SELECT TOP 24 REPLICATE('X',2000)--24行,每行2000bytes
  12. FROM sys.objects
  13. DECLARE @objectIN INT =OBJECT_ID(N'HeapForwardedRecords');
  14. SELECT object_id,index_type_desc,
  15. page_count,record_count,forwarded_record_count
  16. FROM sys.dm_db_index_physical_stats(DB_ID(),@objectIN,NULL,NULL,'DETAILED');
  17. GO
  18. DBCC IND(0,N'HeapForwardedRecords',-1)

一行数据占2000bytes,一个页有8060bytes,所以一个页最多可以存放4行数据,24条数据需要6页来存放

跟新每个页的其中两行数据,把长度变成2500,这个时候堆上的页会拆分出去。

  1. UPDATE dbo.HeapForwardedRecords
  2. SET data=REPLICATE('X',2500)
  3. WHERE RowID%2=0;
  4.  
  5. DECLARE @ObjectID INT = OBJECT_ID('dbo.HeapForwardedRecords');
  6. SELECT object_id,
  7. index_type_desc,
  8. page_count,record_count,forwarded_record_count FROM sys.dm_db_index_physical_stats(DB_ID(),@ObjectID,NULL,NULL,'DETAILED');

物理索引存储信息:

这种页拆分比表扫描更糟糕的情况,本身堆表只有扫描(表、索引扫描)一种方式,当页拆分过多时,需要扫描的页就多了。最终使得性能下降

2.页拆分

这种方式存储在B-Tree结构中,聚集索引和非聚集索引都以这种方式分页

  1. CREATE TABLE dbo.ClusteredPagaSplits
  2. (
  3. RowID INT IDENTITY(1,1),
  4. Data VARCHAR(2500),
  5. CONSTRAINT PK_ClusteredPageSplits PRIMARY KEY CLUSTERED(RowID) --添加聚集索引
  6. );
  7.  
  8. INSERT INTO dbo.ClusteredPagaSplits
  9. ( Data )
  10. SELECT TOP 24 REPLICATE('X',2000)
  11. FROM sys.objects;
  12.  
  13. DECLARE @ObjectID INT =OBJECT_ID('dbo.ClusteredPagaSplits');
  14. SELECT
  15. object_id,index_type_desc,index_level,page_count,record_count
  16. FROM sys.dm_db_index_physical_stats(DB_ID(),@ObjectID,NULL,NULL,'DETAILED');

  1. --分页后索引物理情况
    UPDATE dbo.ClusteredPagaSplits
  2. SET data=REPLICATE('X',2500)
  3. WHERE RowID %2=0;
  4. DECLARE @ObjectID INT =OBJECT_ID('dbo.ClusteredPagaSplits');
  5. SELECT
  6. object_id,index_type_desc,index_level,page_count,record_count
  7. FROM sys.dm_db_index_physical_stats(DB_ID(),@ObjectID,NULL,NULL,'DETAILED');

分页会导致页的增多,影响性能,同时,分页过程中会对新页添加排他锁,这是有一个会话需要访问一条数据,这条数据被更新正在分页,分页后数据的物理位置很有可能已经改变,导致无法进行连续读,所以减少了一次读操作所能处理的页数

三:索引层级的统计信息

1.DBCC SHOW_STATISTICS

返回当前对象的统计信息

  1. DBCC SHOW_STATISTICS(N'Sales.SalesOrderDetail',PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID)

2.目录视图

(1)sys.stats目录视图返回数据库每个创建了统计信息的对象名及一些基础信息

(2)sys.stats_columns

(3)函数STATS_DATE()

知道统计信息的更新格式:

  1. STATS_DATE(OBJECT_ID,stats_id)
  1. SELECT object_id,stats_id
  2. FROM sys.stats WHERE object_id=OBJECT_ID(N'Sales.SalesOrderDetail')
  3. AND name=N'PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID'
  4.  
  5. --查询统计信息时间
  6. SELECT STATS_DATE(1154103152,1)

3.索引使用的统计信息:sys.dm_db_index_usage_stats

可以查看索引的使用频率

  1. SELECT database_id,object_id,index_id,user_lookups,user_scans,user_updates,user_seeks
  2. FROM sys.dm_db_index_usage_stats
  3. WHERE database_id=DB_ID() AND object_id=OBJECT_ID(N'Sales.SalesOrderDetail')
  1. --产生seek
  2. SELECT * FROM sales.SalesOrderDetail WHERE SalesOrderID BETWEEN 43659 AND 44659
  3.  
  4. --产生scan
  5. SELECT * FROM sales.SalesOrderDetail
  6.  
  7. --产生lookup
  8. SELECT ProductID,CarrierTrackingNumber FROM sales.SalesOrderDetail WHERE ProductID=778

  1. UPDATE sales.SalesOrderDetail SET UnitPriceDiscount=0.01 WHERE UnitPriceDiscount=0.00

检查一下DMV:

  1. SELECT database_id,object_id,index_id,
  2. system_lookups,system_scans,system_seeks,system_updates,
  3. last_system_lookup,last_system_scan,last_system_seek,
  4. last_system_update
  5. FROM sys.dm_db_index_usage_stats
  6. WHERE database_id=DB_ID() AND object_id=OBJECT_ID(N'Sales.SalesOrderDetail')

索引操作的统计信息:   sys.dm_db_index_operational

展示了索引底层的信息,前面的DMV主要展示对索引的逻辑操作,而这个DMO展示的是索引的底层信息

  1. --演示DML活动
  2. IF OBJECT_ID(N'Oper_Index','U') IS NOT NULL
  3. DROP TABLE Oper_Index
  4. CREATE TABLE Oper_Index
  5. (
  6. ID INT,
  7. DATA BIT,
  8. CONSTRAINT PK_Oper_ID PRIMARY KEY CLUSTERED(ID)
  9. )
  10. GO
  11. INSERT INTO dbo.Oper_Index
  12. SELECT ROW_NUMBER() OVER(ORDER BY t.object_id),
  13. t.object_id%2
  14. FROM sys.tables t
  15.  
  16. GO
  17. DELETE FROM dbo.Oper_Index WHERE Data=0
  18. GO
  19. UPDATE dbo.Oper_Index SET data=0 WHERE data=1
  20. --查看操作信息
  21. SELECT OBJECT_SCHEMA_NAME(ios.object_id)+'.'+OBJECT_NAME(ios.object_id) AS table_name,
  22. i.name AS index_name,
  23. ios.leaf_insert_count,ios.leaf_update_count,ios.leaf_delete_count,ios.leaf_ghost_count
  24. FROM sys.dm_db_index_operational_stats(DB_ID(),NULL,NULL,NULL) ios
  25. INNER JOIN sys.indexes i ON i.object_id=ios.object_id
  26. AND i.index_id=ios.index_id
  27. WHERE ios.object_id=OBJECT_ID(N'Oper_Index')
  28. ORDER BY ios.range_scan_count DESC

SELECT 操作包含内容的解释

(1)range scan 堆或索引上的表扫描和范围扫描的积累值   Bingint

表中的所有行或者部分行通过扫描形式访问时,就会产生累积。不管扫描过程中访问了多少行数据,这个值只记录发生的次数

  1. -- 查看 Sales.SalesOrderDetail 上的 range scan
  2. SELECT OBJECT_NAME(ios.object_id) AS table_name,
  3. i.name AS index_name,
  4. ios.range_scan_count
  5. FROM sys.dm_db_index_operational_stats(DB_ID(), OBJECT_ID('Sales.SalesOrderDetail'),NULL,NULL) ios
  6. INNER JOIN sys.indexes i ON i.object_id=ios.object_id AND i.index_id=ios.index_id
  7. ORDER BY ios.range_scan_count DESC
  8.  
  9. --执行一次,产生rang scan
  10. SELECT * FROM sales.SalesOrderDetail
  11.  
  12. --再次检查
  13. SELECT OBJECT_NAME(ios.object_id) AS table_name,
  14. i.name AS index_name,
  15. ios.range_scan_count
  16. FROM sys.dm_db_index_operational_stats(DB_ID(), OBJECT_ID('Sales.SalesOrderDetail'),NULL,NULL) ios
  17. INNER JOIN sys.indexes i ON i.object_id=ios.object_id AND i.index_id=ios.index_id
  18. ORDER BY ios.range_scan_count DESC

可以看到range scan已经增加了1

(2)singleton lookup 从索引或堆中单行检索的累积值

当产生key lookup时,值就会增加,这和sys.dm_db_index_usage_stats中的user_lookups的收集机制基本相同。user_lookups每发生一次其值只增加1,sys.dm_db_index_usage_stats是针对影响的所有行,而不是次数

  1. --查看Sales.SalesOrderDetail 上的 range scan
  2. SELECT OBJECT_NAME(ios.object_id) AS table_name,
  3. i.name AS index_name,
  4. ios.singleton_lookup_count
  5. FROM sys.dm_db_index_operational_stats(DB_ID(), OBJECT_ID('Sales.SalesOrderDetail'),NULL,NULL) ios
  6. INNER JOIN sys.indexes i ON i.object_id=ios.object_id AND i.index_id=ios.index_id
  7. ORDER BY ios.singleton_lookup_count DESC

  1. --执行一次产生lookup
  2. SELECT ProductID,CarrierTrackingNumber FROM sales.SalesOrderDetail WHERE productid=778
  1. --再次执行
  2. SELECT OBJECT_NAME(ios.object_id) AS table_name,
  3. i.name AS index_name,
  4. ios.singleton_lookup_count
  5. FROM sys.dm_db_index_operational_stats(DB_ID(), OBJECT_ID('Sales.SalesOrderDetail'),NULL,NULL) ios
  6. INNER JOIN sys.indexes i ON i.object_id=ios.object_id AND i.index_id=ios.index_id
  7. ORDER BY ios.singleton_lookup_count DESC

(3)forwarded fetch

每发生一次forwarded record操作其值就会累计。

  1. --forwarded record
  2. CREATE TABLE dbo.ForwardedRecords(
  3. ID INT IDENTITY(1,1),
  4. Value VARCHAR(8000)
  5. );
  6. INSERT INTO dbo.ForwardedRecords
  7. ( Value )
  8. SELECT replicate(type,500)
  9. FROM sys.objects;
  10.  
  11. UPDATE dbo.ForwardedRecords
  12. SET Value=REPLICATE(Value,16)
  13. WHERE ID%3=1;
  14.  
  15. --检查forwarded_fetch_count
  16. SELECT OBJECT_NAME(ios.object_id) AS table_name,
  17. i.name AS index_name,
  18. ios.forwarded_fetch_count
  19. FROM sys.dm_db_index_operational_stats(DB_ID(),OBJECT_ID('dbo.ForwardedRecords'),NULL,NULL) ios
  20. INNER JOIN sys.indexes i ON i.object_id=ios.object_id
  21. AND i.index_id=ios.index_id
  22. ORDER BY ios.forwarded_fetch_count DESC

这个DMF可以查看在索引上的资源争用,以便研究索引是否合理创建

  1. --行锁 的信息
  2. SELECT SalesOrderID,SalesOrderDetailID,CarrierTrackingNumber,OrderQty
  3. FROM Sales.SalesOrderDetail WHERE ProductID=710
  4.  
  5. --查询
  6. SELECT OBJECT_NAME(ios.object_id) AS table_name,i.name AS index_name,
  7. ios.row_lock_count,
  8. ios.row_lock_wait_count,
  9. ios.row_lock_wait_in_ms
  10. FROM sys.dm_db_index_operational_stats(DB_ID(),OBJECT_ID('Sales.SalesOrderDetail'),NULL,NULL) ios
  11. INNER JOIN sys.indexes i ON i.object_id=ios.object_id
  12. AND i.index_id=ios.index_id
  13. ORDER BY ios.range_scan_count DESC

查看page_lock的相关信息

  1. SELECT OBJECT_NAME(ios.object_id) AS table_name,
  2. i.name AS index_name,
  3. ios.page_lock_count,
  4. ios.page_lock_wait_count,
  5. ios.page_lock_wait_in_ms FROM sys.dm_db_index_operational_stats(DB_ID(),OBJECT_ID('Sales.SalesOrderDetail'),NULL,NULL) ios
  6. INNER JOIN sys.indexes i ON i.object_id=ios.object_id
  7. AND i.index_id=ios.index_id
  8. ORDER BY ios.range_scan_count DESC

SQLServer索引及统计信息的更多相关文章

  1. SQLSERVER是怎麽通过索引和统计信息来找到目标数据的(第三篇)

    SQLSERVER是怎麽通过索引和统计信息来找到目标数据的(第三篇) 最近真的没有什么精力写文章,天天加班,为了完成这个系列,硬着头皮上了 再看这篇文章之前请大家先看我之前写的第一篇和第二篇 第一篇: ...

  2. Sybase数据库收集表及其索引的统计信息

    更新表及其索引的统计信息: update table statistics 表名 go update index statistics 表名 go 建议此操作在闲时操作.

  3. Oracle 和 SQLSERVER 重新获取统计信息的方法

    1. Oracle 重新获取统计信息的命令 exec dbms_stats.gather_schema_stats(ownname =>) # 需要修改 ownername options 指定 ...

  4. 使用DBCC SHOW_STATISTICS展示索引的统计信息

    在开始之前搭建演示环境: USE master GO SET NOCOUNT ON --创建表结构 IF OBJECT_ID(N'ClassA', N'U') IS NOT NULL DROP TAB ...

  5. 转载:性能优化——统计信息——SQLServer自动更新和自动创建统计信息选项

    这段时间AX查询变得非常慢,每天都有很多锁. 最后发现是数据库统计信息需要更新. ----------------------------------------------------------- ...

  6. 第十二章——SQLServer统计信息(1)——创建和更新统计信息

    原文:第十二章--SQLServer统计信息(1)--创建和更新统计信息 简介: 查询的统计信息: 目前为止,已经介绍了选择索引.维护索引.如果有合适的索引并实时更新统计信息,那么优化器会选择有用的索 ...

  7. 性能优化——统计信息——SQLServer自动更新和自动创建统计信息选项

    原文:性能优化--统计信息--SQLServer自动更新和自动创建统计信息选项 原文译自:http://www.mssqltips.com/sqlservertip/2766/sql-server-a ...

  8. sql server 索引阐述系列八 统计信息

    一.概述 sql server在快速查询值时只有索引还不够,还需要知道操作要处理的数据量有多少,从而估算出复杂度,选择一个代价小的执行计划,这样sql server就知道了数据的分布情况.索引的统计值 ...

  9. SQL Server 执行计划利用统计信息对数据行的预估原理二(为什么复合索引列顺序会影响到执行计划对数据行的预估)

    本文出处:http://www.cnblogs.com/wy123/p/6008477.html 关于统计信息对数据行数做预估,之前写过对非相关列(单独或者单独的索引列)进行预估时候的算法,参考这里. ...

随机推荐

  1. Scala 继承

    1. 继承 Scala 通过 extends 关键字来继承类. 那么继承一个类有什么好处呢? 子类拥有继承自超类的方法和字段(即为val(常量), var(变量)所定义的) 可以添加自己需要的新方法和 ...

  2. [转载]linux内存映射mmap原理分析【转】

    转自:http://www.cnblogs.com/wanpengcoder/articles/5306688.html 转自:http://blog.csdn.net/yusiguyuan/arti ...

  3. SharePoint 2013 SqlException (0x80131904):找不到Windows NT 用户或组xxxx\administrator

    过程描述: 在SharePoint 2013里配置创建搜索服务应用程序时报错: 配置 Search Service 应用程序期间遇到错误. System.Data.SqlClient.SqlExcep ...

  4. ROS入门学习

    ROS学习笔记 ROS入门网站; ROS入门书籍 ROS主要包含包括功能包.节点.话题.消息类型和服务; ROS功能包/软件包(Packages) ROS软件包是一组用于实现特定功能的相关文件的集合, ...

  5. web@css高级选择器(after,befor用法),基本css样式

    1.高阶选择器:子代后代,相邻通用兄弟,交集并集,属性,伪类,伪元素子代后代选择器 div>p{}  div p{}相邻通用兄弟 div+p{}  div~p{}理解:div同学的同桌p  di ...

  6. 使用GeoServer导出地图数据GeoJSON并应用

    在项目中,需要使用乡镇街道的地图边界,之前一直使用的是百度地图或Echarts地图,其没有这部分行政区的数据,需要在第三方购买数据,其提供的是shp文件 主文件:counties.shp 索引文件:c ...

  7. ASP.NET MVC5高级编程 之 路由

    每个ASP.NET MVC应用程序都需要路由来定义自己处理请求的方式.路由是MVC应用程序的入口点.路由的核心工作是将一个请求映射到一个操作 路由主要有两种用途: 匹配传入的请求(该请求不匹配服务器文 ...

  8. python学习第天14天。

    模块 什么是模块 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. 但其实import加载的模块分为四个通用类别: 1 使用python编写的代码( ...

  9. LuoGu P1004 方格取数

    题目传送门 一开始这个题我是不会的(沙华弱DP啊QwQ),后来考完试我一想,这东西怎么和数字三角形那题这么像啊? 都是自上而下,只能向下或者向右,求一个max 那么...这不就是个走两遍的数字矩阵嘛 ...

  10. Confluence 6 数据模型

    本文档提供了 Confluence 的数据结构视图(schema )和数据模型概念上的的概述. 备注: Hibernate 的映射文件是针对 Confluence 数据模型的直接描述.在系统中的 Co ...