統計更新のためのサンプルサイズによる奇妙な動作


25

SQL Server(2012)の統計情報の更新を使用してサンプリングのしきい値を調査し、いくつかの奇妙な動作に気付きました。基本的に、サンプリングされる行の数は、同じデータセットであっても、状況によって異なるようです。

このクエリを実行します。

--Drop table if exists
IF (OBJECT_ID('dbo.Test')) IS NOT NULL DROP TABLE dbo.Test;

--Create Table for Testing
CREATE TABLE dbo.Test(Id INT IDENTITY(1,1) CONSTRAINT PK_Test PRIMARY KEY CLUSTERED, TextValue VARCHAR(20) NULL);

--Insert enough data so we have more than 8Mb (the threshold at which sampling kicks in)
INSERT INTO dbo.Test(TextValue) 
SELECT TOP 1000000 'blahblahblah'
FROM sys.objects a, sys.objects b, sys.objects c, sys.objects d;  

--Create Index on TextValue
CREATE INDEX IX_Test_TextValue ON dbo.Test(TextValue);

--Update Statistics without specifying how many rows to sample
UPDATE STATISTICS dbo.Test IX_Test_TextValue;

--View the Statistics
DBCC SHOW_STATISTICS('dbo.Test', IX_Test_TextValue) WITH STAT_HEADER;

SHOW_STATISTICSの出力を見ると、 "Samples Rows"が完全な実行ごとに変化していることがわかります(つまり、テーブルが削除、再作成、および再作成されます)。

例えば:

サンプリングされた行

  • 318618
  • 319240
  • 324198
  • 314154

私の期待は、テーブルが同一であるたびにこの数値が同じになることでした。ちなみに、データを削除して再挿入するだけでは、この動作は発生しません。

これは重要な質問ではありませんが、何が起こっているのかを理解したいと思います。


2
挿入するファイルグループ内のファイルの数。私は2016年に数回を試みたし、両方の時間は、テーブルには、二つの異なるサンプルがIのこぎりは314712と315270だったサイズ64で279行と1と3584ページに分割された- 279の両方の正確な倍数は
マーティン・スミス

1
@JoeObbish-常にすべてのページを読み上げるので、私は驚かなかった。何らかの理由で、質問の数字がそのパターンに一致しないと思いました。しかし、彼らが行う数学をやり直した。318618 = 1142*279319240 = 1144*279 + 64324198=1162*279および314154=1126分散がサンプリングされたページ数ですので。
マーティンスミス

@MartinSmithJust 1つのファイル-279の図は興味深いです、私は常に関与するパターンを理解するのが好き
マシューマクギフェン

回答:


26

バックグラウンド

統計オブジェクトのデータは、次の形式のステートメントを使用して収集されます。

SELECT 
    StatMan([SC0], [SC1], [SB0000]) 
FROM 
(
    SELECT TOP 100 PERCENT 
        [SC0], [SC1], STEP_DIRECTION([SC0]) OVER (ORDER BY NULL) AS [SB0000]
    FROM 
    (
        SELECT 
            [TextValue] AS [SC0], 
            [Id] AS [SC1] 
        FROM [dbo].[Test] 
            TABLESAMPLE SYSTEM (2.223684e+001 PERCENT) 
            WITH (READUNCOMMITTED) 
    ) AS _MS_UPDSTATS_TBL_HELPER 
    ORDER BY 
        [SC0], 
        [SC1], 
        [SB0000] 
) AS _MS_UPDSTATS_TBL
OPTION (MAXDOP 1)

このステートメントは、拡張イベントまたはプロファイラー(SP:StmtCompleted)で収集できます。

統計生成クエリは、多くの場合、非クラスタ化インデックスではなくベーステーブルにアクセスして、非クラスタ化インデックスページで自然に発生する値のクラスタリングを回避します。

サンプリングされる行の数は、サンプリング用に選択されたページ全体の数によって異なります。テーブルの各ページが選択されているか、選択されていないかのいずれかです。選択したページのすべての行が統計に寄与します。

乱数

SQL Serverは、乱数ジェネレーターを使用して、ページが修飾されるかどうかを決定します。このインスタンスで使用されるジェネレーターは、以下に示すパラメーター値を持つレーマー乱数ジェネレーターです。

X  = X シード * 7 5 MOD(2 31 - 1)

の値は、次の合計として計算されます。Xseed

  • (の低整数部bigint)ベーステーブルのpartition_id

    SELECT
        P.[partition_id] & 0xFFFFFFFF
    FROM sys.partitions AS P
    WHERE
        P.[object_id] = OBJECT_ID(N'dbo.Test', N'U')
        AND P.index_id = 1;
  • REPEATABLE句で指定された値

    • sampledのUPDATE STATISTICS場合、REPEATABLE値は1です。
    • この値はm_randomSeed、トレースフラグ8666が有効な場合に、実行プランに表示されるアクセス方法の内部デバッグ情報の要素で公開されます。たとえば、<Field FieldName="m_randomSeed" FieldValue="1" />

SQL Server 2012の場合、この計算は次の場所で行われsqlmin!UnOrderPageScanner::StartScanます。

mov     edx,dword ptr [rcx+30h]
add     edx,dword ptr [rcx+2Ch]

メモリat [rcx+30h]にはパーティションIDの下位32ビットが[rcx+2Ch]含まれ、メモリat にはREPEATABLE使用中の値が含まれます。

