SQL Server 性能优化之——重复索引

原文 http://www.cnblogs.com/BoyceYang/archive/2013/06/16/3139006.html

阅读导航

1. 概述

2. 什么是重复索引

3. 查找重复索引

4. 删除重复索引

5. 总结

 

1. 概述

很多人都知道索引在数据库上的是有利有弊的。像其他主流商业数据库一样SQL Server允许在一个列上重复创建索引。因为SQL Server没有限制创建重复索引的数量,只是限制数据库的一个表上最多可以创建999重复索引,所以这就增加了数据库中存在重复索引的可能性。表的列上 存在重复索引的话,可能会明显的损害数据库性能,因为SQL Server必须分别维护每一个重复索引。此外,SQL Server优化查询语句时,查询优化器也会考虑这个问题,这就导致一系列性能问题。要理解什么事实重复索引、怎么样找到它们、怎么样移除它们。

 

2. 什么是重复索引

首先假设有一个表Test_Table有四个列(Col1, Col2, Col3, Col4)

CREATE TABLE Test_Table

(

    Col1 int NOT NULL PRIMARY KEY,

    Col2 varchr(30) NOT NULL,

    Col3 varchr(30) NOT NULL,

    Col4 varchr(30) NOT NULL,

)

 

1) 在主键列上创建不同类型的索引

   1: CREATE UNIQUE CLUSTERED INDEX IX1 ON Test_Table(Col1); 
   2:  
   3: CREATE INDEX IX2 ON Test_Table(Col1); 

2) 在非主键列上创建不同顺序的包含列的索引

   1: CREATE INDEX IX3 ON Test_Table (Col4) 
   2: INCLUDE (Col2, Col3); 
   3:  
   4: CREATE INDEX IX4 ON Test_Table (Col4) 
   5: INCLUDE (Col3, Col2); 

3) 在非主键列上创建相同顺序包含列的索引

   1: CREATE INDEX IX5 ON Test_Table (Col4) 
   2: INCLUDE (Col2, Col3); 
   3:  
   4: 

CREATE UNIQUE INDEX IX6 ON Test_Table (Col4)

   5: INCLUDE (Col2, Col3); 

4) 在不同非主键列创建不同顺序的索引

   1: CREATE INDEX IX7 ON Test_Table (Col3, Col2); 
   2:  
   3: 

CREATE INDEX IX8 ON Test_Table (Col3, Col2);

这样重复的索引,在执行DML操作(插入、更新、删除)的时候需要更新索引。

 

3. 查找重复索引

一般不会有人特意创建重复索引。有时候,神不知鬼不觉的创建了,有时候 是因为创建新的索引是没有检查当前列是否已经存在索引。那么怎么样才能它们暴露来呢?

1) 使用SQL Server Management Studio (SSMS,但是在SQL Server有很多数据库,数据库中又有大量表和索引的情况下,使用SSMS并不是一个快捷的方式。

2) 使用sp_helpindex查找重复索引

3) 使用SQL Server系统目录,可以在SQL Server数据库上使用和开发脚本查找重复索引,这是一个比较方便并灵活的方式。

SQL系统目录:

a. sys.indexes:包括表格对象(例如,表、视图或表值函数)的索引或堆的每一行

b. sys.objects:在数据库中创建的每个用户定义的架构作用域内的对象在该表中均对应一行。

c. sys.index_columns:属于 sys.indexes 索引或未排序的表(堆)的每个列都对应一行。

d. sys.columns:返回包含列对象(如视图或表)的列的每一行

下面是包含列对象类型的表:

a) 表值程序集函数 (FT)

b) 内联表值 SQL 函数 (IF)

c) 内部表 (IT)

d) 系统表 (S)

e) 表值 SQL 函数 (TF)

f) 用户表 (U)

g) 视图 (V)

有一种是列出所有索引在哪个表上面,它们被扫描多少次,被更新多少次,在内存中的大小, 这些对我们有用的信息

