データウェアハウジングシナリオで「統計の自動更新」を無効にする必要がありますか?


12

SQL Serverに200 GBのデータウェアハウスがあります。

一部のクエリの実行時間が非常に遅くなっています。たとえば、をdelete使用した単純なクエリの場合は12時間ですinner join

実行計画でいくつかの調査を行った後、WITH FULLSCANオプションを使用して、クエリに関連する2つのテーブルの統計を更新しました。

クエリは1秒未満で実行されるようになったため、統計は最新ではなかったようです。

auto update statisticsデータベースを無効にしてUPDATE STATISTICS、データウェアハウスの読み込み後に手動で実行することを検討しています。データウェアハウスは、ソースERPシステムから毎日、夜間に段階的に読み込まれます。

auto update statisticsデータウェアハウジングシナリオでは本当に役に立たないと想定しても正しいですか?代わりに、データのロード後に統計を手動で更新する方が理にかなっていますか?


これは統計についての非常に優れた読み物です。simple-talk.com / sql / performance / また、Olaのソリューションola.hallengren.com/… を使用して毎日のジョブを実行し、1TB dbの統計を更新します。自動統計更新オプションを無効にしません。
Joy Walker、

1
テーブルにあるレコードの数と、バッチに追加するレコードの量に大きく依存します。1晩に2,000万行を追加する1b行テーブルでは、統計は約10日ごとに更新されますが、これはすばらしいことではありません。
JNK、

2
参考までに-統計更新のしきい値を固定20%から動的パーセンテージレートに変更するトレースフラグ(2371)があります。もっとここを参照してください:blogs.msdn.com/b/saponsqlserver/archive/2011/09/07/...
DaniSQL

非常に有益なリンク、ありがとう!@JNK:はい、それは大きなDBです。ステージングテーブルには300m以上の行があり、日によっては、毎日1mから10mの間の何かを挿入します。以下の回答で提案されたように、統計をさらに詳しく見ていきます。
saso

回答:


11

これは、統計のauto_updateが発生するときのホワイトペーパーです。統計の自動更新と比較した場合の重要なポイントは次のとおりです。

  • テーブルのサイズが0から> 0行になりました(テスト1)。
  • 統計が収集されたときのテーブルの行数は500以下であり、統計オブジェクトの先頭列のcolmodctrはそれ以降500以上変更されています(テスト2)。
  • 統計の収集時にテーブルに500行を超える行があり、統計オブジェクトの先頭列のcolmodctrが、統計の収集時にテーブルの行数の500 + 20%を超えて変更された(テスト3) 。

したがって、@ JNKはコメントの中で、テーブルに10億行ある場合、更新をトリガーするには統計の最初の列に20,000,5000の書き込みが必要になると指摘しました。

次の構造を取ってみましょう:

CREATE TABLE dbo.test_table (
    test_table_id INTEGER IDENTITY(1,1) NOT NULL, 
    test_table_value VARCHAR(50), 
    test_table_value2 BIGINT, 
    test_table_value3 NUMERIC(10,2)
);

CREATE CLUSTERED INDEX cix_test_table ON dbo.test_table (test_table_id, test_table_value);

これで、統計ランドで何が起こったかを確認できます。

select * 
    from sys.stats
        where object_id = OBJECT_ID('dbo.test_table')

stat_container

ただし、これが意味のある統計オブジェクトかどうかを確認するには、次のことを行う必要があります。

dbcc show_statistics('dbo.test_table',cix_test_table)

ヒストグラム

したがって、この統計は更新されていません。これは、統計がa SELECTが発生するまで更新されないように見え、それでもSELECTSQL Serverがヒストグラム内にあるものの外にある必要があるためです。これをテストするために実行したテストスクリプトは次のとおりです。

    CREATE TABLE test_table (
        test_table_id INTEGER IDENTITY(1,1) NOT NULL, 
        test_table_value VARCHAR(50), 
        test_table_value2 BIGINT, 
        test_table_value3 NUMERIC(10,2)
    );

    CREATE CLUSTERED INDEX cix_test_table ON test_table (test_table_id, test_table_value);

    ALTER TABLE test_table ADD CONSTRAINT pk_test_table PRIMARY KEY  (test_table_id)

    SELECT * 
        FROM sys.stats
            WHERE object_id = OBJECT_ID('dbo.test_table')

    --DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table)
    DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

declare @test int = 0

WHILE @test < 1
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT 'one row|select < 1', * FROM test_table WHERE test_table_id < 1;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

SET @test = 1

WHILE @test < 500
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '100 rows(add 99)|select < 100',* FROM test_table WHERE test_table_id < 100;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--get the table up to 500 rows/changes
WHILE @test < 500
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END
SELECT '500 rows(add 400)|select < 100',* FROM test_table WHERE test_table_id < 100;
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
SELECT '500 rows(add 400)|select < 500',* FROM test_table WHERE test_table_id < 500;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--bump it to 501
SET @test = 500;
WHILE @test < 501
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END


SELECT '501 rows(add 1)|select < 501',* FROM test_table WHERE test_table_id < 501;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

--bump it to 600
SET @test = 501;
WHILE @test < 600
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '600 rows (add 100)|select < 600',* FROM test_table WHERE test_table_id < 600;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

--bump it to 700
SET @test = 600;
WHILE @test < 700
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '700 rows (add 100)|select < 700', * FROM test_table WHERE test_table_id < 700;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

--bump it to 1200
SET @test = 700;
WHILE @test < 1200
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '1200 rows (add 500)|select < 1200',* FROM test_table WHERE test_table_id < 1200;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--DROP TABLE test_table

auto_update統計を盲目的に無効にする代わりに、データセットのスキューを調べます。あなたのデータは重要なスキューを呈する場合は、フィルタリング統計の作成を検討し、必要があり、その後の統計情報の更新を管理することは、手動アクションの正しいコースがあるかどうかを判断します。

スキューを分析するにDBCC SHOW_STATISTICS(<stat_object>, <index_name>);は、WITH STAT_HEADER調べたい特定のstat / indexの組み合わせで(上記のスクリプトではを使用せずに)実行する必要があります。スキューを目撃する簡単な方法は、ヒストグラム(3番目の結果セット)を見て、の分散を確認することEQ_ROWSです。それがかなり一貫しているなら、あなたのスキューは最小限です。これをステップアップするには、RANGE_ROWS列を見て分散を調べます。これは、各ステップの間に存在する行数を測定するためです。最後に、あなたが取ることができます[All density]から、結果DENSITY_VECTOR(2番目の結果セットを)と乗算することによって、その[Rows Sampled]内の値STAT_HEADER(最初の結果セット)と平均予想は次のようになり、その列上のクエリのためになるものを参照してください。あなたはその平均をあなたのと比較しますEQ_ROWS 大きく異なる場所が多数ある場合は、ゆがみがあります。

スキューがあることがわかった場合は、非常に高い範囲にフィルターを適用した統計を作成してRANGE_ROWS、これらの値をより適切に推定できるように追加の手順を実行することを検討する必要があります。

これらのフィルタリングされた統計を配置したら、手動で統計を更新する可能性を確認できます。


包括的な回答ありがとうございます。統計についてはあまり経験がありませんが、これは思ったより難しいようです。間違いなくよく見て、ガイドラインに従います。
saso
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.