増分更新後に統計が消える


21

増分統計を利用する大規模なパーティションSQL Serverデータベースがあります。すべてのインデックスはパーティション分割されています。パーティションごとにオンラインでパーティションを再構築しようとすると、インデックスが再構築された後にすべての統計が消えます。

以下は、AdventureWorks2014データベースを使用してSQL Server 2014の問題を再現するスクリプトです。

--Example against AdventureWorks2014 Database

CREATE PARTITION FUNCTION TransactionRangePF1 (DATETIME)
AS RANGE RIGHT FOR VALUES 
(
   '20130501', '20130601', '20130701', '20130801', 
   '20130901', '20131001', '20131101', '20131201', 
   '20140101', '20140201', '20140301'
);
GO

CREATE PARTITION SCHEME TransactionsPS1 AS PARTITION TransactionRangePF1 TO 
(
  [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], 
  [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], 
  [PRIMARY], [PRIMARY], [PRIMARY]
);
GO

CREATE TABLE dbo.TransactionHistory 
(
  TransactionID        INT      NOT NULL, -- not bothering with IDENTITY here
  ProductID            INT      NOT NULL,
  ReferenceOrderID     INT      NOT NULL,
  ReferenceOrderLineID INT      NOT NULL DEFAULT (0),
  TransactionDate      DATETIME NOT NULL DEFAULT (GETDATE()),
  TransactionType      NCHAR(1) NOT NULL,
  Quantity             INT      NOT NULL,
  ActualCost           MONEY    NOT NULL,
  ModifiedDate         DATETIME NOT NULL DEFAULT (GETDATE()),
  CONSTRAINT CK_TransactionType 
    CHECK (UPPER(TransactionType) IN (N'W', N'S', N'P'))
) 
ON TransactionsPS1 (TransactionDate);


INSERT INTO dbo.TransactionHistory
SELECT * FROM Production.TransactionHistory
--  SELECT * FROM sys.partitions
--  WHERE object_id = OBJECT_ID('dbo.TransactionHistory');

CREATE NONCLUSTERED INDEX IDX_ProductId ON dbo.TransactionHistory (ProductId) 
  WITH (DATA_COMPRESSION = ROW, STATISTICS_INCREMENTAL=ON)  
  ON TransactionsPS1 (TransactionDate)

DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);
PRINT 'Stats are avialable'  

ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = 9 WITH (ONLINE = ON , DATA_COMPRESSION = ROW)

PRINT 'After online index rebuild by partition stats are now gone'
DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);

PRINT 'Rebuild the stats with a rebuild for all paritions (this works)' 
ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = ALL WITH (ONLINE = ON , DATA_COMPRESSION = ROW, 
  STATISTICS_INCREMENTAL = ON)

PRINT 'Stats are back'
DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);

PRINT 'Works correctly for an offline rebuild by partition'
ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = 9 WITH (ONLINE = OFF , DATA_COMPRESSION = ROW)

    --stats still there  
DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);

ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = 9 WITH (ONLINE = ON , DATA_COMPRESSION = ROW)

DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);
PRINT' stats are gone!!!!!!'

示されているように、インデックスのすべての統計を失うことなく、パーティションごとにインデックスを再構築することはできません。これは私たちにとって大きなメンテナンス問題です。統計インクリメンタルオプションは、単一インデックス再構築構文の一部である必要があるか、オンラインオプションがオフラインオプションと同様に適切に処理する必要があるようです。

何か足りない場合は教えてください。

更新:

増分統計の必要性に関しては、日付ではなく内部顧客IDでパーティション分割しています。そのため、新しいクライアントが取り込まれた場合(大量のデータのバックロード)、パーティションの統計情報を更新するだけで、この新しい顧客のために作成されるい計画をすばやく回避できます。マイクロソフトにバグとして提出し、彼らが言わなければならないことを確認し、そのパーティションの統計を再サンプリングするだけの解決策を採用すると思います。

接続バグレポート:

増分統計を使用したオンラインインデックスの再構築後に統計が消える

更新:マイクロソフトは、それがバグであることを確認しました。


1
更新:Microsoftは今朝、このバグをSQL 2014の次のCU更新で修正するというメールを私に送信しました
。– JasonR

どのCUがそれを修正したか、またはそのメールで報告されたKBを知っていますか?いつ修正されたかを確認しようとしています。
mbourgon

1
VSTSのバグ番号8046729 KBの記事番号3194959であり、SQL Server 2014 SP1のCU 9の一部であったことは間違いありません。KBへのリンクはこちらです。
JasonR

ええ、それはそのように見えます-そして、先月2016SP1で修正されました。とても感謝しています!
mbourgon

修正:2016 SP1 CU2で修正されました。2016 SP1 CU1で発生します。
mbourgon

回答:


17

