一、索引的利弊
优点: 1.大大加快数据的检索速度;
2.创建唯一性索引,保证数据库表中每一行数据的唯一性;
3.加速表和表之间的连接;
4.在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。
缺点: 1.索引需要占物理空间;
2.当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度;
3.索引过多,生成执行计划的时候,也可能导致优化器需要更多的时间去选择一个合适的索引,消耗更多的CPU时间。
二、 索引的优化方法 :
1 索引缺失-------增加索引----新增
2 索引过多-------合并索引----删除合并
3 索引碎片-------索引维护----重建、重组索引
TIP: 索引碎片会降低索引页的数据密度,在查询时造成额外的I/O。
4 更新统计信息
三、索引的优化思路
通过作业调用存储过程,定期、自动地完成索引的优化维护,并记录日志;
索引的整合,不合理索引的删除合并主要还是依赖人工判断、取舍,暂时没想到比较好的自动化处理的办法。
四、相关的存储过程源码
1、索引缺失,根据系统视图,自动创建可能带来最大性能提升的10条索引,并记录日志:
建表 DBA_MissingIndexCreateRecord
IF OBJECT_ID('DBA_MissingIndexCreateRecord') IS NOT NULL DROP TABLE DBA_MissingIndexCreateRecord
CREATE TABLE [dbo].[DBA_MissingIndexCreateRecord](
[Datekey] [INT] NULL,
[PossibleImprovement] [FLOAT] NULL,
[Last_User_Seek] [DATETIME] NULL,
[Last_User_Scan] [DATETIME] NULL,
[DBName] [NVARCHAR](128) NULL,
[TableName] [NVARCHAR](128) NULL,
[IndexName] [NVARCHAR](4000) NULL,
[CreateSql] [NVARCHAR](4000) NULL,
[Status] [INT] NOT NULL
)
GO
ALTER TABLE [dbo].[DBA_MissingIndexCreateRecord] ADD DEFAULT ((0)) FOR [Status]
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'日期' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'Datekey'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'可能的性能提高' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'PossibleImprovement'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'由可能使用了组中建议索引的用户查询所导致的上次查找日期和时间' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'Last_User_Seek'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'由可能使用了组中建议索引的用户查询所导致的上次扫描日期和时间' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'Last_User_Scan'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'数据库名称' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'DBName'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'表名' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'TableName'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引名称' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'IndexName'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'执行脚本' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'CreateSql'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'状态 0未执行 1已执行' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'Status'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'缺失索引创建记录表' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord'
GO
View Code
过程 DBAMissingIndexCreate
CREATE PROCEDURE [dbo].[DBAMissingIndexCreate] @Result INT = 0 OUTPUT ----返回结果
AS /*
创建者:TurboWay
申请人:TurboWay
所属模板:数据库维护
所属项目:数据库索引维护
过程说明:top10缺失索引创建并记录到 DBA_DBA_MissingIndexCreateRecord
创建时间:2017-06-27
使用索引:
返回字段信息:
@Result 返回结果 0成功,-2失败
*/
BEGIN
SET NOCOUNT ON;
DECLARE @undo INT = 1;
DECLARE @CreateSql NVARCHAR(MAX);
DECLARE @UUID BIGINT;
SET @UUID = CONVERT(BIGINT,(((((((YEAR(GETDATE())-1990)*12+MONTH(GETDATE()))*31+DAY(GETDATE()))*24+
CONVERT(VARCHAR(2),DATEPART(HOUR,GETDATE())))*60+CONVERT(VARCHAR(2),DATEPART(MINUTE,GETDATE())))*60+
CONVERT(VARCHAR(2),DATEPART(SECOND,GETDATE())))))*1000000+ CAST(CEILING(RAND() * 999999) AS BIGINT)
BEGIN TRY
/*记录TOP10缺失索引*/
INSERT INTO DBA_MissingIndexCreateRecord(Datekey,PossibleImprovement,Last_User_Seek,Last_User_Scan,DBName,TableName,IndexName,CreateSql)
SELECT a.Datekey,
a.PossibleImprovement,
a.last_user_seek,
a.last_user_scan,
a.DbName,
a.TableName,
CASE WHEN LEN(a.Index_Name) >= 128 THEN SUBSTRING(a.Index_Name,0,CHARINDEX( '_i',a.Index_Name))+'_IncloudeTooMany'+CAST((@UUID+ROW_NUMBER()OVER(ORDER BY PossibleImprovement DESC)) AS NVARCHAR(30)) ELSE a.Index_Name END AS NewIndexName, --索引名有长度限制
CASE WHEN LEN(a.Index_Name) >= 128 THEN REPLACE(CreateSql,a.Index_Name,SUBSTRING(a.Index_Name,0,CHARINDEX( '_i',a.Index_Name))+'_IncloudeTooMany'+CAST((@UUID+ROW_NUMBER()OVER(ORDER BY PossibleImprovement DESC)) AS NVARCHAR(30))) ELSE CreateSql END AS NewCreateSql
FROM (
SELECT TOP 10
CONVERT(INT,CONVERT(NVARCHAR(8),GETDATE(),112)) AS Datekey,
CAST(avg_total_user_cost * avg_user_impact * ( user_scans + user_seeks )AS NUMERIC(18,2)) AS PossibleImprovement ,
last_user_seek ,
last_user_scan ,
DB_NAME() AS DbName ,
OBJECT_NAME(D.object_id) AS TableName ,
REPLACE(REPLACE(ISNULL('x' + REPLACE(equality_columns, ', ', '_x'), '')
+ CASE WHEN equality_columns IS NOT NULL
AND inequality_columns IS NOT NULL
THEN '_'
ELSE ''
END + ISNULL('x' + REPLACE(inequality_columns, ', ',
'_x'), '')
+ CASE WHEN included_columns IS NOT NULL
AND included_columns IS NOT NULL THEN '_'
ELSE ''
END + ISNULL('i' + REPLACE(included_columns, ', ',
'_i'), ''), '[', ''), ']',
'') AS Index_Name ,
'CREATE INDEX [' + REPLACE(REPLACE(ISNULL('x'
+ REPLACE(equality_columns,
', ', '_x'), '')
+ CASE WHEN equality_columns IS NOT NULL
AND inequality_columns IS NOT NULL
THEN '_'
ELSE ''
END + ISNULL('x'
+ REPLACE(inequality_columns,
', ', '_x'), '')
+ CASE WHEN included_columns IS NOT NULL
AND included_columns IS NOT NULL
THEN '_'
ELSE ''
END + ISNULL('i'
+ REPLACE(included_columns,
', ', '_i'), ''),
'[', ''), ']', '') + ']' + ' ON '
+ [statement] + ' (' + ISNULL(equality_columns, '')
+ CASE WHEN equality_columns IS NOT NULL
AND inequality_columns IS NOT NULL THEN ','
ELSE ''
END + ISNULL(inequality_columns, '') + ')' + ISNULL(' INCLUDE ('
+ included_columns
+ ')', '') AS CreateSql
FROM sys.dm_db_missing_index_groups AS G
INNER JOIN sys.dm_db_missing_index_group_stats AS GS ON G.index_group_handle = GS.group_handle
INNER JOIN sys.dm_db_missing_index_details AS D ON G.index_handle = D.index_handle
WHERE D.database_id = DB_ID()
ORDER BY PossibleImprovement DESC
) a
/*创建索引,执行脚本*/
WHILE @undo <> 0
BEGIN
SET @CreateSql = ( SELECT TOP 1
CreateSql
FROM dbo.DBA_MissingIndexCreateRecord
WHERE Status = 0
AND Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
);
EXEC sp_executesql @CreateSql;
UPDATE dbo.DBA_MissingIndexCreateRecord
SET Status = 1
WHERE Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
AND Status = 0
AND CreateSql = @CreateSql;
SET @undo = ( SELECT COUNT(1)
FROM dbo.DBA_MissingIndexCreateRecord
WHERE Status = 0
AND Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
)
END
END TRY
BEGIN CATCH
SET @Result = -2;
PRINT '执行失败的语句是:''' + @CreateSql + ''' 错误信息: ' + ERROR_MESSAGE();
END CATCH
SELECT @Result AS Result;
SET NOCOUNT OFF
END
View Code
2、索引过多,将所有的索引及使用情况记录到表,再人为分析判断,删除合并:
建表 DBA_IndexMergeRecord
IF OBJECT_ID('DBA_IndexMergeRecord') IS NOT NULL DROP TABLE DBA_IndexMergeRecord
CREATE TABLE [dbo].[DBA_IndexMergeRecord](
[DbName] [NVARCHAR](255) NULL,
[TableName] [NVARCHAR](255) NULL,
[IndexName] [NVARCHAR](255) NULL,
[IndexType] [NVARCHAR](60) NULL,
[IsUnique] INT NULL,
[Key_Cols] [NVARCHAR](MAX) NULL,
[Included_Cols] [NVARCHAR](MAX) NULL,
[StartDate] [DATETIME] NULL,
[IsDrop] INT DEFAULT 0 ,
[IsAuto] INT DEFAULT 0 ,
[DropDate] AS (CASE WHEN IsDrop = 1 THEN GETDATE() ELSE '1900-01-01 00:00:00.000' END ),
[DropSql] [NVARCHAR](MAX) NULL,
[USER_SEEKS] BIGINT NULL ,
[USER_SCANS] BIGINT NULL ,
[USER_LOOKUPS] BIGINT NULL ,
[USER_UPDATES] BIGINT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'数据库名' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'DbName'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'表名' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'TableName'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引名称' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'IndexName'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引类型' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'IndexType'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'是否唯一索引' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'IsUnique'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引列' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'Key_Cols'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引包含列' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'Included_Cols'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'是否已删除 0正常 1已整合删除 2其它途径删除' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'IsDrop'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'是否自动创建 0正常 1自动创建' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'IsAuto'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'删除日期' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'DropDate'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'删除脚本' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'DropSql'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引查找的次数' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'USER_SEEKS'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引扫描的次数' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'USER_SCANS'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'书签查找的次数' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'USER_LOOKUPS'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'增删改操作的次数' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'USER_UPDATES'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引整合记录表' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord'
GO
View Code
过程 DBAIndexMerge
ALTER PROCEDURE [dbo].[DBAIndexMerge] @Result INT = 0 OUTPUT ----返回结果
AS /*
创建者:TurboWay
申请人:TurboWay
所属模板:数据库维护
所属项目:数据库索引维护
过程说明:将所有新增的非聚集索引记录到 DBA_IndexMergeRecord
创建时间:2017-06-27
使用索引:
返回字段信息:
@Result 返回结果 0成功,-2失败
*/
BEGIN
SET NOCOUNT ON;
BEGIN TRY
CREATE TABLE #temp
(tbname NVARCHAR(255) NULL ,
idname NVARCHAR(255) NULL ,
USER_SEEKS BIGINT ,
USER_SCANS BIGINT,
USER_LOOKUPS BIGINT,
USER_UPDATES BIGINT
)
--索引使用情况
INSERT INTO #temp(tbname,idname,USER_SEEKS,USER_SCANS,USER_LOOKUPS,USER_UPDATES)
SELECT OBJECT_NAME(S.[OBJECT_ID]) AS [OBJECT NAME] ,
I.[NAME] AS [INDEX NAME] ,
USER_SEEKS ,
USER_SCANS ,
USER_LOOKUPS ,
USER_UPDATES
FROM SYS.DM_DB_INDEX_USAGE_STATS AS S
INNER JOIN SYS.INDEXES AS I ON I.[OBJECT_ID] = S.[OBJECT_ID]
AND I.INDEX_ID = S.INDEX_ID
WHERE S.[database_id] = DB_ID()
--更新索引的删除状态
UPDATE DBA_IndexMergeRecord SET IsDrop = 2 --其它途径删除
WHERE IsDrop = 0 AND NOT EXISTS( SELECT 1 FROM sys.indexes a WHERE a.name= IndexName AND a.object_id = OBJECT_ID(TableName))
--更新索引的重建状态
UPDATE DBA_IndexMergeRecord SET IsDrop = 0, IsRebuild = 1 --重建
WHERE IsDrop = 1 AND EXISTS( SELECT 1 FROM sys.indexes a WHERE a.name= IndexName AND a.object_id = OBJECT_ID(TableName))
--更新索引使用情况
UPDATE DBA_IndexMergeRecord SET USER_SEEKS = b.USER_SEEKS,
USER_SCANS = b.USER_SCANS,
USER_LOOKUPS = b.USER_LOOKUPS,
USER_UPDATES = b.USER_UPDATES
FROM DBA_IndexMergeRecord a, #temp b
WHERE a.TableName = b.tbname AND a.IndexName = b.idname
--记录新增索引
INSERT INTO DBA_IndexMergeRecord([DbName],[TableName] ,[IndexName] ,[IndexType] ,[IsUnique],[Key_Cols] ,[Included_Cols] ,[StartDate] ,[DropSql],
[USER_SEEKS] ,[USER_SCANS] ,[USER_LOOKUPS] ,[USER_UPDATES])
SELECT DB_NAME(),
o.name AS TableName,
i.name AS IndexName,
i.type_desc AS IndexType,
i.is_unique AS IsUnique,
SUBSTRING(ikey.cols, 3, LEN(ikey.cols)) AS Key_Cols ,
SUBSTRING(inc.cols, 3, LEN(inc.cols)) AS Included_Cols ,
STATS_DATE(o.object_id, i.index_id) AS StartDate,
'drop index ['+i.name+'] on ['+ o.name +']' +
' update DBA_IndexMergeRecord set IsDrop = 1 where TableName = '''+o.name+ ''' And IndexName = '''+ i.name +'''' AS DropSql,
USER_SEEKS ,
USER_SCANS ,
USER_LOOKUPS ,
USER_UPDATES
FROM sys.objects o
JOIN sys.indexes i ON i.object_id = o.object_id
CROSS APPLY ( SELECT ', ' + c.name + CASE ic.is_descending_key
WHEN 1 THEN ' DESC'
ELSE ''
END
FROM sys.index_columns ic
JOIN sys.columns c ON ic.object_id = c.object_id
AND ic.column_id = c.column_id
WHERE ic.object_id = i.object_id
AND ic.index_id = i.index_id
AND ic.is_included_column = 0
ORDER BY ic.key_ordinal
FOR
XML PATH('')
) AS ikey ( cols )
OUTER APPLY ( SELECT ', ' + c.name
FROM sys.index_columns ic
JOIN sys.columns c ON ic.object_id = c.object_id
AND ic.column_id = c.column_id
WHERE ic.object_id = i.object_id
AND ic.index_id = i.index_id
AND ic.is_included_column = 1
ORDER BY ic.index_column_id
FOR
XML PATH('')
) AS inc ( cols )
LEFT JOIN (SELECT *
FROM SYS.DM_DB_INDEX_USAGE_STATS
WHERE [database_id] = DB_ID()) t ON t.index_id = i.index_id AND t.object_id = i.object_id
WHERE o.type = 'u'
AND i.type IN (1,2)
AND NOT EXISTS(SELECT 1 FROM DBA_IndexMergeRecord rd WHERE o.name = rd.TableName AND i.name = rd.IndexName)
ORDER BY o.name ,
i.index_id
--更新索引来源
UPDATE DBA_IndexMergeRecord SET IsAuto = 1 --自动创建
FROM DBA_IndexMergeRecord a ,DBA_MissingIndexCreateRecord b
WHERE IsAuto = 0 AND a.TableName = b.TableName AND a.IndexName= b.IndexName
END TRY
BEGIN CATCH
SET @Result = -2;
END CATCH
SELECT @Result AS Result;
SET NOCOUNT OFF
END
GO
View Code
3、索引碎片,通过索引重组或重建,减少或消除索引,并记录日志:
建表 DBA_IndexDefragRecord
CREATE TABLE [dbo].[DBA_IndexDefragRecord](
[Datekey] [INT] NULL,
[DBName] [NVARCHAR](255) NULL,
[SchemaName] [NVARCHAR](255) NULL,
[TableName] [NVARCHAR](255) NULL,
[IndexName] [NVARCHAR](255) NULL,
[AvgFragmentB] [DECIMAL](5, 2) NULL,
[AvgFragmentE] [DECIMAL](5, 2) NULL,
[DefragSql] [NVARCHAR](MAX) NULL,
[Status] [INT] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[DBA_IndexDefragRecord] ADD DEFAULT ((0)) FOR [Status]
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'日期' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'Datekey'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'数据库名称' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'DBName'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'架构' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'SchemaName'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'表名' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'TableName'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引名称' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'IndexName'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'维护前的碎片程度' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'AvgFragmentB'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'维护后的碎片程度' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'AvgFragmentE'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'执行脚本' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'DefragSql'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'状态 0未执行 1已执行' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'Status'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引维护记录表' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord'
GO
View Code
过程 DBA_IndexDefrag
CREATE PROCEDURE [dbo].[DBAIndexDefrag] @Result INT = 0 OUTPUT ----返回结果
AS /*
创建者:TurboWay
申请人:TurboWay
所属模板:数据库维护
所属项目:数据库索引维护
过程说明:整理索引碎片并记录到 DBA_IndexDefragRecord
创建时间:2017-06-27
使用索引:
返回字段信息:
@Result 返回结果 0成功,-2失败
*/
BEGIN
SET NOCOUNT ON;
DECLARE @undo INT = 1;
DECLARE @DefragSql NVARCHAR(MAX);
IF OBJECT_ID('tempdb..#Frag') IS NOT NULL
DROP TABLE #Frag;
CREATE TABLE #Frag
(
Datekey INT ,
DBName NVARCHAR(255) ,
SchemaName NVARCHAR(255) ,
TableName NVARCHAR(255) ,
IndexName NVARCHAR(255) ,
AvgFragmentE DECIMAL(5, 2)
);
BEGIN TRY
/*记录索引碎片情况和维护脚本,索引碎片10-30,重组索引;索引碎片30以上,重建索引*/
IF NOT EXISTS ( SELECT 1
FROM DBA_IndexDefragRecord
WHERE Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112)) )
BEGIN
INSERT INTO DBA_IndexDefragRecord
( Datekey ,
DBName ,
SchemaName ,
TableName ,
IndexName ,
AvgFragmentB ,
DefragSql
)
SELECT CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112)) Datekey ,
DB_NAME() AS DBName ,
sysSc.name AS SchemaName ,
sysTa.name AS TableName ,
sysIn.name AS IndexName ,
func.avg_fragmentation_in_percent AvgFragmentB ,
CASE WHEN func.avg_fragmentation_in_percent BETWEEN 10.0
AND
30.0
THEN N'ALTER INDEX [' + sysIn.name
+ '] ON [' + DB_NAME() + '].['
+ sysSc.name + '].['
+ sysTa.name + '] REORGANIZE'
WHEN func.avg_fragmentation_in_percent > 30.0
THEN N'ALTER INDEX [' + sysIn.name
+ '] ON [' + DB_NAME() + '].['
+ sysSc.name + '].['
+ sysTa.name
+ '] REBUILD'
END AS DefraSql
FROM sys.dm_db_index_physical_stats(DB_ID(),
NULL, NULL, NULL,
NULL) AS func
INNER JOIN sys.indexes AS sysIn ON func.object_id = sysIn.object_id
AND func.index_id = sysIn.index_id
LEFT JOIN sys.all_objects AS sysTa ON func.object_id = sysTa.object_id
LEFT JOIN sys.schemas AS sysSc ON sysTa.schema_id = sysSc.schema_id
WHERE sysIn.index_id > 0
AND func.page_count > 8 --只维护数据页有8页以上的索引
AND func.avg_fragmentation_in_percent > 10 --只维护碎片达到10%以上的索引
AND sysTa.type IN ( 'u', 'v' ) --维护表索引、视图索引
ORDER BY func.avg_fragmentation_in_percent;
END;
/*整理索引碎片,执行脚本*/
WHILE @undo <> 0
BEGIN
SET @DefragSql = ( SELECT TOP 1
DefragSql
FROM dbo.DBA_IndexDefragRecord
WHERE Status = 0
AND Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
);
EXEC sp_executesql @DefragSql;
UPDATE dbo.DBA_IndexDefragRecord
SET Status = 1
WHERE Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
AND Status = 0
AND DefragSql = @DefragSql;
SET @undo = ( SELECT COUNT(1)
FROM dbo.DBA_IndexDefragRecord
WHERE Status = 0
AND Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
);
END;
/*更新维护后的索引碎片程度*/
INSERT INTO #Frag
( Datekey ,
DBName ,
SchemaName ,
TableName ,
IndexName ,
AvgFragmentE
)
SELECT CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112)) Datekey ,
DB_NAME() AS DBName ,
sysSc.name AS SchemaName ,
sysTa.name AS TableName ,
sysIn.name AS IndexName ,
func.avg_fragmentation_in_percent AvgFragmentE
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL,
NULL, NULL) AS func
INNER JOIN sys.indexes AS sysIn ON func.object_id = sysIn.object_id
AND func.index_id = sysIn.index_id
LEFT JOIN sys.all_objects AS sysTa ON func.object_id = sysTa.object_id
LEFT JOIN sys.schemas AS sysSc ON sysTa.schema_id = sysSc.schema_id
WHERE sysIn.index_id > 0
AND sysTa.type IN ( 'u', 'v' ); --维护表索引、视图索引
UPDATE dbo.DBA_IndexDefragRecord
SET AvgFragmentE = b.AvgFragmentE
FROM dbo.DBA_IndexDefragRecord a ,
#Frag b
WHERE a.Datekey = b.Datekey
AND a.DBName = b.DBName
AND a.SchemaName = b.SchemaName
AND a.TableName = b.TableName
AND a.IndexName = b.IndexName;
END TRY
BEGIN CATCH
SET @Result = -2;
PRINT '执行失败的语句是:''' + @DefragSql + ''' 错误信息: ' + ERROR_MESSAGE();
END CATCH;
SELECT @Result AS Result;
DROP TABLE #Frag;
SET NOCOUNT OFF;
END;
View Code
4、更新统计信息,并记录日志(这个日志并没什么用,但统计信息是有意义的,有利于优化器选择合理的索引、生成最优的执行计划)
建表 DBA_StatisticsUpdateRecord
CREATE TABLE [dbo].[DBA_StatisticsUpdateRecord](
[Datekey] [INT] NULL,
[DBName] [NVARCHAR](255) NULL,
[SchemaName] [NVARCHAR](255) NULL,
[TableName] [NVARCHAR](255) NULL,
[StatisticsName] [NVARCHAR](255) NULL,
[LastStatUpdateDate] [DATETIME] NULL,
[RowModCTR] [INT] NULL,
[TotalRowsInTable] [INT] NULL,
[UpdateSql] [NVARCHAR](MAX) NULL,
[Status] [INT] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[DBA_StatisticsUpdateRecord] ADD DEFAULT ((0)) FOR [Status]
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'日期' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'Datekey'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'数据库名称' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'DBName'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'架构' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'SchemaName'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'表名' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'TableName'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'统计信息名称' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'StatisticsName'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'最后更新的时间' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'LastStatUpdateDate'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'抽样行数' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'RowModCTR'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'实际行数' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'TotalRowsInTable'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'执行脚本' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'UpdateSql'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'状态 0未执行 1已执行' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'Status'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'统计信息更新记录表' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord'
GO
View Code
过程 DBAStatisticsUpdate
CREATE PROCEDURE [dbo].[DBAStatisticsUpdate] @Result INT = 0 OUTPUT ----返回结果
AS
/*
创建者:TurboWay
申请人:TurboWay
所属模板:数据库维护
所属项目:数据库统计信息维护
过程说明:更新过时的统计信息并记录到 DBA_StatisticsUpdateRecord
创建时间:2017-06-27
使用索引:
返回字段信息:
@Result 返回结果 0成功,-2失败
*/
BEGIN
SET NOCOUNT ON;
DECLARE @undo INT = 1;
DECLARE @UpdateSql NVARCHAR(MAX);
BEGIN TRY
/*记录过时的统计信息*/
INSERT INTO DBA_StatisticsUpdateRecord
( [Datekey] ,
[DBName] ,
[SchemaName] ,
[TableName] ,
[StatisticsName] ,
[LastStatUpdateDate] ,
[RowModCTR] ,
[TotalRowsInTable] ,
[UpdateSql]
)
SELECT DISTINCT
CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112)) Datekey ,
DB_NAME() DBName ,
SCHEMA_NAME(SO.schema_id) SchemaName ,
OBJECT_NAME(SI.object_id) AS TableName ,
SI.name AS StatisticsName ,
STATS_DATE(SI.object_id, SI.index_id) AS LastStatUpdateDate ,
SSI.rowmodctr AS RowModCTR ,
SP.rows AS TotalRowsInTable ,
'UPDATE STATISTICS [' + DB_NAME() + '].['
+ SCHEMA_NAME(SO.schema_id) + '].['
+ OBJECT_NAME(SI.object_id) + ']' + SPACE(2)
+'[' +SI.name+']' AS UpdateSql
FROM sys.indexes AS SI ( NOLOCK )
INNER JOIN sys.objects AS SO ( NOLOCK ) ON SI.object_id = SO.object_id
INNER JOIN sys.sysindexes SSI ( NOLOCK ) ON SI.object_id = SSI.id
AND SI.index_id = SSI.indid
INNER JOIN sys.partitions AS SP ON SI.object_id = SP.object_id
WHERE SSI.rowmodctr > 0
AND STATS_DATE(SI.object_id, SI.index_id) IS NOT NULL
AND SO.type = 'U'
ORDER BY RowModCTR DESC;
/*更新过时的统计信息,执行脚本*/
WHILE @undo <> 0
BEGIN
SET @UpdateSql = ( SELECT TOP 1
UpdateSql
FROM dbo.DBA_StatisticsUpdateRecord
WHERE Status = 0
AND Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
);
EXEC sp_executesql @UpdateSql;
UPDATE dbo.DBA_StatisticsUpdateRecord
SET Status = 1
WHERE Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
AND Status = 0
AND UpdateSql = @UpdateSql;
SET @undo = ( SELECT COUNT(1)
FROM dbo.DBA_StatisticsUpdateRecord
WHERE Status = 0
AND Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
);
END;
END TRY
BEGIN CATCH
SET @Result = -2;
PRINT '执行失败的语句是:' + @UpdateSql;
END CATCH;
SELECT @Result AS Result;
SET NOCOUNT OFF;
END;
View Code
五、简化作业调用
用一个过程,灵活调用以上的过程。
再用作业来调用这个过程DBAMaintenance,定期执行,达到自动维护的效果。
过程DBAMaintenance
ALTER PROCEDURE [dbo].[DBAMaintenance]
@Returnvalue INT = 0 OUTPUT ----返回结果
AS
/*
创建者:TurboWay
申请人:TurboWay
所属模板:数据库维护
所属项目:数据库维护
过程说明:执行数据库维护的过程,供作业直接调用
创建时间:2017-06-27
使用索引:
返回字段信息:
Returnvalue 返回结果 0 没有执行,成功
大于0 表示执行次数
-2 执行失败,超出执行次数
*/
BEGIN
/*整理索引碎片*/
DECLARE @IsSucess INT =0
DECLARE @Count INT =0 --执行计数
EXEC DBAIndexDefrag @IsSucess OUTPUT
IF @IsSucess = -2 --由于重建索引可能被高并发的查询,阻塞牺牲,导致失败,所以需要等待重复执行
BEGIN
WHILE EXISTS(SELECT 1 FROM dbo.DBA_IndexDefragRecord WHERE Status = 0 AND Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))) --存在未执行的脚本
AND @Count < 30 --执行过程的次数不得高于30,防止出现死循环
BEGIN
WAITFOR DELAY '00:00:10' --延迟10秒执行,等待引起阻塞的查询完成
BEGIN
EXEC DBAIndexDefrag --再次整理碎片
SET @Count = @Count + 1
END
END
END
/*更新统计信息*/
EXEC DBAStatisticsUpdate
/*创建缺失索引top10*/
EXEC DBAMissingIndexCreate
/*记录索引整体情况*/
EXEC DBAIndexMerge
/*返回执行情况*/
SELECT @Returnvalue = CASE WHEN @Count = 0 THEN 0
WHEN @Count > 0 AND @Count < 30 THEN @Count
ELSE -2
END
SELECT @Returnvalue AS Returnvalue
END
GO
View Code