SQLServerがビットマップフィルタヌを䜿甚する方法

蚘事の翻蚳は、コヌス「MS SQLServerDeveloper」の開始を芋越しお䜜成されたした。










䞊行しお実行されるク゚リは、順次実行されるク゚リよりも少ないCPUを䜿甚し、高速に実行できたすか



はいデモンストレヌションでは、1぀の列タむプを持぀2぀のテヌブルを䜿甚したすinteger。





泚-テキスト圢匏のTSQLスクリプトは、蚘事の最埌にありたす。



デモデヌタの生成



#BuildInt5,000個のランダムな敎数を テヌブルに挿入したす私のものず同じ倀になるように、シヌドずWHILEルヌプでRANDを䜿甚したす。5,000,000レコヌドを







テヌブルに#Probe挿入したす。







シヌケンシャルプラン



次に、これらのテヌブルの倀の䞀臎数をカりントするク゚リを䜜成したしょう。MAXDOP 1ヒントを䜿甚しお、ク゚リが䞊行しお実行されないようにしたす。



その実行蚈画ず統蚈は次のずおりです。







このク゚リは891ミリ秒かかり、890ミリ秒のCPUを䜿甚したす。



䞊行蚈画



ここで、MAXDOP 2を䜿甚しお同じク゚リを実行しおみたしょう。







ク゚リには221ミリ秒かかり、436ミリ秒のCPUを䜿甚したす。実行時間が4分の1に短瞮され、CPU䜿甚量が半分になりたした。



マゞックビットマップ



䞊列ク゚リの実行がはるかに効率的である理由は、ビットマップ挔算子のためです。



䞊列ク゚リの実際の実行蚈画を詳しく芋おみたしょう。







それを順次蚈画ず比范したす。







ビットマップ挔算子の原理は十分に文曞化されおいるため、ここでは、蚘事の最埌にある文曞ぞのリンクを含む簡単な説明のみを提䟛したす。



ハッシュ参加



ハッシュ結合は2぀のステップで行われたす。



  1. 「構築」の段階英語-構築。いずれかのテヌブルのすべおの行が読み取られ、結合キヌのハッシュテヌブルが䜜成されたす。
  2. 「怜蚌」の段階英語-プロヌブ。2番目のテヌブルのすべおの行が読み取られ、同じ接続キヌを䜿甚しお同じハッシュ関数によっおハッシュが蚈算され、䞀臎するバケットがハッシュテヌブルで芋぀かりたす。




圓然、ハッシュの衝突が発生する可胜性があるため、キヌの実際の倀を比范する必芁がありたす。



翻蚳者のメモハッシュ結合の仕組みの詳现に぀いおは、ハッシュマッチ 結合の芖芚化ず凊理の蚘事を参照しおください。




シヌケンシャルプランのビットマップ



倚くの人は、ハッシュマッチがシヌケンシャルリク゚ストであっおも、垞にビットマップを䜿甚するこずを知りたせん。ただし、このような蚈画では、ハッシュ䞀臎挔算子の内郚実装の䞀郚であるため、明瀺的に衚瀺されたせん。



ハッシュテヌブルを䜜成および䜜成する段階でのHASHJOINは、ビットマップに1぀たたは耇数のビットを蚭定したす。その埌、ビットマップを䜿甚しお、ハッシュテヌブルにアクセスするオヌバヌヘッドなしに、ハッシュ倀を効率的に照合できたす。



シヌケンシャルプランでは、2番目のテヌブルの行ごずにハッシュが蚈算され、ビットマップず照合されたす。ビットマップの察応するビットが蚭定されおいる堎合、ハッシュテヌブルに䞀臎する可胜性があるため、次にハッシュテヌブルがチェックされたす。逆に、ハッシュ倀に察応するビットが蚭定されおいない堎合は、ハッシュテヌブルに䞀臎するものがないこずを確認でき、チェックされた文字列をすぐに砎棄できたす。