それがバグかどうかは確かではありませんが、間違いなく興味深い出来事です。オンラインパーティションの再構築はSQL Server 2014で新しく追加されたものであるため、これに対処するための内部構造が存在する場合があります。

これが私の最高の説明です。増分統計では、エンジンが統計ページをマージするときに、サンプリングされた分布が同等であると確信できるように、すべてのパーティションが同じレートでサンプリングされることが絶対に必要です。REBUILD必ず100%のサンプルレートでデータをサンプリングします。パーティション9の100%のサンプルレートが常に残りのパーティションの正確なサンプルレートになるという保証はありません。このため、エンジンがサンプルをマージできず、統計blobが空になるように見えます。ただし、統計オブジェクトはまだあります。

select 
    check_time = sysdatetime(),                         
    schema_name = sh.name,
    table_name = t.name,
    stat_name = s.name,
    index_name = i.name,
    stats_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1),
    s.stats_id,
    s.has_filter,                       
    s.is_incremental,
    s.auto_created,
    sp.last_updated,    
    sp.rows,
    sp.rows_sampled,                        
    sp.unfiltered_rows,
    modification_counter 
from sys.stats s 
join sys.tables t 
    on s.object_id = t.object_id
join sys.schemas sh
    on t.schema_id = sh.schema_id
left join sys.indexes i 
    on s.object_id = i.object_id
    and s.name = i.name
outer apply sys.dm_db_stats_properties(s.object_id, s.stats_id) sp
where t.name = 'TransactionHistory' and sh.name = 'dbo'

任意の数の方法でブロブを埋めることができます。

UPDATE STATISTICS dbo.TransactionHistory (IDX_ProductId) WITH RESAMPLE;

または

UPDATE STATISTICS dbo.TransactionHistory (IDX_ProductId) WITH RESAMPLE ON PARTITIONS (9);

または、そのオブジェクトを使用したクエリプランの最初のコンパイル時にAutoStatsが更新されるのを待つことができます。

-- look at my creative query
select * 
from dbo.TransactionHistory
where TransactionDate = '20140101';

そのすべてを述べたが、Erin Stellatoによるこの啓発的な投稿は、増分統計の大きな欠陥として認識されるようになったものを強調しています。パーティションレベルのデータは、クエリプランの生成でオプティマイザーによって使用されないため、増分統計の推定される利点が減少します。では、増分統計の現在の利点は何ですか?私は、彼らの主なユーティリティは、大きなテーブルを従来の統計よりも高いレートでより一貫してサンプリングする能力にあると提出します。

あなたの例を使用して、次のようになります。

set statistics time on;

update statistics dbo.TransactionHistory(IDX_ProductId)
with fullscan;

--SQL Server Execution Times:
--  CPU time = 94 ms,  elapsed time = 131 ms.


update statistics dbo.TransactionHistory(IDX_ProductId)
with resample on partitions(2);

 --SQL Server Execution Times:
 --  CPU time = 0 ms,  elapsed time = 5 ms.

drop index IDX_ProductId On dbo.TransactionHistory;

CREATE NONCLUSTERED INDEX IDX_ProductId ON dbo.TransactionHistory (ProductId) 
  WITH (DATA_COMPRESSION = ROW)  
  ON [PRIMARY]

update statistics dbo.TransactionHistory(IDX_ProductId)
with fullscan;

 --SQL Server Execution Times:
 --  CPU time = 76 ms,  elapsed time = 66 ms.

増分統計のフルスキャン統計更新は131ミリ秒です。非パーティション調整統計のフルスキャン統計更新は66ミリ秒かかります。個々の統計ページをメインヒストグラムにマージする際に発生するオーバーヘッドが原因で、非整列統計が遅くなる可能性が最も高い。ただし、パーティションにアライメントされた統計オブジェクトを使用すると、1つのパーティションを更新し、5ミリ秒でメインヒストグラムBLOBにマージできます。したがって、この時点で、増分統計を持つ管理者は決定に直面します。パーティションを更新するだけで従来は更新する必要があるだけで、全体的な統計のメンテナンス時間を短縮できます。または、以前のメンテナンス時間枠と同じ期間にサンプリングされる行を増やす可能性があるように、より高いサンプルレートで実験することもできます。前者はメンテナンスウィンドウに余裕を持たせ、後者非常に大きなテーブルの統計を、クエリがより正確な統計に基づいてより良い計画を取得する場所にプッシュする可能性があります。これは保証ではなく、走行距離は異なる場合があります。

読者は、この表で66 msが統計更新時間の苦痛ではないことがわかるので、stackexchangeデータセットでテストを設定してみました。ダウンロードした最近のダンプには、6,418,608件の投稿(StackOverflow投稿と2012年のすべての投稿を除く-私の側のデータエラー)があります。

[CreationDate]デモによってデータを分割しました。