乱数ジェネレーターは、後で同じメソッドで初期化されsqlmin!RandomNumGenerator::Initます。

imul    r9d,r9d,41A7h

... 上記の式に示すように、シードに41A716進数(16807 10進数= 7 5)を掛けます。

その後、(個々のページの)乱数は、にインライン化された同じ基本コードを使用して生成されsqlmin!UnOrderPageScanner::SetupSubScannerます。

スタットマン

StatMan上記のクエリ例では、T-SQLステートメントと同じページが収集されます。

SELECT 
    COUNT_BIG(*) 
FROM dbo.Test AS T 
    TABLESAMPLE SYSTEM (2.223684e+001 PERCENT)  -- Same sample %
    REPEATABLE (1)                              -- Always 1 for statman
    WITH (INDEX(0));                            -- Scan base object

これは、次の出力と一致します。

SELECT 
    DDSP.rows_sampled
FROM sys.stats AS S
CROSS APPLY sys.dm_db_stats_properties(S.[object_id], S.stats_id) AS DDSP
WHERE 
    S.[object_id] = OBJECT_ID(N'dbo.Test', N'U')
    AND S.[name] = N'IX_Test_TextValue';

エッジケース

MINSTD Lehmer乱数ジェネレーターを使用した結果の1つは、シード値zeroおよびint.maxを使用しないことです。これにより、アルゴリズムがゼロのシーケンスを生成するようになります(すべてのページを選択)。

コードはゼロを検出し、その場合のシードとしてシステム「クロック」からの値を使用します。種子はint.max(ある場合には、同じことをしない0x7FFFFFFF= 2 31 - 1)。

初期シードはパーティションIDの下位32ビットと値の合計として計算されるため、このシナリオを設計できREPEATABLEます。REPEATABLEシードがint.maxになるため、サンプルとして選択されるすべてのページの値は次のとおりです。

SELECT
    0x7FFFFFFF - (P.[partition_id] & 0xFFFFFFFF)
FROM sys.partitions AS P
WHERE
    P.[object_id] = OBJECT_ID(N'dbo.Test', N'U')
    AND P.index_id = 1;

それを完全な例に取り込んでください:

DECLARE @SQL nvarchar(4000) = 
    N'
    SELECT
        COUNT_BIG(*) 
    FROM dbo.Test AS T 
        TABLESAMPLE (0 PERCENT) 
        REPEATABLE (' +
        (
            SELECT TOP (1)
                CONVERT(nvarchar(11), 0x7FFFFFFF - P.[partition_id] & 0xFFFFFFFF)
            FROM sys.partitions AS P
            WHERE
                P.[object_id] = OBJECT_ID(N'dbo.Test', N'U')
                AND P.index_id = 1
        ) + ')
        WITH (INDEX(0));';

PRINT @SQL;
--EXECUTE (@SQL);

これにより、TABLESAMPLE句に記載されている内容(ゼロパーセントであっても)にかかわらず、すべてのページのすべての行が選択されます。


11

これは素晴らしい質問です!私は確かに知っていることから始め、それから推測に移ります。このことについての詳細は、私のブログ投稿にあります

統計の更新のサンプリングTABLESAMPLEは、舞台裏で使用されます。そのオンラインに関するドキュメントを見つけるのは非常に簡単です。ただし、によって返される行がオブジェクトのにTABLESAMPLE部分的に依存していることはあまり知られていないと思いhobt_idます。オブジェクトをドロップして再作成すると、新しいオブジェクトが取得されるhobt_idため、ランダムサンプリングによって返される行は異なります。

データを削除して再挿入してもhobt_id、同じままです。データがディスク上で同じ方法でレイアウトされている限り(割り当て順序スキャンは同じ順序で同じ結果を返します)、サンプリングされたデータは変更されません。

テーブルのクラスター化インデックスを再構築することで、サンプリングされる行の数を変更することもできます。例えば:

UPDATE STATISTICS dbo.Test IX_Test_TextValue;

DBCC SHOW_STATISTICS('dbo.Test', IX_Test_TextValue) WITH STAT_HEADER; -- 273862 rows

ALTER INDEX PK_Test on Test REBUILD;

UPDATE STATISTICS dbo.Test IX_Test_TextValue;

DBCC SHOW_STATISTICS('dbo.Test', IX_Test_TextValue) WITH STAT_HEADER; -- 273320 rows

その理由は、SQL Serverがインデックスのサンプリング統計を収集するときに、非クラスター化インデックスではなくクラスター化インデックスをスキャンするためだと考えています。私はまたのための(隠された統計情報の更新クエリをトレースし、私たちのものに)隠された値があることを考えるREPEATABLEと使用はTABLESAMPLE。私はそれのいずれも証明していませんが、クラスター化インデックスの再構築でヒストグラムと行が変更をサンプリングする理由を説明します。


3

ページごとにランダムな確率を割り当てるという点でTABLESAMPLEがどのように機能するかを忘れました。- マーティン・スミス

Inside Microsoft SQL Server 2008: Itzik Ben-GanによるT-SQLクエリでこれを見ましたが、コメントとして追加することはできませんので、ここに投稿します。他の人にも興味深いと思います:

ここに画像の説明を入力してください

RojiによるTABLESAMPLEを使用したサンプリングも参照してください。P.トーマス。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.