SELECT
	sch.name + '.' + t.name AS [Table Name],
	i.name AS [Index Name],
	i.type_desc,
	ISNULL(user_updates,0) AS [Total Writes],
	ISNULL(user_seeks + user_scans + user_lookups,0) AS [Total Reads],
	s.last_user_seek,
	s.last_user_scan ,
	s.last_user_lookup,
	ISNULL(user_updates,0) - ISNULL((user_seeks + user_scans + user_lookups),0) AS [Difference],
	p.reserved_page_count * 8.0 / 1024 as SpaceInMB
FROM sys.indexes AS i WITH (NOLOCK)
	LEFT OUTER JOIN sys.dm_db_index_usage_stats AS s	WITH (NOLOCK) ON s.object_id = i.object_id  AND i.index_id = s.index_id  AND s.database_id=db_id()  AND objectproperty(s.object_id,'IsUserTable') = 1
	INNER JOIN		sys.tables					AS t	WITH (NOLOCK) ON i.object_id = t.object_id
	INNER JOIN		sys.schemas					AS sch	WITH (NOLOCK) ON t.schema_id = sch.schema_id
	LEFT OUTER JOIN sys.dm_db_partition_stats	AS p	WITH (NOLOCK) ON i.index_id = p.index_id and i.object_id = p.object_id
WHERE (1=1)
	--AND ISNULL(user_updates,0) >= ISNULL((user_seeks + user_scans + user_lookups),0) --显示包含没有使用的约束在内的所有约束
	--AND ISNULL(user_updates,0) - ISNULL((user_seeks + user_scans + user_lookups),0)>0 --仅仅显示那些已经使用的索引
	--AND i.index_id > 1			-- 非第一索引
	--AND i.is_primary_key<>1		-- 不是作为主键被定义的
	--AND i.is_unique_constraint<>1         -- 不是UniqueConstraints
ORDER BY [Table Name], [index name]

 

还有一种是基于列查找重复索引

/* 执行这个脚本后,索引将会以三个报表的实行展现出来
请看下面:
1.  列出所有索引和约束的关键信息
2.  列出表潜在的冗余索引
3.  列出表潜在的反向索引
*/
-- 创建一个存放索引信息的表
DECLARE @AllIndexes TABLE (
 [Table ID] [int] NOT NULL,
 [Schema] [sysname] NOT NULL,
 [Table Name] [sysname] NOT NULL,
 [Index ID] [int] NULL,
 [Index Name] [nvarchar](128) NULL,
 [Index Type] [varchar](12) NOT NULL,
 [Constraint Type] [varchar](11) NOT NULL,
 [Object Type] [varchar](10) NOT NULL,
 [AllColName] [nvarchar](2078) NULL,
 [ColName1] [nvarchar](128) NULL,
 [ColName2] [nvarchar](128) NULL,
 [ColName3] [nvarchar](128) NULL,
 [ColName4] [nvarchar](128) NULL,
 [ColName5] [nvarchar](128) NULL,
 [ColName6] [nvarchar](128) NULL,
 [ColName7] [nvarchar](128) NULL,
 [ColName8] [nvarchar](128) NULL,
 [ColName9] [nvarchar](128) NULL,
 [ColName10] [nvarchar](128) NULL
)

--  加载索引信息到下面语句
INSERT INTO @AllIndexes
 ([Table ID],[Schema],[Table Name],[Index ID],[Index Name],[Index Type],[Constraint Type],[Object Type]
 ,[AllColName],[ColName1],[ColName2],[ColName3],[ColName4],[ColName5],[ColName6],[ColName7],[ColName8],
 [ColName9],[ColName10])
