您的位置:首页 > 数据库

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

2013-06-17 09:18 429 查看
原文/article/5325299.html

阅读导航

1.概述

2.什么是重复索引

3.查找重复索引

4.删除重复索引

5.总结

1.概述

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

2.什么是重复索引

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

CREATETABLETest_Table

(

Col1intNOTNULLPRIMARYKEY,

Col2varchr(30)NOTNULL,

Col3varchr(30)NOTNULL,

Col4varchr(30)NOTNULL,

)


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

CREATEUNIQUECLUSTEREDINDEXIX1ONTest_Table(Col1);


CREATEINDEXIX2ONTest_Table(Col1);


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

CREATEINDEXIX3ONTest_Table(Col4)

INCLUDE(Col2,Col3);


CREATEINDEXIX4ONTest_Table(Col4)

INCLUDE(Col3,Col2);


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

CREATEINDEXIX5ONTest_Table(Col4)

INCLUDE(Col2,Col3);



CREATEUNIQUEINDEXIX6ONTest_Table(Col4)
INCLUDE(Col2,Col3);


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

CREATEINDEXIX7ONTest_Table(Col3,Col2);



CREATEINDEXIX8ONTest_Table(Col3,Col2);

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

3.查找重复索引

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


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

2)使用sp_helpindex查找重复索引

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



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.nameAS[TableName],
i.nameAS[IndexName],
i.type_desc,
ISNULL(user_updates,0)AS[TotalWrites],
ISNULL(user_seeks+user_scans+user_lookups,0)AS[TotalReads],
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/1024asSpaceInMB
FROMsys.indexesASiWITH(NOLOCK)
LEFTOUTERJOINsys.dm_db_index_usage_statsASs WITH(NOLOCK)ONs.object_id=i.object_idANDi.index_id=s.index_idANDs.database_id=db_id()ANDobjectproperty(s.object_id,'IsUserTable')=1
INNERJOIN sys.tables ASt WITH(NOLOCK)ONi.object_id=t.object_id
INNERJOIN sys.schemas ASsch WITH(NOLOCK)ONt.schema_id=sch.schema_id
LEFTOUTERJOINsys.dm_db_partition_stats ASp WITH(NOLOCK)ONi.index_id=p.index_idandi.object_id=p.object_id
WHERE(1=1)
--ANDISNULL(user_updates,0)>=ISNULL((user_seeks+user_scans+user_lookups),0)--显示包含没有使用的约束在内的所有约束
--ANDISNULL(user_updates,0)-ISNULL((user_seeks+user_scans+user_lookups),0)>0--仅仅显示那些已经使用的索引
--ANDi.index_id>1 --非第一索引
--ANDi.is_primary_key<>1 --不是作为主键被定义的
--ANDi.is_unique_constraint<>1--不是UniqueConstraints
ORDERBY[TableName],[indexname]


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