以下は、かなり標準的なシナリオのタイミングです(100%-インデックスの再構築、デフォルト-統計の自動更新、またはUPDATE STATISTICS指定されたサンプルレートなし:

  • フルスキャンで非増分統計を作成します:CPU時間= 23500ミリ秒、経過時間= 22521ミリ秒。
  • フルスキャンによる増分統計の作成:CPU時間= 20406ミリ秒、経過時間= 15413ミリ秒。
  • デフォルトのサンプルレートで非増分統計を更新:CPU時間= 406ミリ秒、経過時間= 408ミリ秒。
  • デフォルトのサンプルレートで増分統計を更新:CPU時間= 453ミリ秒、経過時間= 507ミリ秒。

これらのデフォルトのシナリオよりも高度であり、メンテナンス時間を妥当な時間枠に保ちながら、10%のサンプルレートが必要な計画を得るための最小レートであると判断したとします。

  • サンプル10%で非増分統計を更新します:CPU時間= 2344ミリ秒、経過時間= 2441ミリ秒。
  • サンプル10%で増分統計を更新します。CPU時間= 2344ミリ秒、経過時間= 2388ミリ秒。

これまでのところ、増分統計を持つことには明確な利点はありません。ただし、文書化されていない sys.dm_db_stats_properties_internal() DMV(下記)を活用すると、どのパーティションを更新する必要があるかについての洞察を得ることができます。パーティション3のデータに変更を加え、着信クエリの統計が最新であることを確認するとします。オプションは次のとおりです。

  • デフォルトで非増分更新(自動統計更新のデフォルトの動作でもあります):408ミリ秒。
  • 10%で非増分を更新:2441ミリ秒。
  • 増分統計の更新、パーティション3(リサンプルあり)(10%-定義されたサンプルレート):CPU時間= 63ミリ秒、経過時間= 63ミリ秒。

ここで、決定を下す必要があります。63ミリ秒の勝利を取りますか。パーティションベースの統計の更新、またはサンプルレートをさらに高くしますか?増分統計で50%のサンプリングの最初のヒットを受け入れたいとします。

  • 増分統計を50%で更新:経過時間= 16840ミリ秒。
  • 増分統計の更新、パーティション3のリサンプル(50%-新しい更新時間):経過時間= 295ミリ秒。

パーティションレベルの統計情報をまだ使用していない場合でも、オプティマイザーをセットアップしてデータをより正確に推測できるようにすることで、より多くのデータをサンプリングでき、より迅速にこれを実行できるようになりました。増分統計。

ただし、最後に理解しておかなければならないことがあります。同期統計の更新はどうですか?autostatsが起動しても、50%のサンプルレートは保持されますか?

パーティション3からデータを削除し、CreationDateでクエリを実行し、以下の同じクエリでレートをチェックしてからチェックしました。50%のサンプルレートが維持されました。

要するに、インクリメンタル統計は、適切な思考と初期セットアップ作業を備えた有用なツールになります。ただし、解決しようとしている問題を把握してから、適切に解決する必要があります。誤ったカーディナリティの推定値を取得している場合、戦略的なサンプルレートといくつかの投資介入により、より良い計画を取得できる可能性あります。ただし、使用されているヒストグラムはパーティションレベルの情報ではなく、単一のマージされた統計ページであるため、メリットの一部しか得られません。メンテナンス期間に苦痛を感じている場合は、増分統計情報が役立つ場合がありますが、おそらく、高度なメンテナンス介入プロセスを設定する必要があります。とにかく、

  • ベーステーブルとパーティションアラインされていないインデックスで作成された統計。
  • AlwaysOnで読み取り可能なセカンダリデータベースで作成された統計。
  • 読み取り専用データベースで作成された統計。
  • フィルター選択されたインデックスで作成された統計。
  • ビューで作成された統計。
  • 内部テーブルで作成された統計。
  • 空間インデックスまたはXMLインデックスで作成された統計。

お役に立てれば

select 
    sysdatetime(),                          
    schema_name = sh.name,
    table_name = t.name,
    stat_name = s.name,
    index_name = i.name,
    leading_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1),
    s.stats_id,
    parition_number = isnull(sp.partition_number,1),
    s.has_filter,                       
    s.is_incremental,
    s.auto_created,
    sp.last_updated,    
    sp.rows,
    sp.rows_sampled,                        
    sp.unfiltered_rows,
    modification_counter = coalesce(sp.modification_counter, n1.modification_counter) 
from sys.stats s 
join sys.tables t 
    on s.object_id = t.object_id
join sys.schemas sh
    on t.schema_id = sh.schema_id
left join sys.indexes i 
    on s.object_id = i.object_id
        and s.name = i.name
cross apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) sp
outer apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) n1
where n1.node_id = 1
    and (
            (is_incremental = 0)
               or
            (is_incremental = 1 and sp.partition_number is not null)
         )
    and t.name = 'Posts'
    and s.name like 'st_posts%'
order by s.stats_id,isnull(sp.partition_number,1)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.