SQL Server蚈画ガむドおよびその他のベストプラクティス

通垞、ク゚リの最適化に関する投皿は、ク゚リオプティマむザが最適な実行プランを遞択するのに圹立぀正しい方法を瀺しおいたす。WHEREでSARGable匏を䜿甚し、必芁な列のみを取埗し、最適化された敎圢匏むンデックスを䜿甚し、統蚈を曎新したす。



今日、私は他のこずに぀いお話したい-決しおベストプラクティスに属しおいないものに぀いお、足を螏みにじっお以前に実行されたク゚リを遅くしたり、゚ラヌのためにたったく実行されなくなったりする助けを借りお...ヒントず蚈画ガむドに぀いおです。



ヒントはク゚リオプティマむザヌぞのヒントです。完党なリストはMSDNにありたす。それらの䞀郚は実際のヒントですたずえば、OPTIONMAXDOP 4を指定できたす。これにより、ク゚リは最倧䞊列床= 4で実行できたすが、SQL Serverがこのヒントを䜿甚しお䞊列プランを生成する保蚌はありたせん。



他の郚分は、行動ぞの盎接のガむドです。たずえば、OPTIONHASH JOINず蚘述した堎合、SQL ServerはNESTED LOOPSずMERGE JOINのないプランを構築したす。そしお、ハッシュ結合だけで蚈画を構築するこずが䞍可胜であるこずが刀明した堎合、どうなるか知っおいたすかオプティマむザヌはそう蚀うでしょう-私は蚈画を構築できず、ク゚リは実行されたせん。



問題は、オプティマむザがハンマヌで攻撃できるヒントが特定の少なくずも私にはわからないこずです。そしお、どのヒントが手動のヒントであり、䜕か問題が発生した堎合にリク゚ストをクラッシュさせる可胜性がありたす。確かに、これが説明されおいる既補のコレクションはすでにいく぀かありたすが、これはいずれにしおも公匏情報ではなく、い぀でも倉曎される可胜性がありたす。



プランガむドは、特定のヒントのセットを特定の芁求テキストがわかっおいるにバむンドできるようにする正しく翻蚳する方法がわからないものです。これは、たずえば、ORMによっお生成される芁求テキストに盎接圱響を䞎えられない堎合に関連する可胜性がありたす。



ヒントず蚈画ガむドはどちらもベストプラクティスではなく、ヒントずこれらのガむドを省略するこずをお勧めしたす。これは、デヌタの分垃が倉化し、デヌタタむプが倉化し、100䞇以䞊の事態が発生する可胜性があるためです。ヒントを䜿甚したク゚リは、ヒントを䜿甚しない堎合よりも機胜が䜎䞋し、堎合によっおは完党に機胜しなくなりたす。あなたは自分が䜕をしおいるのか、そしおその理由を100認識しおいる必芁がありたす。



なぜ私がこれに取り掛かったのかに぀いおの簡単な説明です。



私は、10から最倧たでのさたざたなサむズのnvarcharフィヌルドがたくさんある広いテヌブルを持っおいたす。そしお、このテヌブルぞの䞀連のク゚リがあり、CHARINDEXはこれらの列の1぀以䞊でサブストリングの出珟を怜玢したす。たずえば、次のようなリク゚ストがありたす。



SELECT *
FROM table
WHERE CHARINDEX(N' ', column)>1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET x ROWS FETCH NEXT y ROWS ONLY


テヌブルのIdにはクラスタヌ化むンデックスがあり、列には䞀意でない非クラスタヌ化むンデックスがありたす。ご存知のずおり、ここではCHARINDEXを䜿甚しおいるため、これはたったくSARGableではありたせん。SBの朜圚的な問題を回避するために、ここで芋぀けるこずができる開いおいるデヌタベヌスStackOverflow2013でこの状況をシミュレヌトしたす。



Idによるクラスタヌ化むンデックスず次のようなク゚リのみを持぀dbo.Postsテヌブルを考えおみたす。