SELECT o.[object_id] AS [Table ID] ,u.[name] AS [Schema],o.[name] AS [Table Name],
 i.[index_id] AS [Index ID]
 , CASE i.[name]
 WHEN o.[name] THEN '** Same as Table Name **'
 ELSE i.[name] END AS [Index Name],
 CASE i.[type]
 WHEN 1 THEN 'CLUSTERED'
 WHEN 0 THEN 'HEAP'
 WHEN 2 THEN 'NONCLUSTERED'
 WHEN 3 THEN 'XML'
 ELSE 'UNKNOWN' END AS [Index Type],
 CASE
 WHEN (i.[is_primary_key]) = 1 THEN 'PRIMARY KEY'
 WHEN (i.[is_unique]) = 1 THEN 'UNIQUE'
 ELSE '' END AS [Constraint Type],
 CASE
 WHEN (i.[is_unique_constraint]) = 1
 OR (i.[is_primary_key]) = 1
 THEN 'CONSTRAINT'
 WHEN i.[type] = 0 THEN 'HEAP'
 WHEN i.[type] = 3 THEN 'XML INDEX'
 ELSE 'INDEX' END AS [Object Type],
 (SELECT COALESCE(c1.[name],'') FROM [sys].[columns] AS c1 INNER JOIN [sys].[index_columns] AS ic1
 ON c1.[object_id] = ic1.[object_id] AND c1.[column_id] = ic1.[column_id] AND ic1.[key_ordinal] = 1
 WHERE ic1.[object_id] = i.[object_id] AND ic1.[index_id] = i.[index_id]) +
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].['+ o.[name] + ']', i.[index_id], 2) IS NULL THEN ''
 ELSE ', '+INDEX_COL('[' + u.[name] + '].['+ o.[name] + ']', i.[index_id],2) END +
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id], 3) IS NULL THEN ''
 ELSE ', '+INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],3) END +
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id], 4) IS NULL THEN ''
 ELSE ', '+INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],4) END +
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].['+ o.[name] + ']', i.[index_id], 5) IS NULL THEN ''
 ELSE ', '+INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],5) END  +
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id], 6) IS NULL THEN ''
 ELSE ', '+INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],6) END +
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id], 7) IS NULL THEN ''
 ELSE ', '+INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id], 7) END +
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],8) IS NULL THEN ''
 ELSE ', '+INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],8) END +
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].['+ o.[name] + ']', i.[index_id], 9) IS NULL THEN ''
 ELSE ', '+INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],9) END +
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].['+ o.[name] + ']', i.[index_id], 10) IS NULL THEN ''
 ELSE ', '+INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],10) END  AS [AllColName],
 (SELECT COALESCE(c1.[name],'') FROM [sys].[columns] AS c1 INNER JOIN [sys].[index_columns] AS ic1
 ON c1.[object_id] = ic1.[object_id] AND c1.[column_id] = ic1.[column_id] AND ic1.[key_ordinal] = 1
 WHERE ic1.[object_id] = i.[object_id] AND ic1.[index_id] = i.[index_id])   AS [ColName1],
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].['+ o.[name] + ']', i.[index_id], 2) IS NULL THEN ''
 ELSE INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],2) END AS [ColName2],
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id], 3) IS NULL THEN ''
 ELSE INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],3) END AS [ColName3],
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].['+ o.[name] + ']', i.[index_id], 4) IS NULL THEN ''
 ELSE INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],4) END AS [ColName4],
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].['+ o.[name] + ']', i.[index_id], 5) IS NULL THEN ''
 ELSE INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],5) END AS [ColName5],
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].['+ o.[name] + ']', i.[index_id], 6) IS NULL THEN ''
 ELSE INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],6) END AS [ColName6],
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id], 7) IS NULL THEN ''
 ELSE INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],7) END AS [ColName7],
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].['+ o.[name] + ']', i.[index_id],8) IS NULL THEN ''
 ELSE INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],8) END AS [ColName8],
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].['+ o.[name] + ']', i.[index_id], 9) IS NULL THEN ''
 ELSE INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],9) END AS [ColName9],
 CASE
 WHEN INDEX_COL('[' + u.[name] + '].['+ o.[name] + ']', i.[index_id], 10) IS NULL THEN ''
 ELSE INDEX_COL('[' + u.[name] + '].[' + o.[name] + ']', i.[index_id],10) END AS [ColName10]
FROM [sys].[objects] AS o WITH (NOLOCK)
 LEFT OUTER JOIN [sys].[indexes] AS i WITH (NOLOCK)
 ON o.[object_id] = i.[object_id]
 JOIN [sys].[schemas] AS u WITH (NOLOCK)
 ON o.[schema_id] = u.[schema_id]