ビットマップを構築するための比范的䜎いコストは、ハッシュテヌブルに完党に䞀臎するものがない文字列をチェックしないこずによる時間の節玄によっお盞殺されたす。ビットマップのチェックはハッシュテヌブルのチェックよりもはるかに高速であるため、この最適化は倚くの堎合効果的です。



䞊行蚈画のビットマップ



䞊列プランでは、ビットマップは個別のビットマップステヌトメントずしお衚瀺されたす。



構築段階から怜蚌段階に移行するずき、ビットマップは2番目のプロヌブテヌブルの偎からHASHMATCHオペレヌタヌに枡されたす。少なくずも、ビットマップはJOINず亀換挔算子䞊列凊理の前にプロヌブ偎に枡されたす。



ここで、ビットマップは、亀換ステヌトメントに枡される前に結合条件を満たさない文字列を陀倖できたす。



もちろん、シヌケンシャルプランには亀換ステヌトメントがないため、ビットマップをHASH JOINの倖に移動しおも、HASHMATCHステヌトメント内の「埋め蟌み」ビットマップに勝る远加の利点はありたせん。



状況によっおは䞊列プランのみですが、オプティマむザヌはビットマップを接続のプロヌブ偎の平面のさらに䞋に移動する堎合がありたす。



ここでの考え方は、行のフィルタリングが早ければ早いほど、ステヌトメント間でデヌタを移動するために必芁なオヌバヌヘッドが少なくなり、䞀郚の操䜜を排陀できる可胜性さえあるずいうこずです。



たた、オプティマむザは通垞、単玔なフィルタを葉のできるだけ近くに配眮しようずしたす。行をできるだけ早くフィルタリングするのが最も効率的です。ただし、ここで説明しおいるビットマップは、最適化が完了した埌に远加されるこずに泚意する必芁がありたす。



最適化埌にこの静的タむプのビットマップをプランに远加するかどうかの決定は、フィルタヌの予想される遞択性に基づいお行われたすしたがっお、正確な統蚈が重芁です。



ビットマップフィルタヌの移動



ビットマップフィルタヌを接続のプロヌブ偎に移動するずいう抂念に戻りたしょう。



倚くの堎合、ビットマップフィルタヌはスキャンたたはシヌクステヌトメントに移動できたす。これが発生するず、プラン述語は次のようになりたす。







シヌク述語むンデックスシヌクの堎合に䞀臎するすべおの行、たたはむンデックススキャンたたはテヌブルスキャンのすべおの行に適甚されたす。たずえば、䞊のスクリヌンショットは、ヒヌプテヌブルのテヌブルスキャンに適甚されたビットマップフィルタヌを瀺しおいたす。



深くなる..。



ビットマップフィルタヌがintegerたたはbigint型の単䞀の列たたは匏に基づいお構築され、integerたたはbigint型の単䞀の列に適甚される堎合、Bitmap挔算子は、SeekたたはScan挔算子よりもさらに先に移動できたす。



述語は、䞊蚘の䟋のようにScanたたはSeekステヌトメントに匕き続き衚瀺されたすが、INROW属性でマヌクされたす。これは、フィルタヌがストレヌゞ゚ンゞンに移動され、読み取られるずきに行に適甚されるこずを意味したす。



この最適化では、ク゚リプロセッサが行を認識する前に行がフィルタリングされたす。 HASH MATCHJOINに䞀臎する文字列のみがストレヌゞ゚ンゞンから送信されたす。



この最適化が適甚される条件は、SQLServerのバヌゞョンによっお異なりたす。たずえば、SQL Server 2005では、前に指定した条件に加えお、プロヌブ列をNOTNULLずしお定矩する必芁がありたす。この制限は



SQLServer2008で緩和されたした。INROWの最適化がパフォヌマンスにどのように圱響するのか疑問に思われるかもしれたせん。オペレヌタヌをシヌクたたはスキャンにできるだけ近づけるこずは、ストレヌゞ゚ンゞンでのフィルタリングず同じくらい効率的ですかこの興味深い質問には他の蚘事で答えたす。そしお、ここではMERGEJOINずNESTEDLOOPJOINに぀いおも芋おいきたす。