SELECT *
FROM dbo.Posts
WHERE CHARINDEX (N'Data', Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY


実際のデヌタベヌスず䞀臎させるために、Title列にむンデックスを䜜成したす。



CREATE INDEX ix_Title ON dbo.Posts (Title);


もちろん、結果ずしお、クラスタヌ化むンデックスを反察方向にスキャンするこずからなる、完党に論理的な実行蚈画が埗











られたす。

テヌブル「投皿」。スキャンカりント1、論理読み取り516、物理読み取り0、先読み読み取り0、lob論理読み取り0、lob物理読み取り0、lob先読み読み取り0。SQLServer

実行時間

CPU時間= 16 ms


しかし、「デヌタ」ずいう䞀般的な単語の代わりに、もっず珍しいものを探すずどうなりたすかたずえば、N'Aptana 'それが䜕であるかわからない。もちろん、蚈画は同じたたですが、実行統蚈は倚少異なりたす。

テヌブル「投皿」。スキャンカりント1、論理読み取り253191、物理読み取り113、先読み読み取り224602、LOB論理読み取り0、LOB物理読み取り0、LOB先読み読み取り0。SQLServer

実行時間

CPU時間= 2563ミリ秒


そしおこれも論理的です-蚀葉はあたり䞀般的ではなく、SQL Serverはそれを含む25行を芋぀けるためにより倚くのデヌタをスキャンする必芁がありたす。しかし、どういうわけか、それはクヌルではありたせんよね

そしお、私は非クラスタヌ化むンデックスを䜜成しおいたした。たぶん、SQL Serverがそれを䜿甚する方が良いでしょうか圌自身はそれを䜿甚しないので、ヒントを远加したす。



SELECT *
FROM dbo.Posts
WHERE CHARINDEX (N'Aptana', Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY
OPTION (TABLE HINT (dbo.Posts, INDEX(ix_Title)));


そしお、䜕かがどういうわけか完党に悲しいです。実行統蚈

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table 'Posts'. Scan count 5, logical reads 109312, physical reads 5, read-ahead reads 104946, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:

CPU time = 35031 ms


そしお蚈画







実行蚈画は䞊列で、2぀の皮類があり、どちらもtempdbに流出がありたす。ちなみに、非クラスタヌ化むンデックススキャンの埌、キヌルックアップの前に実行される最初の䞊べ替えに泚意しおください。これは、ランダムI / Oの数を削枛しようずする特別なSQL Serverの最適化です。キヌルックアップは、クラスタヌ化むンデックスキヌの昇順で実行されたす。詳现に぀いおは、こちらをご芧ください。



2番目の゜ヌトは、降順のIDで25行を遞択するために必芁です。ちなみに、SQL Serverは、降順でのみIdで䞊べ替え、 "逆"方向でキヌルックアップを実行し、最初にクラスタヌ化むンデックスキヌを昇順ではなく降順で䞊べ替える必芁があるず掚枬した可胜性がありたす。



゚ントリ 'Data'による怜玢を䜿甚しお、非クラスタヌ化むンデックスに関するヒントを含むク゚リの実行に関する統蚈を提䟛しおいたせん。ラップトップのハヌフデッドハヌドドラむブでは、16分以䞊かかり、スクリヌンショットを撮ろうずは思いたせんでした。申し蚳ありたせんが、これ以䞊長く埅ちたくありたせん。

しかし、リク゚ストはどうですかクラスタヌ化むンデックススキャンは究極の倢であり、䜕も速くするこずはできたせんか



すべおの皮類を回避しようずするず、非クラスタヌ化むンデックスを考えお䜜成したしたが、これは通垞、非クラスタヌ化むンデックスのベストプラクティスず通垞芋なされるものず矛盟したす。



CREATE INDEX ix_Id_Title ON dbo.Posts (Id DESC, Title);


次に、ヒントを䜿甚しお、SQL Serverに䜿甚するように指瀺したす。



SELECT *
FROM dbo.Posts
WHERE CHARINDEX (N'Aptana', Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY
OPTION (TABLE HINT (dbo.Posts, INDEX(ix_Id_Title)));


ああ、それはうたくいきたした





テヌブル「投皿」。スキャンカりント1、論理読み取り6259、物理読み取り0、先読み読み取り7816、LOB論理読み取り0、LOB物理読み取り0、LOB先読み読み取り0。SQLServer

実行時間

CPU時間= 1734ミリ秒


プロセッサ時間の増加はそれほど倧きくありたせんが、読む必芁があるのはそれほど倚くはありたせん。頻繁な「デヌタ」はどうですか

テヌブル「投皿」。スキャンカりント1、論理読み取り208、物理読み取り0、先読み読み取り0、LOB論理読み取り0、LOB物理読み取り0、LOB先読み読み取り0。SQLServer

実行時間

CPU時間= 0 ms


うわヌ、それもいいです。さお、リク゚ストはORMからのものであり、そのテキストを倉曎するこずはできないので、このむンデックスをリク゚ストに「ネむル」する方法を理解する必芁がありたす。そしお、蚈画ガむドが助けになりたす。



sp_create_plan_guideMSDNストアドプロシヌゞャは、プランガむドの䜜成に䜿甚されたす。



詳现に怜蚎しおみたしょう。



sp_create_plan_guide [ @name = ] N'plan_guide_name'  
    , [ @stmt = ] N'statement_text'  
    , [ @type = ] N'{ OBJECT | SQL | TEMPLATE }'  
    , [ @module_or_batch = ]  
      {   
        N'[ schema_name. ] object_name'  
        | N'batch_text'  
        | NULL  
      }  
    , [ @params = ] { N'@parameter_name data_type [ ,...n ]' | NULL }   
    , [ @hints = ] { 
                 N'OPTION ( query_hint [ ,...n ] )'   
                 | N'XML_showplan'  
                 | NULL 
      }  


名前 -明確で䞀意のプランガむド名

stmt-これは、ヒントを远加する必芁があるリク゚ストです。このリク゚ストは、アプリケヌションからのリク゚ストずたったく同じように蚘述する必芁があるこずを知っおおくこずが重芁です。奇劙なスペヌスプランガむドは䜿甚されたせん。間違った改行プランガむドは䜿甚されたせん。物事をより簡単にするために、少し埌で戻る「ラむフハック」がありたすここで芋぀けたした。



タむプ -指定されたリク゚ストの堎所を瀺したす stmt... ストアドプロシヌゞャの䞀郚である堎合は、OBJECTにする必芁がありたす。これが耇数のリク゚ストのバッチの䞀郚である堎合、たたはアドホックリク゚ストの堎合、たたは1぀のリク゚ストのバッチの堎合は、SQLが必芁です。ここでTEMPLATEが瀺されおいる堎合、これは、MSDNで読むこずができるク゚リのパラメヌタヌ化に関する別の話です。



@module_or_batchは䟝存したすタむプ。もしタむプ= 'OBJECT'、これはストアドプロシヌゞャの名前である必芁がありたす。もしタむプ= 'BATCH'-バッチ党䜓のテキストがあり、アプリケヌションから取埗したものを単語ごずに指定したす。奇劙なスペヌスたあ、あなたはすでに知っおいたす。NULLの堎合、これは1぀の芁求からのバッチであり、stmt すべおの制限付き。



パラメヌタ-リク゚ストに枡されるすべおのパラメヌタずデヌタタむプがここにリストされたす。



最埌に@hintsがいい郚分です。ここで、リク゚ストに远加するヒントを指定する必芁がありたす。ここでは、必芁に応じお、必芁な実行プランをXML圢匏で明瀺的に挿入できたす。このパラメヌタヌはNULLにするこずもできたす。これにより、SQL Serverは、ク゚リで明瀺的に指定されおいるヒントを䜿甚したせん。stmt...



そこで、ク゚リのプランガむドを䜜成したす。



DECLARE @sql nvarchar(max) = N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (N''Data'', Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY';

exec sp_create_plan_guide @name = N'PG_dboPosts_Index_Id_Title'  
    ,  @stmt =  @sql
    ,  @type = N'SQL'
    ,  @module_or_batch =  NULL          
    ,  @params = NULL
    ,  @hints =  N'OPTION (TABLE HINT (dbo.Posts, INDEX (ix_Id_Title)))'


そしお、リク゚ストを実行しようずしたす

SELECT *
FROM dbo.Posts
WHERE CHARINDEX (N'Data', Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY


うわヌ、うたく







いきたした。最埌のSELECTステヌトメントのプロパティには、次のように衚瀺







されたす。すばらしい、蚈画ガむドが適甚されたした。今「Aptana」を怜玢するずどうなりたすかそしお、すべおが悪くなりたす-すべおの結果ずずもにクラスタヌ化むンデックスのスキャンに戻りたす。どうしおたた、プランガむドはSPECIFICク゚リに適甚されるため、そのテキストは実行䞭のテキストず1察1で䞀臎したす。



幞いなこずに、私のシステム䞊のほずんどのリク゚ストはパラメヌタヌ化されおいたす。パラメヌタ化されおいないク゚リを操䜜しなかったので、操䜜する必芁がないこずを願っおいたす。それらに぀いおは、テンプレヌトを䜿甚するこずができTEMPLATEに぀いお少し䞊を参照、デヌタベヌスで匷制パラメヌタ化を有効にできたす䜕をしおいるのかを理解せずにこれを行わないでください!!!。おそらく、その埌、蚈画ガむドをリンクできるようになりたす。しかし、私は本圓にそれを詊しおいたせん。



私の堎合、リク゚ストは次のように実行されたす



exec sp_executesql  
N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (@p0, Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY;'
, N'@p0 nvarchar(250), @p1 int, @p2 int'
, @p0 = N'Aptana', @p1 = 0, @p2 = 25;


したがっお、察応するプランガむドを䜜成したす。



DECLARE @sql nvarchar(max) = N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (@p0, Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY;';

exec sp_create_plan_guide @name = N'PG_paramters_dboPosts_Index_Id_Title'  
    ,  @stmt =  @sql
    ,  @type = N'SQL'
    ,  @module_or_batch =  NULL          
    ,  @params = N'@p0 nvarchar(250), @p1 int, @p2 int'
    ,  @hints =  N'OPTION (TABLE HINT (dbo.Posts, INDEX (ix_Id_Title)))'


そしお、䞇事、すべおが必芁に応じお機胜したす











枩宀条件の倖にいるず、パラメヌタヌを正しく指定できるずは限りたせんstmtプランガむドをリク゚ストに添付するために、私は䞊蚘の「ラむフハック」がありたす。プランキャッシュをクリアし、ガむドを削陀し、パラメヌタヌ化されたク゚リを再床実行しお、キャッシュからその実行プランずplan_handleを取埗したす。



これに察する芁求は、たずえば次のように䜿甚できたす。



SELECT qs.plan_handle, st.text, qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text (qs.sql_handle) st
CROSS APPLY sys.dm_exec_query_plan (qs.plan_handle) qp






これで、sp_create_plan_guide_from_handleストアドプロシヌゞャを䜿甚しお、既存のプランからプランガむドを䜜成できたす。



パラメヌタずしお受け取りたす名前-䜜成されたガむドの名前、@ plan_handle-既存の実行プランのハンドル、および@statement_start_offset-ガむドを䜜成する必芁があるバッチ内のステヌトメントの開始を定矩したす。



詊しおみる



exec sp_create_plan_guide_from_handle N'PG_dboPosts_from_handle'  
    , 0x0600050018263314F048E3652102000001000000000000000000000000000000000000000000000000000000
    , NULL;


そしお今、SSMSでProgrammability-> Plan Guidesにあるものを確認し







たす。珟圚の実行プランは、Plan Guide 'PG_dboPosts_from_handle'を䜿甚しおリク゚ストに「釘付け」されおいたすが、䜕よりも、ほずんどすべおのオブゞェクトず同様ですSSMSでは、必芁な方法でスクリプトを䜜成しお再䜜成できたす。



RMB、スクリプト->ドロップしお䜜成するず、@ hintsパラメヌタの倀を必芁なものに眮き換える必芁がある既補のスクリプトが埗られるため、結果ずしお次のようになりたす。



USE [StackOverflow2013]
GO

/****** Object:  PlanGuide PG_dboPosts_from_handle    Script Date: 05.07.2020 16:25:04 ******/
EXEC sp_control_plan_guide @operation = N'DROP', @name = N'[PG_dboPosts_from_handle]'
GO
/****** Object:  PlanGuide PG_dboPosts_from_handle    Script Date: 05.07.2020 16:25:04 ******/
EXEC sp_create_plan_guide @name = N'[PG_dboPosts_from_handle]', @stmt = N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (@p0, Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY', @type = N'SQL', @module_or_batch = N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (@p0, Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY;', 
@params = N'@p0 nvarchar(250), @p1 int, @p2 int', 
@hints = N'OPTION (TABLE HINT (dbo.Posts, INDEX (ix_Id_Title)))'
GO


リク゚ストを実行しお再実行したす。おっず、すべおが機胜







したす。パラメヌタ倀を眮き換えるず、すべおが同じように機胜したす。



1぀のステヌトメントに察応できるガむドは1぀だけであるこずに泚意しおください。同じステヌトメントに別のガむドを远加しようずするず、゚ラヌメッセヌゞが衚瀺されたす。

メッセヌゞ10502、レベル16、状態1、行1で

指定されたステヌトメントにより、プランガむド 'PG_dboPosts_from_handle2'を䜜成できたせんstmtおよび@module_or_batch、たたは@plan_handleず@statement_start_offsetによっお、デヌタベヌス内の既存のプランガむド 'PG_dboPosts_from_handle'ず䞀臎したす。新しいプランガむドを䜜成する前に、既存のプランガむドを削陀しおください。


最埌に觊れおおきたいのは、sp_control_plan_guideストアドプロシヌゞャです。



その助けを借りお、プランガむドを削陀、無効化、有効化できたす-䞀床に1぀ず぀、名前を瀺し、すべおのガむドよくわかりたせん-たたはすべお。たたはプロシヌゞャが実行されるデヌタベヌスのコンテキスト内のすべお-このため、倀@operationパラメヌタヌ-DROP ALL、DISABLE ALL、ENABLE ALL。特定のプランにHPを䜿甚する䟋をすぐ䞊に瀺したす。指定した名前の特定のプランガむドは削陀されたす。



ヒントず蚈画ガむドなしで実行できたしたか



䞀般に、ク゚リオプティマむザヌが愚かである皮のゲヌムを実行しおいるように思われ、99の確率で私の堎合のようにある皮のゲヌムを実行しおいるこずがどれほど最良かを知っおいる堎合。ただし、リク゚ストテキストに盎接圱響を䞎えるこずができない堎合は、リク゚ストにヒントを远加できるプランガむドが呜の恩人になるこずができたす。必芁に応じおリク゚ストテキストを曞き換える胜力があるずしたしょう-これで䜕か倉曎できたすか承知したした党文怜玢の圢匏で「゚キゟチック」を䜿甚しなくおも、実際にはここで䜿甚する必芁がありたす。たずえば、そのようなク゚リには、完党に通垞のク゚リの蚈画ず実行統蚈がありたす。



;WITH c AS (
    SELECT p2.id
    FROM dbo.Posts p2
    WHERE CHARINDEX (N'Aptana', Title) > 1
    ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
    OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY
)
SELECT p.* 
FROM dbo.Posts p
JOIN c ON p.id = c.id;




テヌブル「投皿」。スキャンカりント1、論理読み取り6250、物理読み取り0、先読み読み取り0、LOB論理読み取り0、LOB物理読み取り0、LOB先読み読み取り0。SQLServer

実行時間

CPU時間= 1500ミリ秒


SQL Serverは最初にix_Id_Titleの "クロヌルされた"むンデックスによっお必芁な25の識別子を芋぀け、それからクラスタヌむンデックスで遞択された識別子を怜玢したす-ガむドよりも優れおいたすしかし、「デヌタ」に察しおク゚リを実行し、20,000行目から25行を衚瀺するずどうなりたすか。



;WITH c AS (
    SELECT p2.id
    FROM dbo.Posts p2 
    WHERE CHARINDEX (N'Data', Title) > 1
    ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
    OFFSET 20000 ROWS FETCH NEXT 25 ROWS ONLY
)
SELECT p.* 
FROM dbo.Posts p
JOIN c ON p.id = c.id;




テヌブル「投皿」。スキャンカりント1、論理読み取り5914、物理読み取り0、先読み読み取り0、LOB論理読み取り11、LOB物理読み取り0、LOB先読み読み取り0。SQLServer

実行時間

CPU時間= 1453ミリ秒


exec sp_executesql  
N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (@p0, Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY;'
, N'@p0 nvarchar(250), @p1 int, @p2 int'
, @p0 = N'Data', @p1 = 20000, @p2 = 25;




Table 'Posts'. Scan count 1, logical reads 87174, physical reads 0, read-ahead reads 0, lob logical reads 11, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:

CPU time = 1437 ms


はい、charindexに費やされるため、プロセッサ時間は同じですが、ガむドを䜿甚したリク゚ストでは、桁違いに倚くの読み取りが行われ、これが問題になる可胜性がありたす。



最終結果をたずめたしょう。ヒントずガむドは、今ここであなたを助けるこずができたすが、物事をさらに悪化させる可胜性がありたす。リク゚ストテキストでむンデックス付きのヒントを明瀺的に指定しおからむンデックスを削陀するず、ク゚リを実行できなくなりたす。 SQL Server 2017では、むンデックスを削陀した埌、ガむド付きのク゚リは正垞に実行されたす-ガむドは無芖されたすが、SQL Serverのすべおのバヌゞョンで垞にこのようになるずは限りたせん。



ロシア語のプランガむドに぀いおはあたり情報がないので、自分で曞くこずにしたした。ここで読むこずができたすプランガむドの䜿甚に関する制限に぀いお、特に、PGを䜿甚しおヒントを䜿甚しおむンデックスを明瀺的に瀺すず、リク゚ストが倱敗する堎合があるずいう事実に぀いお。私はあなたがそれらを決しお䜿甚しないこずを望みたす。そしお、もしあなたが-もし、幞運なら-これがどこに぀ながるのか知っおいるなら。



All Articles