WHERE o.[type] = 'U' --AND i.[index_id] < 255
 AND o.[name] NOT IN ('dtproperties')
 AND i.[name] NOT LIKE '_WA_Sys_%'

-----------
SELECT 'Listing All Indexes' AS [Comments]

SELECT I.*
 FROM @AllIndexes AS I
 ORDER BY [Table Name]

-----------
SELECT 'Listing Possible Redundant Index keys' AS [Comments]

SELECT DISTINCT I.[Table Name], I.[Index Name] ,I.[Index Type],  I.[Constraint Type], I.[AllColName]
 FROM @AllIndexes AS I
 JOIN @AllIndexes AS I2
 ON I.[Table ID] = I2.[Table ID]
 AND I.[ColName1] = I2.[ColName1]
 AND I.[Index Name] <> I2.[Index Name]
 AND I.[Index Type] <> 'XML'
 ORDER BY I.[Table Name], I.[AllColName]

----------
SELECT 'Listing Possible Reverse Index keys' AS [Comments]

SELECT DISTINCT I.[Table Name], I.[Index Name], I.[Index Type],  I.[Constraint Type], I.[AllColName]
 FROM @AllIndexes AS I
 JOIN @AllIndexes AS I2
 ON I.[Table ID] = I2.[Table ID]
 AND I.[ColName1] = I2.[ColName2]
 AND I.[ColName2] = I2.[ColName1]
 AND I.[Index Name] <> I2.[Index Name]
 AND I.[Index Type] <> 'XML'

4. 删除重复索引

把它们暴露出来,剩下的事情就很简单了,删除。

   1: USE test_table; 
   2: GO 
   3: --从表Test_Tabler删除索引 IX2 
   4: DROP IX2 
   5: ON Test_Tabler 
   6: GO 

 

5. 总结

设计数据库查询语句时,需要相当的留意重复索引可能引起DML操作的性能降低。设计新数据库之前最好检查一下已有数据库的索引。在自己的数据库发现 重复索引,明智的选择就是果断删除它,删除之前最好还是先做数据库备份,这样可以避免删除后对数据库造成重大影响。其实,删除重复索引不仅能提高性能而且 可以给数据库瘦身,同时备份文件也会变小。

 

在此谢谢读完这篇博客,有什么写的不对的地方请指正

有帮助就推荐下,有感想就下下来,不满意就留言,有问题就更正。

时间: 2024-09-30 20:39:15

SQL Server 性能优化之——重复索引的相关文章

Sql Server 性能优化之包含列

原文:Sql Server 性能优化之包含列 Sql Server 性能优化之包含列        导读:数据数优化查询一直是个比较热门的话题,小生在这方面也只能算是个入门生.今 天我们就讲下数据库包含列这个一项的作用及带来的优化效果           引用下MSDN里面的一段解释:        当查询中的所有列都作为键列或非键列包含在索引中时,带有包含性非键列的索引可以显 著提高查询性能. 这样可以实现性能提升,因为查询优化器可以在索引中找到所有列值:不 访问表或聚集索引数据,从而减少磁盘

sql server 性能优化之nolock_MsSql

伴随着时间的增长,公司的数据库会越来越多,查询速度也会越来越慢.打开数据库看到几十万条的数据,查询起来难免不废时间. 要提升SQL的查询效能,一般来说大家会以建立索引(index)为第一考虑.其实除了index的建立之外,当我们在下SQL Command时,在语法中加一段WITH (NOLOCK)可以改善在线大量查询的环境中数据集被LOCK的现象藉此改善查询的效能. 不过有一点千万要注意的就是,WITH (NOLOCK)的SQL SELECT有可能会造成Dirty Read,就是读到无效的数据.

sql server 性能优化之nolock