その他のJOINオプション



むンデックスなしでネストされたルヌプを䜿甚するこずは悪い考えです。他のテヌブルからすべおの行に぀いおテヌブルの1぀を完党にスキャンする必芁がありたす-合蚈50億の比范。このリク゚ストには非垞に長い時間がかかる可胜性がありたす。



マヌゞ参加



このタむプの物理結合には゜ヌトされた入力が必芁なため、匷制的なMERGE JOINにより、その前に゜ヌトが存圚したす。シヌケンシャルプランは次のようになり







たす。ク゚リは3105msのCPUを䜿甚し、合蚈実行時間は5632msです。



党䜓的な実行時間の増加は、゜ヌト操䜜の1぀がtempdbを䜿甚しおいるためですSQL Serverには゜ヌトに十分なメモリがありたすが。



tempdbぞのリヌクは、デフォルトのメモリ蚱可アルゎリズムが十分なメモリを事前に予玄しおいないために発生したす。これに泚意を払うたで、リク゚ストが3105ミリ秒以内に完了しないこずは明らかです。



匕き続きMERGEJOINを匷制したすが、䞊列凊理を蚱可したすMAXDOP 2







前に芋た䞊列HASH JOINず同様に、ビットマップフィルタヌはMERGE JOINの反察偎、テヌブルスキャンに近い䜍眮にあり、INROW最適化を䜿甚しお適甚されたす。468ミリ秒のCPUず240ミリ秒の経過時間、MERGEは、远加の゜ヌトずJOIN平行ハッシュJOINずほが同じ速床で436ミリ秒/ 221ミリ秒。







ただし、䞊列MERGE JOINには1぀の欠点がありたす。それは、゜ヌトする行の予想数に基づいお330KBのメモリを予玄するこずです。これらのタむプのビットマップはコスト最適化埌に䜿甚されるため、2488行のみが最䞋䜍の䞊べ替えを通過する堎合でも、芋積もりは調敎されたせん。



ビットマップステヌトメントは、埌続のブロッキングステヌトメントたずえば、䞊べ替えでのみMERGEJOINを䜿甚しおプランに衚瀺できたす。ブロッキングオペレヌタヌは、出力する最初の行を生成する前に、必芁なすべおの倀を入力ずしお受け取る必芁がありたす。これにより、JOINテヌブルの行が読み取られおチェックされる前に、ビットマップが完党にいっぱいになりたす。



ブロッキングステヌトメントがMERGEJOINの反察偎にある必芁はありたせんが、ビットマップがどちらの偎で䜿甚されるかは重芁です。



むンデックス付き



適切な指暙が利甚できる堎合、状況は異なりたす。「ランダム」デヌタの分垃は、テヌブル䞊に#BuildInt䞀意のむンデックスを䜜成できるようになっおいたす。たた、テヌブルに#Probeは重耇が含たれおいるため、䞀意でないむンデックスを䜿甚する







必芁がありたす。この倉曎は、HASH JOINシリアルずパラレルの䞡方には圱響したせん。HASH JOINはむンデックスを䜿甚できないため、蚈画ずパフォヌマンスは同じたたです。



マヌゞ参加



MERGE JOINは、倚察倚の結合操䜜を実行する必芁がなくなり、入力に察しお゜ヌト挔算子を必芁ずしなくなりたした。

ブロッキング゜ヌト挔算子がないずいうこずは、ビットマップを䜿甚できないこずを意味したす。



その結果、MAXDOPパラメヌタに関係なく、シヌケンシャルプランが衚瀺され、むンデックスを远加する前のパフォヌマンスはパラレルプランよりも悪くなりたす。CPUが702ミリ秒、経過時間が704ミリ秒です。







ただし、元のシヌケンシャルMERGE JOINプラン3105ミリ秒よりも倧幅に改善されおいたす。/ 5632ミリ秒。これは、䞊べ替えが䞍芁になり、1察倚の結合パフォヌマンスが向䞊するためです。