/*执行这个脚本后,索引将会以三个报表的实行展现出来
请看下面:
1.列出所有索引和约束的关键信息
2.列出表潜在的冗余索引
3.列出表潜在的反向索引
*/
--创建一个存放索引信息的表
DECLARE@AllIndexesTABLE(
[TableID][int]NOTNULL,
[Schema][sysname]NOTNULL,
[TableName][sysname]NOTNULL,
[IndexID][int]NULL,
[IndexName][nvarchar](128)NULL,
[IndexType][varchar](12)NOTNULL,
[ConstraintType][varchar](11)NOTNULL,
[ObjectType][varchar](10)NOTNULL,
[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
)

--加载索引信息到下面语句
INSERTINTO@AllIndexes
([TableID],[Schema],[TableName],[IndexID],[IndexName],[IndexType],[ConstraintType],[ObjectType]
,[AllColName],[ColName1],[ColName2],[ColName3],[ColName4],[ColName5],[ColName6],[ColName7],[ColName8],
[ColName9],[ColName10])
SELECTo.[object_id]AS[TableID],u.[name]AS[Schema],o.[name]AS[TableName],
i.[index_id]AS[IndexID]
,CASEi.[name]
WHENo.[name]THEN'**SameasTableName**'
ELSEi.[name]ENDAS[IndexName],
CASEi.[type]
WHEN1THEN'CLUSTERED'
WHEN0THEN'HEAP'
WHEN2THEN'NONCLUSTERED'
WHEN3THEN'XML'
ELSE'UNKNOWN'ENDAS[IndexType],
CASE
WHEN(i.[is_primary_key])=1THEN'PRIMARYKEY'
WHEN(i.[is_unique])=1THEN'UNIQUE'
ELSE''ENDAS[ConstraintType],
CASE
WHEN(i.[is_unique_constraint])=1
OR(i.[is_primary_key])=1
THEN'CONSTRAINT'
WHENi.[type]=0THEN'HEAP'
WHENi.[type]=3THEN'XMLINDEX'
ELSE'INDEX'ENDAS[ObjectType],
(SELECTCOALESCE(c1.[name],'')FROM[sys].[columns]ASc1INNERJOIN[sys].[index_columns]ASic1
ONc1.[object_id]=ic1.[object_id]ANDc1.[column_id]=ic1.[column_id]ANDic1.[key_ordinal]=1
WHEREic1.[object_id]=i.[object_id]ANDic1.[index_id]=i.[index_id])+
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],2)ISNULLTHEN''
ELSE','+INDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],2)END+
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],3)ISNULLTHEN''
ELSE','+INDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],3)END+
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],4)ISNULLTHEN''
ELSE','+INDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],4)END+
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],5)ISNULLTHEN''
ELSE','+INDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],5)END+
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],6)ISNULLTHEN''
ELSE','+INDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],6)END+
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],7)ISNULLTHEN''
ELSE','+INDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],7)END+
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],8)ISNULLTHEN''
ELSE','+INDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],8)END+
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],9)ISNULLTHEN''
ELSE','+INDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],9)END+
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],10)ISNULLTHEN''
ELSE','+INDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],10)ENDAS[AllColName],
(SELECTCOALESCE(c1.[name],'')FROM[sys].[columns]ASc1INNERJOIN[sys].[index_columns]ASic1
ONc1.[object_id]=ic1.[object_id]ANDc1.[column_id]=ic1.[column_id]ANDic1.[key_ordinal]=1
WHEREic1.[object_id]=i.[object_id]ANDic1.[index_id]=i.[index_id])AS[ColName1],
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],2)ISNULLTHEN''
ELSEINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],2)ENDAS[ColName2],
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],3)ISNULLTHEN''
ELSEINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],3)ENDAS[ColName3],
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],4)ISNULLTHEN''
ELSEINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],4)ENDAS[ColName4],
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],5)ISNULLTHEN''
ELSEINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],5)ENDAS[ColName5],
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],6)ISNULLTHEN''
ELSEINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],6)ENDAS[ColName6],
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],7)ISNULLTHEN''
ELSEINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],7)ENDAS[ColName7],
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],8)ISNULLTHEN''
ELSEINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],8)ENDAS[ColName8],
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],9)ISNULLTHEN''
ELSEINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],9)ENDAS[ColName9],
CASE
WHENINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],10)ISNULLTHEN''
ELSEINDEX_COL('['+u.[name]+'].['+o.[name]+']',i.[index_id],10)ENDAS[ColName10]
FROM[sys].[objects]ASoWITH(NOLOCK)
LEFTOUTERJOIN[sys].[indexes]ASiWITH(NOLOCK)
ONo.[object_id]=i.[object_id]
JOIN[sys].[schemas]ASuWITH(NOLOCK)
ONo.[schema_id]=u.[schema_id]
WHEREo.[type]='U'--ANDi.[index_id]<255
ANDo.[name]NOTIN('dtproperties')
ANDi.[name]NOTLIKE'_WA_Sys_%'

-----------
SELECT'ListingAllIndexes'AS[Comments]

SELECTI.*
FROM@AllIndexesASI
ORDERBY[TableName]

-----------
SELECT'ListingPossibleRedundantIndexkeys'AS[Comments]

SELECTDISTINCTI.[TableName],I.[IndexName],I.[IndexType],I.[ConstraintType],I.[AllColName]
FROM@AllIndexesASI
JOIN@AllIndexesASI2
ONI.[TableID]=I2.[TableID]
ANDI.[ColName1]=I2.[ColName1]
ANDI.[IndexName]<>I2.[IndexName]
ANDI.[IndexType]<>'XML'
ORDERBYI.[TableName],I.[AllColName]

----------
SELECT'ListingPossibleReverseIndexkeys'AS[Comments]

SELECTDISTINCTI.[TableName],I.[IndexName],I.[IndexType],I.[ConstraintType],I.[AllColName]
FROM@AllIndexesASI
JOIN@AllIndexesASI2
ONI.[TableID]=I2.[TableID]
ANDI.[ColName1]=I2.[ColName2]
ANDI.[ColName2]=I2.[ColName1]
ANDI.[IndexName]<>I2.[IndexName]
ANDI.[IndexType]<>'XML'


4.删除重复索引

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

USEtest_table;

GO

--从表Test_Tabler删除索引IX2

DROPIX2

ONTest_Tabler

GO


5.总结

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

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

有帮助就推荐下,有感想就下下来,不满意就留言,有问题就更正。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐
章节导航