인덱스 통계와 관련된 성능 관련 문제가있는 SQL Server 2017 (CU9) 데이터베이스가 있습니다. 문제 해결 중에 통계가 업데이트되지 않은 것을 발견했습니다 (DBCC SHOW_STATISTICS가 모든 NULL 값을 반환 함을 의미 함).
영향을받는 테이블에서 UPDATE STATISTICS를 실행하고 어제 오후 4시에 SHOW_STATISTICS가 실제 값을 반환하는지 확인했습니다. 오늘 아침 8:00 AM에 통계가 다시 비워졌습니다 (Null 값 반환).
클라이언트에는 매일 오전 4시에 실행되도록 예약 된 유지 관리 작업이 있으며,이 작업은 데이터베이스에 대해 인덱스를 생성 한 다음 전체 데이터베이스에 대해 sp_updatestats를 실행합니다. 통계가 오전 4시에 프로파일 러 추적으로 업데이트되는지 확인했습니다.
통계가 비어있는 이유에 대해 유실되었습니다. 오전 4시에 유지 보수 작업을 실행합니까? 이 버전의 SQL Server에서 알지 못하는 버그가 있습니까?
도움을 주셔서 감사합니다.
더 많은 정보:
- 자동 업데이트 통계가 활성화되었습니다.
- 자동 업데이트 통계는 비동기 적으로 비활성화됩니다.
- 증분 통계 자동 작성이 비활성화됩니다.
색인 생성 스크립트 (폐기 됨) :
USE DBNAME;
DECLARE @CERTENG_Lock INT
DECLARE @WebSite_Control_ProcessRunning_Lock INT
DECLARE @WebSite_Control_Disabled_Lock INT
DECLARE @LogMessage VARCHAR(1024)
SELECT @CERTENG_Lock = Lock FROM application.CERTENG_Lock
SELECT @WebSite_Control_Disabled_Lock = MAX(CAST(Disabled AS INT)),
@WebSite_Control_ProcessRunning_Lock = MAX(CAST(ProcessRunning AS INT))
FROM application.WebSite_Control
WHERE Webname = 'Reports'
IF(@CERTENG_Lock = 0 AND @WebSite_Control_Disabled_Lock = 0 AND
@WebSite_Control_ProcessRunning_Lock = 0)
BEGIN
EXECUTE Dba.ReIndex
END
ELSE
BEGIN
SET @LogMessage = 'The reindex job did not run because the following locks were set: '
IF(@CERTENG_Lock = 1)
BEGIN
SET @LogMessage = @LogMessage + 'The CERTENG_Lock was set to 1;'
END
IF(@WebSite_Control_Disabled_Lock = 1)
BEGIN
SET @LogMessage = @LogMessage + 'The WebSite_Control_Disabled_Lock was set to 1;'
END
IF(@WebSite_Control_ProcessRunning_Lock = 1)
BEGIN
SET @LogMessage = @LogMessage + 'The WebSite_Control_ProcessRunning_Lock was set to 1;'
END
INSERT INTO [Dba].[ReindexLog] ([LogMessage]) VALUES (@LogMessage)
END
DBA. 재색 인
USE [Database]
GO
/****** Object: StoredProcedure [Dba].[ReIndex] Script Date: 12/20/2018 11:15:33 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--Create procedure to perform reindexing
ALTER PROCEDURE [Dba].[ReIndex] (
-- Only rebuild if fragmentation is above ___
@REBUILD_FRAGMENTATION_THRESHOLD FLOAT = 30,
-- Or only reorganize if fragmentation is above ___
@REORG_FRAGMENTATION_THRESHOLD FLOAT = 10
)
AS
SET NOCOUNT ON;
DECLARE @WorkingId BIGINT, @ReindexId BIGINT, @Sql VARCHAR(2000);
DECLARE @TableId INT, @IndexId INT;
DECLARE @ExecutionTime DATETIME
SET @ExecutionTime = GETDATE()
-------------Identify tables------------------------------------------------------------
TRUNCATE TABLE Dba.ReindexList;
-- List all the tables and their indexes in the database by the number of rows
-- in order to do the largest tables first.
INSERT INTO Dba.ReindexList (SchemaName, TableName, IndexName, TableId, IndexId, IndexType, NumberOfRows)
SELECT s.NAME AS [SchemaName], t.NAME AS [TableName], i.NAME AS [IndexName], i.object_id, i.index_id, i.type_desc, p.row_count
FROM sys.schemas AS s
INNER JOIN sys.tables AS t
ON t.schema_id = s.schema_id
INNER JOIN sys.indexes AS i
ON i.object_id = t.object_id
INNER JOIN sys.dm_db_partition_stats AS p
ON p.object_id = i.object_id
AND p.index_id = i.index_id
-- Ignore heaps because they can't be rebuilt or reorganized
WHERE i.type_desc != 'HEAP'
-- Skip individual schemas owned by domain accounts
AND charindex('\', s.NAME) = 0
-- Skip DBA schema
AND s.NAME != 'Dba'
ORDER BY p.row_count DESC, s.NAME, t.NAME, i.index_id;
----------------Check fragmentation---------------------------------------------------
DECLARE
-- Separate table to keep track of only the indexes that need to be now
@FragmentationWorkingList TABLE (ReindexId BIGINT NOT NULL PRIMARY KEY CLUSTERED);
INSERT INTO @FragmentationWorkingList (ReindexId)
SELECT r.ReindexId
FROM Dba.ReindexList AS r
-- Skip fragmentation check for this specific index or table?
LEFT JOIN Dba.ReindexSetting AS st
ON st.DatabaseName = db_name()
AND st.SchemaName = r.SchemaName
AND st.TableName = r.TableName
AND st.IndexName IS NULL
LEFT JOIN Dba.ReindexSetting AS si
ON si.DatabaseName = db_name()
AND si.SchemaName = r.SchemaName
AND si.TableName = r.TableName
AND si.IndexName = r.IndexName
WHERE r.IsFragmentationChecked = 'N'
AND r.IsReindexed = 'N'
-- Index setting overrides table setting if both are specified
AND coalesce(si.SkipFragmentationCheck, st.SkipFragmentationCheck, 'N') = 'N'
ORDER BY r.ReindexId;
SELECT @ReindexId = min(w.ReindexId)
FROM @FragmentationWorkingList AS w;
WHILE @ReindexId IS NOT NULL
BEGIN
-- Pull IDs into variables because the physical stats DM function can't
-- cross-apply values from a JOIN.
SELECT @TableId = r.TableId, @IndexId = r.IndexId
FROM Dba.ReindexList AS r
WHERE r.ReindexId = @ReindexId;
-- Load the fragmentation for each index individually
-- with duration-tracking so we can figure out whether or not
-- this is really worthwhile.
UPDATE Dba.ReindexList
SET FragmentationCheckStartTime = getdate()
WHERE ReindexId = @ReindexId;
UPDATE r
SET Fragmentation = p.avg_fragmentation_in_percent
FROM Dba.ReindexList AS r
-- Use LIMITED for fastest scan
INNER JOIN sys.dm_db_index_physical_stats(db_id(), @TableId, @IndexId, NULL, 'LIMITED') AS p
-- Should only return one row for this index
ON 1 = 1
WHERE r.ReindexId = @ReindexId;
UPDATE Dba.ReindexList
SET IsFragmentationChecked = 'Y', FragmentationCheckEndTime = getdate()
WHERE ReindexId = @ReindexId;
SELECT @ReindexId = min(w.ReindexId)
FROM @FragmentationWorkingList AS w
WHERE w.ReindexId > @ReindexId;
END
------------------------------Reindex------------------------------------
DECLARE
-- Separate table to keep track of only the indexes that need to be now
@ReindexWorkingList TABLE (
-- Order differently based on row count and fragmentation
WorkingId BIGINT NOT NULL identity(1, 1) PRIMARY KEY CLUSTERED, ReindexId BIGINT NOT NULL
);
INSERT INTO @ReindexWorkingList (ReindexId)
SELECT r.ReindexId
FROM Dba.ReindexList AS r
-- Skip fragmentation check for this specific index or table?
LEFT JOIN Dba.ReindexSetting AS st
ON st.DatabaseName = db_name()
AND st.SchemaName = r.SchemaName
AND st.TableName = r.TableName
AND st.IndexName IS NULL
LEFT JOIN Dba.ReindexSetting AS si
ON si.DatabaseName = db_name()
AND si.SchemaName = r.SchemaName
AND si.TableName = r.TableName
AND si.IndexName = r.IndexName
WHERE r.IsReindexed = 'N'
-- Index setting overrides table setting if both are specified
AND coalesce(si.SkipReindex, st.SkipReindex, 'N') = 'N'
-- Process tables in order of the most fragmented, largest
AND r.Fragmentation >= @REORG_FRAGMENTATION_THRESHOLD
ORDER BY r.Fragmentation DESC, r.NumberOfRows DESC, r.ReindexId;
SELECT @WorkingId = min(w.WorkingId)
FROM @ReindexWorkingList AS w;
WHILE @WorkingId IS NOT NULL
BEGIN
SELECT @ReindexId = w.ReindexId
FROM @ReindexWorkingList AS w
WHERE w.WorkingId = @WorkingId;
-- Skip index because of low fragmentation?
IF @REORG_FRAGMENTATION_THRESHOLD > (
-- Assume that an index is highly fragmented if the exact %
-- wasn't calculated to save time
SELECT isnull(r.Fragmentation, 100)
FROM Dba.ReindexList AS r
WHERE r.ReindexId = @ReindexId
)
BEGIN
UPDATE Dba.ReindexList
SET IsReindexed = 'Y', IsSkipped = 'Y', ReindexStartTime = getdate(), ReindexEndTime = getdate()
WHERE ReindexId = @ReindexId;
END
-- Rebuild or reorganize...
ELSE
BEGIN
-- Try/catch inside a loop causes slower performance, but reindexing
-- should continue on the next index if an error occurs.
BEGIN TRY
-- Rebuild or reorganize?
-- 1) Ignore heaps
-- 2) Always rebuild a clustered index
-- 3) Rebuild nonclustered if > __, otherwise reorganize it
-- According to Kalen Delaney (http://social.msdn.microsoft.com/Forums/en/sqldatabaseengine/thread/dd612296-5b3a-40f1-829f-c654b835efed),
-- rebuild always updates statistics with FULLSCAN while reorgnize does not.
SELECT @Sql = 'alter index [' + r.IndexName + '] on [' + r.SchemaName + '].[' + r.TableName + '] ' +
CASE WHEN IndexType = 'HEAP' THEN 'rebuild'
WHEN IndexType = 'CLUSTERED' THEN 'rebuild'
WHEN Fragmentation > @REBUILD_FRAGMENTATION_THRESHOLD THEN 'rebuild'
ELSE 'reorganize; update statistics [' + r.SchemaName + '].[' + r.TableName + '] [' + r.IndexName + ']'
END +
-- TODO: Handle partitions properly
';'
FROM Dba.ReindexList AS r
WHERE r.ReindexId = @ReindexId;
UPDATE Dba.ReindexList
SET ReindexStartTime = getdate(), Sql = @Sql
WHERE ReindexId = @ReindexId;
EXECUTE (@sql);
UPDATE Dba.ReindexList
SET ReindexEndTime = getdate(), IsReindexed = 'Y'
WHERE ReindexId = @ReindexId;
END TRY
BEGIN CATCH
UPDATE Dba.ReindexList
SET ReindexEndTime = getdate(),
-- Mark as reindexed to show that an attempt was made...
IsReindexed = 'Y', ErrorNumber = error_number(), ErrorMessage = error_message()
WHERE ReindexId = @ReindexId;
END CATCH
END
SELECT @WorkingId = min(w.WorkingId)
FROM @ReindexWorkingList AS w
WHERE w.WorkingId > @WorkingId;
END
INSERT INTO Dba.ReindexHistory (HistoryTime, TableId, IndexId, SchemaName, TableName, IndexName, IsClustered, IsReindexed, NumberOfRows, Fragmentation)
SELECT isnull(@ExecutionTime, getdate()), l.TableId, l.IndexId, l.SchemaName, l.TableName, l.IndexName,
CASE l.IndexType WHEN 'CLUSTERED' THEN 'Y'
ELSE 'N'
END AS IsClustered,
l.IsReindexed, l.NumberOfRows, l.Fragmentation
FROM Dba.ReindexList AS l
LEFT JOIN Dba.ReindexHistory AS h
ON h.HistoryTime = l.FragmentationCheckStartTime
AND h.TableId = l.TableId
AND h.IndexId = l.IndexId
WHERE h.HistoryTime IS NULL
ORDER BY l.FragmentationCheckStartTime, l.TableId, l.IndexId;
업데이트 : 데이터베이스에 대한 자동 업데이트 통계를 비활성화하고 어제 통계를 수동으로 업데이트했습니다. 오늘 아침에 그들은 여전히 채워져 있습니다. 자동 업데이트에 문제가 있음을 의미한다고 가정합니다.
1
도움이 될 것입니다 -brentozar.com/archive/2018/09/…
—
Kin Shah