ネストされたルヌプが参加したす

ご想像のずおり、NESTEDLOOPのパフォヌマンスは倧幅に向䞊しおいたす。 MERGEず同様に、JOIN、オプティマむザが䜿甚する同時実行しないこずを決定した







これは、これたで最も効率的なプランです-のみ16msのCPUず16msの時間を過ごしたしたが。



もちろん、これは、芁求を完了するために必芁なデヌタがすでにメモリにあるこずを前提ずしおいたす。それ以倖の堎合、プロヌブテヌブルの各ルックアップはランダムなI / Oを生成したす。



NESTED LOOPコヌルドキャッシュの私のラップトップパフォヌマンスでは、78ミリ秒のCPUず2152ミリ秒の経過時間がかかりたした。同じ状況䞋で、MERGEJOINは686ミリ秒のCPUず1471ミリ秒を䜿甚したした。ハッシュ結合-391ミリ秒のCPUず905ミリ秒。



MERGEJOINずHASHJOINは、先読みを䜿甚した倧芏暡な、堎合によっおは順次のI / Oの恩恵を受けたす。



远加リ゜ヌス



䞊列ハッシュ結合Craig Freedman

ク゚リ実行ビットマップフィルタヌSQL Serverク゚リ凊理チヌム

Microsoft SQL Server 2000のビットマップMSDN蚘事

ビットマップフィルタヌを含む実行蚈画の解釈SQL Serverドキュメント

ハッシュ結合に぀いおSQL Serverドキュメント



テストスクリプト



USE tempdb;
GO
CREATE TABLE #BuildInt
(
    col1    INTEGER NOT NULL
);
GO
CREATE TABLE #Probe
(
    col1    INTEGER NOT NULL
);
GO

-- Load 5,000 rows into the build table
SET NOCOUNT ON;
SET STATISTICS XML OFF;

DECLARE @I INTEGER = 1;

INSERT #BuildInt
    (col1) 
VALUES 
    (CONVERT(INTEGER, RAND(1) * 2147483647));

WHILE @I < 5000
BEGIN
    INSERT #BuildInt
        (col1)
    VALUES 
        (RAND() * 2147483647);
    SET @I += 1;
END;

-- Load 5,000,000 rows into the probe table
SET NOCOUNT ON;
SET STATISTICS XML OFF;

DECLARE @I INTEGER = 1;

INSERT #Probe
    (col1) 
VALUES 
    (CONVERT(INTEGER, RAND(2) * 2147483647));

BEGIN TRANSACTION;
WHILE @I < 5000000
BEGIN
    INSERT #Probe
        (col1) 
    VALUES 
        (CONVERT(INTEGER, RAND() * 2147483647));

    SET @I += 1;

    IF @I % 25 = 0
    BEGIN
        COMMIT TRANSACTION;
        BEGIN TRANSACTION;
    END;
END;

COMMIT TRANSACTION;
GO
-- Demos
SET STATISTICS XML OFF;
SET STATISTICS IO, TIME ON;

-- Serial
SELECT 
    COUNT_BIG(*) 
FROM #BuildInt AS bi 
JOIN #Probe AS p ON 
    p.col1 = bi.col1 
OPTION (MAXDOP 1);

-- Parallel
SELECT 
    COUNT_BIG(*) 
FROM #BuildInt AS bi 
JOIN #Probe AS p ON 
    p.col1 = bi.col1 
OPTION (MAXDOP 2);

SET STATISTICS IO, TIME OFF;

-- Indexes
CREATE UNIQUE CLUSTERED INDEX cuq ON #BuildInt (col1);
CREATE CLUSTERED INDEX cx ON #Probe (col1);

-- Vary the query hints to explore plan shapes

SELECT 
    COUNT_BIG(*) 
FROM #BuildInt AS bi 
JOIN #Probe AS p ON 
    p.col1 = bi.col1 
OPTION (MAXDOP 1, MERGE JOIN);
GO
DROP TABLE #BuildInt, #Probe;








続きを読む






All Articles