伴随着时间的增长,公司的数据库会越来越多,查询速度也会越来越慢.打开数据库看到几十万条的数据,查询起来难免不废时间. 要提升SQL的查询效能,一般来说大家会以建立索引(index)为第一考虑.其实除了index的建立之外,当我们在下SQL Command时,在语法中加一段WITH (NOLOCK)可以改善在线大量查询的环境中数据集被LOCK的现象藉此改善查询的效能. 不过有一点千万要注意的就是,WITH (NOLOCK)的SQL SELECT有可能会造成Dirty Read,就是读到无效的数据.

SQL Server 性能优化(一)——简介

原文:SQL Server 性能优化(一)--简介 一.性能优化的理由: 听起来有点多余,但是还是详细说一下: 1.节省成本:这里的成本不一定是钱,但是基本上可以变相认为是节省钱.性能上去了,本来要投入的硬件就可以减缓投入,从另外一个角度看来它就是节省了钱. 2.增加效率:对于客户来说,性能上去了,他们的工作效率也高了. 3.降低挫折感:性能底下,客户抱怨,无疑是对自己心灵上的打击. 二.性能误区: 误区 现实 如果处理器使用率很高,那么需要添加更快的处理器 某一部分导致了性能问题 80%的性能

浅析SQL Server性能优化之索引运算

技术准备 基于SQL Server2008R2版本,利用微软的一个更简洁的案例库(Northwind)进行解析. 简介 所谓的索引应用就是在我们日常写的T-SQL语句中,如何利用现有的索引项,再分析的话就是我们所写的查询条件,其实大部分情况也无非以下几种: 1.等于谓词:select ...where...column=@parameter 2.比较谓词:select ...where...column> or < or <> or <= or >= @paramete

SQL Server 性能优化之——系统化方法提高性能

原文http://www.cnblogs.com/BoyceYang/archive/2013/06/15/3138142.html 阅读导航 1. 概述 2. 规范逻辑数据库设计 3. 使用高效索引设计 4. 使用高效的查询设计 5. 使用技术分析低性能 6. 总结   1. 概述 在比较大的范围内找出能够大幅提高性能的区域,并且专注于分析这个区域,这是最有效的优化SQL Server性能的方式.否则,大量的时间和精力可能被浪费在不能提高很大性能的区域.在这里并没有讨论关于多用户并发所带来的性

SQL Server性能优化

一.概述 在数据库应用中,速度是一个永恒的话题.有许多因素会影响数据库的性能表现,例如:操作系统,硬件方面的因素,如内存和磁盘空间,访问数据库的应用软件.除此之外,数据库本身的设计也是一个影响性能的重要因素. 本文要讨论的是我们可以采取哪些措施提高SQL Server数据库的性能.讨论的焦点主要集中在SQL Server 2000为视图创建索引的能力,以及如何使用Index Tuning Wizard(ITW,索引调整向导)优化索引.另外,我们还要讨论如何确保查询充分地利用了索引以及数据库统计信

谈谈Tempdb对SQL Server性能优化有何影响_MsSql

先给大家巩固tempdb的基础知识 简介: tempdb是SQLServer的系统数据库一直都是SQLServer的重要组成部分,用来存储临时对象.可以简单理解tempdb是SQLServer的速写板.应用程序与数据库都可以使用tempdb作为临时的数据存储区.一个实例的所有用户都共享一个Tempdb.很明显,这样的设计不是很好.当多个应用程序的数据库部署在同一台服务器上的时候,应用程序共享tempdb,如果开发人员不注意对Tempdb的使用就会造成这些数据库相互影响从而影响应用程序. 特性:

谈谈Tempdb对SQL Server性能优化有何影响

先给大家巩固tempdb的基础知识 简介: tempdb是SQLServer的系统数据库一直都是SQLServer的重要组成部分,用来存储临时对象.可以简单理解tempdb是SQLServer的速写板.应用程序与数据库都可以使用tempdb作为临时的数据存储区.一个实例的所有用户都共享一个Tempdb.很明显,这样的设计不是很好.当多个应用程序的数据库部署在同一台服务器上的时候,应用程序共享tempdb,如果开发人员不注意对Tempdb的使用就会造成这些数据库相互影响从而影响应用程序. 特性: