配色: 字号:
SQL Server索引维护指导
2016-09-05 | 阅:  转:  |  分享 
  
SQLServer索引维护指导

索引在数据库相关工作者的日常工作中占据了很重要的位置,索引需要牵涉到索引创建、优化和维护多方面的工作,本文以实例结合相关原理来介绍索引维护相关的知识。文中的相关代码,也可以满足多数情况下索引的维护需求。



实现步骤



1、以什么标准判断索引是否需要维护?



2、索引维护的方法有哪些?



3、能否方便地整理出比较通用的维护过程,实现自动化维护?



一、以什么标准判断索引是否需要维护?



由于本文集中讨论索引维护相关,所以我们暂且抛开创建的不合理的那些索引,仅从维护的角度来讨论。从索引维护的角度来讲,最主要的参考标准就是索引碎片的大小。通常情况下,索引碎片在10%以内,是可以接受的。下面介绍获取索引碎片的方法:



SQLServer2000:DBCCSHOWCONTIG



SQLServer2005:sys.dm_db_index_physical_stats



实例(取db_test数据库所有索引碎片相关信息):



SQLServer2000:



USE[db_test];

GO

DBCCSHOWCONTIGWITHTABLERESULTS,ALL_INDEXES

GO

SQLServer2005:

DECLARE@db_nameVARCHAR(256)

SET@db_name=''db_test''

SELECT

db_name(a.database_id)[db_name],

c.name[table_name],

b.name[index_name],

a.avg_fragmentation_in_percent

FROM

sys.dm_db_index_physical_stats(DB_ID(@db_name),NULL,NULL,NULL,''Limited'')ASa

JOIN

sys.indexesASbONa.object_id=b.object_idANDa.index_id=b.index_id

JOIN

sys.tablesAScONa.object_id=c.object_id

WHERE

a.index_id>0

ANDa.avg_fragmentation_in_percent>5-–碎片程度大于5



二、索引维护的方法有哪些?



注:维护方式的选择,一方面要考虑是否是联机维护,另一方面就是速度上的考虑。一般碎片<=30%时,使用重新组织的方法速度比索引重建快;碎片>30%时,索引重建的速度比重新组织要快。



1、联机维护



SQLServer2000:



DBCCINDEXDEFRAG重新组织索引,占用资源少,锁定资源周期短,可联机进行。



SQLServer2005:



联机重新组织:



ALTERINDEX[index_name]ON[table_name]



REORGANIZE;



2、联机重建:



ALTERINDEX[index_name]ON[table_name]



REBUILDWITH(FILLFACTOR=85,SORT_IN_TEMPDB=OFF,



STATISTICS_NORECOMPUTE=ON,ONLINE=ON);



3、脱机维护



SQLServer2000:DBCCDBREINDEX



SQLServer2005:ALTERINDEX[indexname]ON[table_name]REBUILD;



CREATEINDEXWITHDROP_EXISTING



4、能否方便地整理出比较通用的维护过程,实现自动化维护?



a)获取及查看所有索引的碎片情况



SQLServer2000:



/

描述:获取服务器上所有数据库的逻辑碎片率>5的索引信息

适用:SqlServer2000以后版本

/

SETNOCOUNTON

DECLARE@db_namevarchar(128)

DECLARE@tablenamevarchar(128)

DECLARE@table_schemavarchar(128)

DECLARE@execstrvarchar(255)

DECLARE@objectidint

DECLARE@indexidint

DECLARE@fragdecimal

DECLARE@maxfragdecimal

DECLARE@sqlvarchar(8000)

--Decideonthemaximumfragmentationtoallowfor.

SELECT@maxfrag=5

--Createthetable.

ifnotexists(select1fromsys.tableswherename=''dba_manage_index_defrag'')

createtabledba_manage_index_defrag

([db_name]varchar(255)

,[table_name]varchar(255)

,[index_name]varchar(255)

,avg_fragmentation_in_percentreal

,write_timedatetimedefaultgetdate()

)

ifnotexists(select1fromdbo.sysobjectswherename=''dba_manage_index_defrag_temp'')

CREATETABLEdba_manage_index_defrag_temp(

[db_name]char(255)default'''',

ObjectNamechar(255),

ObjectIdint,

IndexNamechar(255),

IndexIdint,

Lvlint,

CountPagesint,

CountRowsint,

MinRecSizeint,

MaxRecSizeint,

AvgRecSizeint,

ForRecCountint,

Extentsint,

ExtentSwitchesint,

AvgFreeBytesint,

AvgPageDensityint,

ScanDensitydecimal,

BestCountint,

ActualCountint,

LogicalFragdecimal,

ExtentFragdecimal)



--Declareacursor.

DECLAREdatabasesCURSORFOR

select

name

from

master.dbo.sysdatabases

where

dbid>4



--Openthecursor.

opendatabases

fetchdatabasesinto@db_name

while(@@fetch_status=0)

begin

insertintodba_manage_index_defrag_temp

(ObjectName,

ObjectId,

IndexName,

IndexId,

Lvl,

CountPages,

CountRows,

MinRecSize,

MaxRecSize,

AvgRecSize,

ForRecCount,

Extents,

ExtentSwitches,

AvgFreeBytes,

AvgPageDensity,

ScanDensity,

BestCount,

ActualCount,

LogicalFrag,

ExtentFrag)

exec(''use[''+@db_name+''];

dbccshowcontig

with

FAST,

TABLERESULTS,

ALL_INDEXES,

NO_INFOMSGS'')

update

dba_manage_index_defrag_temp

set

[db_name]=@db_name

where

[db_name]=''''

fetchnextfromdatabasesinto@db_name

end



closedatabases

deallocatedatabases

insertintodba_manage_index_defrag

([db_name]

,[table_name]

,[index_name]

,avg_fragmentation_in_percent

)

select

[db_name],

ObjectName[table_name],

indexname[index_name],

LogicalFrag[avg_fragmentation_in_percent]

from

dba_manage_index_defrag_temp

where

logicalfrag>5

--Deletethetemporarytable.

DROPTABLEdba_manage_index_defrag_temp



GO

SELECTFROMdba_manage_index_defrag--查看结果



SQLServer2005:



/



描述:只显示逻辑碎片率大于5%的索引信息

限制:针对SqlServer2005以后版本。

功能:对数据库服务器所有非系统数据库进行索引碎片检查

返回碎片率>5%的索引信息



/

createprocp_dba_manage_get_index_defrage

as

setnocounton

ifnotexists(select1fromsys.tableswherename=''dba_manage_index_defrag'')

createtabledba_manage_index_defrag

([db_name]varchar(255)

,[table_name]varchar(255)

,[index_name]varchar(255)

,avg_fragmentation_in_percentreal

,write_timedatetimedefaultgetdate()

)

declare@db_namenvarchar(40)

set@db_name=''''

declarecur_db_namecursorfor

select

name

from

sys.databases

where

database_id>4andstate=0



opencur_db_name

fetchcur_db_nameinto@db_name

while(@@fetch_status=0)

begin

insertintodba_manage_index_defrag

([db_name]

,table_name

,index_name

,avg_fragmentation_in_percent)

SELECT

db_name(a.database_id)[db_name],

c.name[table_name],

b.name[index_name],

a.avg_fragmentation_in_percent

FROM

sys.dm_db_index_physical_stats(DB_ID(@db_name),null,NULL,NULL,''Limited'')ASa

JOIN

sys.indexesASbONa.object_id=b.object_idANDa.index_id=b.index_id

join

sys.tablesascona.object_id=c.object_id

where

a.index_id>0

anda.avg_fragmentation_in_percent>5

fetchnextfromcur_db_nameinto@db_name

end



CLOSEcur_db_name

DEALLOCATEcur_db_name



GO

selectfromdba_manage_index_defrag–查看结果



b)根据索引碎片的情况自动选择合适的处理方法



针对SqlServer2000的联机维护:



/Performa''USE''toselectthedatabaseinwhichtorunthescript./

--Declarevariables

SETNOCOUNTON;

DECLARE@tablenamevarchar(128);

DECLARE@execstrvarchar(255);

DECLARE@objectidint;

DECLARE@indexidint;

DECLARE@fragdecimal;

DECLARE@maxfragdecimal;

--Decideonthemaximumfragmentationtoallowfor.

SELECT@maxfrag=30.0;



--Declareacursor.

DECLAREtablesCURSORFOR

SELECTTABLE_SCHEMA+''.''+TABLE_NAME--MSDN上面直接使用TABLE_NAME,如果SCHEMA不是DBO就会出错

FROMINFORMATION_SCHEMA.TABLES

WHERETABLE_TYPE=''BASETABLE'';



--Createthetable.

CREATETABLE#fraglist(

ObjectNamechar(255),

ObjectIdint,

IndexNamechar(255),

IndexIdint,

Lvlint,

CountPagesint,

CountRowsint,

MinRecSizeint,

MaxRecSizeint,

AvgRecSizeint,

ForRecCountint,

Extentsint,

ExtentSwitchesint,

AvgFreeBytesint,

AvgPageDensityint,

ScanDensitydecimal,

BestCountint,

ActualCountint,

LogicalFragdecimal,

ExtentFragdecimal);



--Openthecursor.

OPENtables;



--Loopthroughallthetablesinthedatabase.

FETCHNEXT

FROMtables

INTO@tablename;



WHILE@@FETCH_STATUS=0

BEGIN

--Dotheshowcontigofallindexesofthetable

INSERTINTO#fraglist

EXEC(''DBCCSHOWCONTIG(''''''+@tablename+'''''')

WITHFAST,TABLERESULTS,ALL_INDEXES,NO_INFOMSGS'');

FETCHNEXT

FROMtables

INTO@tablename;

END;



--Closeanddeallocatethecursor.

CLOSEtables;

DEALLOCATEtables;



--Declarethecursorforthelistofindexestobedefragged.

DECLAREindexesCURSORFOR

SELECTObjectName,ObjectId,IndexId,LogicalFrag

FROM#fraglist

WHERELogicalFrag>=@maxfrag

ANDINDEXPROPERTY(ObjectId,IndexName,''IndexDepth'')>0;



--Openthecursor.

OPENindexes;



--Loopthroughtheindexes.

FETCHNEXT

FROMindexes

INTO@tablename,@objectid,@indexid,@frag;



WHILE@@FETCH_STATUS=0

BEGIN

PRINT''ExecutingDBCCINDEXDEFRAG(0,''+RTRIM(@tablename)+'',

''+RTRIM(@indexid)+'')-fragmentationcurrently''

+RTRIM(CONVERTwww.wang027.com(varchar(15),@frag))+''%'';

SELECT@execstr=''DBCCINDEXDEFRAG(0,''+RTRIM(@objectid)+'',

''+RTRIM(@indexid)+'')'';

EXEC(@execstr);



FETCHNEXT

FROMindexes

INTO@tablename,@objectid,@indexid,@frag;

END;



--Closeanddeallocatethecursor.

CLOSEindexes;

DEALLOCATEindexes;



--Deletethetemporarytable.

DROPTABLE#fraglist;

GO



针对SQLServer2000的脱机维护:



sp_msforeachtable@command1="dbccdbreindex(''?'','''',85)"







针对SQLServer2005的通用维护过程



(碎片小于30%的联机组织,碎片>=30%的脱机重建):



--ensureaUSEstatementhasbeenexecutedfirst.

SETNOCOUNTON;

DECLARE@objectidint;

DECLARE@indexidint;

DECLARE@partitioncountbigint;

DECLARE@schemanamesysname;

DECLARE@objectnamesysname;

DECLARE@indexnamesysname;

DECLARE@partitionnumbigint;

DECLARE@partitionsbigint;

DECLARE@fragfloat;

DECLARE@commandvarchar(8000);

--ensurethetemporarytabledoesnotexist

IFEXISTS(SELECTnameFROMsys.objectsWHEREname=''work_to_do'')

DROPTABLEwork_to_do;

--conditionallyselectfromthefunction,convertingobjectandindexIDstonames.

SELECT

object_idASobjectid,

index_idASindexid,

partition_numberASpartitionnum,

avg_fragmentation_in_percentASfrag

INTOwork_to_do

FROMsys.dm_db_index_physical_stats(DB_ID(),NULL,NULL,NULL,''LIMITED'')

WHEREavg_fragmentation_in_percent>10.0ANDindex_id>0;

--Declarethecursorforthelistofpartitionstobeprocessed.

DECLAREpartitionsCURSORFORSELECTFROMwork_to_do;

--Openthecursor.

OPENpartitions;



--Loopthroughthepartitions.

FETCHNEXT

FROMpartitions

INTO@objectid,@indexid,@partitionnum,@frag;



WHILE@@FETCH_STATUS=0

BEGIN;

SELECT@objectname=o.name,@schemaname=s.name

FROMsys.objectsASo

JOINsys.schemasassONs.schema_id=o.schema_id

WHEREo.object_id=@objectid;



SELECT@indexname=name

FROMsys.indexes

WHEREobject_id=@objectidANDindex_id=@indexid;



SELECT@partitioncount=count()

FROMsys.partitions

WHEREobject_id=@objectidANDindex_id=@indexid;



--30isanarbitrarydecisionpointatwhichtoswitchbetweenreorganizingand



rebuilding

IF@frag<30.0and@frag>5

BEGIN;

SELECT@command=''ALTERINDEX''+@indexname+''ON''+@schemaname+''.''+@objectname



+''REORGANIZE'';

IF@partitioncount>1

SELECT@command=@command+''PARTITION=''+CONVERT(CHAR,@partitionnum);

EXEC(@command);

END;



IF@frag>=30.0

BEGIN;

SELECT@command=''ALTERINDEX''+@indexname+''ON''+@schemaname+''.''+@objectname+



''REBUILD'';

IF@partitioncount>1

SELECT@command=@command+''PARTITION=''+CONVERT(CHAR,@partitionnum);

EXEC(@command);

END;

PRINT''Executed''+@command;



FETCHNEXTFROMpartitionsINTO@objectid,@indexid,@partitionnum,@frag;

END;

--Closeanddeallocatethecursor.

CLOSEpartitions;

DEALLOCATEpartitions;



--dropthetemporarytable

IFEXISTS(SELECTnameFROMsys.objectsWHEREname=''work_to_do'')

DROPTABLEwork_to_do;

GO



总结



索引的维护是有参考依据的,应该根据具体的碎片情况以及是否需要联机操作等需求,采用合理的维护方法。自动化的索引维护策略是可行的。

献花(0)
+1
(本文系thedust79首藏)