オプティマイザが推定するコストが0.01の1つのサーバーに対するクエリがあります。実際には、それは非常にひどく実行されることになります。
注:nitty-gritty ddl、sql、tablesなどが見つかります ここではStackoverflow 。しかし、その情報は興味深いものですが、ここでは重要ではありません。これは無関係な質問です。そして、この質問はDDLを必要としません。
カバリングインデックスシークの使用を強制する場合、そのインデックスの使用は推定します。サブツリーのコストは0.04。
したがって、サーバーが次のような計画を使用することを選択することは、驚くに値しません。
サーバーA:
_| Plan | Cost | I/O Cost | CPU Cost |
|--------------------------------------------|-----------|-------------|-----------|
| clustered index scan (optimizer preferred) | 0.0106035 | 116.574 | 5.01949 | Actually run extraordinarily terrible (147k logical reads, 27 seconds)
| covering index seek (force hint) | 0.048894 | 0.0305324 | 0.0183616 | actually runs very fast (16 logical reads, instant)
_
これは、WITH FULLSCANと同じように最新の統計です。
別のサーバーで試してみます。本番データベースの最新のコピーと、最新の統計情報(WITH FULLSCAN)を使用して、同じクエリの見積もりを取得します。
サーバーB:
_| Plan | Cost | I/O Cost | CPU Cost |
|-------------------------------------------|-------------|------------|-----------|
| Clustered index scan (force hint) | 115.661 | 110.889 | 4.77115 | Runs extraordinarily terrible as server A (147k logical reads, 27 seconds)
| Covering index seek (optimizer preferred) | 0.0032831 | 0.003125 | 0.0001581 | Runs fast (16 logical reads, near instant)
_
私が理解できないのは、これらの2つのサーバーがデータベースのコピーがほとんど同じで、どちらも最新の統計情報があり、どちらもSQL Server 2014である理由です。
統計が古くなっている古典的なケースのように思えます。または、キャッシュされた実行プラン、またはパラメータスニッフィング。ただし、これらのテストクエリはどちらもOPTION(RECOMPILE)
を使用して発行されます。例:
_SELECT MIN(RowNumber) FROM Transactions
WITH (index=[IX_Transactions_TransactionDate]) WHERE TransactionDate >= '20191002 04:00:00.000' OPTION(RECOMPILE)
_
クラスター化インデックススキャンは悪いことです。そして、サーバーの1つはそれを知っています。これは非常に負荷の高い操作であり、スキャン操作がそれを教えてくれるはずです。
クラスタ化インデックススキャンをforceし、両方のサーバーからの推定スキャン操作を確認すると、何かが飛び出してきます。
_| Cost | Server A | Server B |
|---------------------|-------------|------------|
| I/O Cost | 116.573 | 110.889 |
| CPU Cost | 5.01945 | 4.77155 |
| Total Operator Cost | 0.0106035 | 115.661 |
mistakenly | avoids it
uses it |
_
Operatorサーバーのコスト[〜#〜] a [〜#〜]は低すぎます。
それが、悪い実行計画を誤って選択している理由です。単にoperatorコストがかかるだけです。サーバーBはそれを正しく把握し、クラスター化インデックススキャンを回避します。
ほぼすべての実行プランノードにカーソルを合わせると、dba、stackoverflow、およびすべてのブログの実行プランのすべてのスクリーンショットに、必ず表示されます。
_operatorCost >= max(cpuCost, ioCost)
_
そして実際には通常です:
_operatorCost = cpuCost + ioCost
_
ここで何が起こっているのでしょうか?
115 + 5のコストはほとんどゼロであるとサーバーが判断し、代わりにそのコストの1/10000の何かを決定するサーバーの説明は何ですか?
SQL Serverには、CPUおよびI/O操作に適用される内部の重みを調整するオプションがあることを知っています。
_DBCC TRACEON (3604); -- Show DBCC output
DBCC SETCPUWEIGHT(1E0); -- Default CPU weight
DBCC SETIOWEIGHT(0.6E0); -- I/O multiplier = 0.6
DBCC SHOWWEIGHTS; -- Show the settings
_
また、operatorコストがCPU + I/Oコストを下回ることがあります。
しかし、誰もそれらで遊んでいません。 SQL Serverが環境に基づいて、またはディスクサブシステムとの何らかの通信に基づいて、自動ウェイト調整を行うことは可能ですか?
サーバーが仮想SCSIディスクを使用し、ファイバーリンクでストレージエリアネットワーク(SAN)に接続された仮想マシンである場合、CPUとI/Oのコストを無視できると判断する可能性がありますか?
ただし、このサーバーで永続的な環境になることはありません。私が見つけたすべてのotherクエリは適切に動作するためです。
_ I/O: 0.0112613
CPU: +0.0001
=0.0113613 (theoretical)
Operator: 0.0113613 (actual)
_
サーバーが使用していないことを説明できるもの:
_I/O Cost + Cpu Cost = Operator Cost
_
この1つのインスタンスで正しく?
SQL Server 2014 SP2。
オペレーターのコストは、少なくともそれを構成するI/OまたはCPUのコストと同じ大きさであるべきではありませんか
場合によります。
私が同様のアイデアを思いついたので他の人が彼らの投稿を削除したのは残念です。
これはスクリーンショットに基づいて経験していることではありませんが、これはオペレーターコストの計算の要素です。 I/OとCPUのコストはスケーリングされません。行の目標が有効でない場合は、実行ごとのコストが表示されます。オペレーターのコストは、行の目標を示すためにスケーリングされます。これは、I/OとCPUが正確にオペレーターコストを構成していない場合の1つの例であり、推定実行回数は考慮に入れるべきものです。これらの統計をどのように表示するかは、内部入力と外部入力のどちらを見ているかによって異なります。
ソース: オプティマイザ内:ポールホワイトによる行の目標の詳細-2010年8月18日 ( アーカイブ )
これはあなたに影響を与えている要因である可能性があります。
オペレーションの全コストは、実行数にCPUコストを掛けたものに、さらにIOが必要な数の数式が必要になります。IO =は、IOがすでに多くのページにアクセスされた後、すでにメモリ内にある確率を表します。大きなテーブルの場合、以前にアクセスされたページがすでに削除されている可能性をモデル化します。サブツリーコストは、現在の操作と現在の操作にフィードされるすべての操作のコストを表します。
出典: ジョーチャンによる実行計画のコストモデル-2009年7月( アーカイブ )
あなたの問題に
スクリーンショットを見ると、サーバー上で非常に興味深いサブツリーコストが発生していることがわかります。興味深いのは、使用するメモリが多く、CPUが少ないことです。
上記の情報は、サブツリーコストに問題があり、オペレーターコストが症状であることを示しています。
...推定サブツリーコストは、個々のオペレーターの累積(NodeID順で合計された)コストです。
出典: Frantcheyによる実際の実行計画のコスト-2018年8月20日( アーカイブ )
答えはこれらの文にあると思います:
IOの数式は、IOが、すでに多くのページにアクセスした後に、メモリ内に存在する可能性を表しています。大きなテーブルの場合、以前にアクセスされたページが再び必要になったときに、すでに削除されている可能性があります。
それ以外の場合は、どのように見えるかを深く掘り下げるための推定および実際のクエリプランを確認してください。
重要、これは傷つきます(解雇される可能性があります)何が起こるかを理解せずに、これを計画せずに本稼働でこれを実行した場合。これは、再コンパイルで再度テストするためにキャッシュをクリアする方法です
Bhavesh PatelによるSQL Serverキャッシュのフラッシュまたはクリアの異なる方法-2017年3月31日( archive )
DBCC FREESYSTEMCACHE
DBCC FREESESSIONCACHE
DBCC FREEPROCCACHE
クエリで行の目標が設定されると、行の見積もりとコストに影響する可能性があります。
これが問題の原因であるかどうかを確認するには、トレースフラグ4138を有効にしてクエリを実行します(これにより、行の目標の影響が削除されます)。
使用可能なバッファープールが大きい場合、一部のI/O操作の推定コストを削減できます(コストが削減されたサーバーのRAMは14 GBですが、他のマシンでは6 GB)。
プランXMLで「EstimatedPagesCached」を探すことにより、この動作の影響を確認できます。このプロパティの値が大きいほど、同じデータにアクセスする可能性のある実行プランの部分のI/Oコストを削減できます。
並列クエリの場合、オペレーターのCPUコストは「スケジューラーの数/ 2」も削減できます。プランXMLで「EstimatedAvailableDegreeOfParallelism」を探すことで、これがどのような値であるかを確認できます。
「遅いクエリ」は4コアのサーバーで実行され、1コアのサーバーでは高速のクエリが実行されることに気付いたので、これについて言及します。
フォレストは、彼のブログでコストが意味をなさなくなる可能性があるさまざまな方法について語っています: Percentage Non Grata
サーバーが完全に同一であると想定できますか?
Sql2012サーバーのdb互換性レベルを修正した後、SP実行プランに対して返されたクエリステップコストの小さな変更に気づきました。(アイドルDB、最初のプランxmlを取得、適用オプションの変更、再コンパイルされたsp 、2番目のプランxmlを取得しました)プラン自体は同じように見えます。オプティマイザ内で使用できるオプションが増え、計算が少し異なる可能性があります。2xサーバー間で異なるパッチ/互換性がある場合、実際のプランが根本的に異なる可能性があります(間違っています) ..)
私にとって、サーバーAがクラスター化インデックススキャンを選択するのは、まったく正常なことのようです。これは、オプティマイザが持っている知識を考慮すれば最良の決定です。奇妙なことに、サーバーBは同じものを選択しません。その答えがあると思いますが、まずオプティマイザがクラスタ化インデックススキャンを選択する必要がある理由を説明します。
基本的な理由は、RowNumberとTransactionDateの値が独立していると考えていることに関係しています。それが言うように ここ :
独立性:相関情報が利用可能でない限り、異なる列のデータ分布は独立しています。
そしてクエリは
SELECT MIN(RowNumber) FROM Transactions WHERE TransactionDate >= '20191002 04:00:00.000'
オプションは次のとおりです。1)RowNumberでソートされたクラスター化インデックスのスキャンを開始し、トランザクションの実際の回答となるTransactionDate> = '20191002 04:00:00.000'の最初のタプルが検出されるとすぐに停止します。 2)値 '20191002 04:00:00.000'のTransactionDateの非クラスター化インデックスを検索し、その値以降の残りのインデックスをスキャンし、検出される最小のRowNumberを維持します。
ここでは、値「20191002 04:00:00.000」が列TransactionDateの最大値であると想定しています。実際には、値の95%より大きいと仮定しましょう。オプション1の独立性の仮定では、スキャンされた各タプルは最終的な回答になる確率が5%であるため、単一のディスクフェッチで回答が見つかると想定するのが妥当です。オプション2では、特定の日付のインデックスを検索すると、すでにディスクページのフェッチが多くなっているため、インデックスの5%もスキャンする必要があります。ただし、実際には、2つの列の値は直接相関しているため、オプティマイザが最良のオプションと見なすものは、クラスター化インデックスの95%をスキャンすることになります。
では、なぜサーバーBはクラスター化インデックスのスキャンを選択しないのでしょうか。明らかに、サーバーBでは、元の質問に投稿された計画からわかるように、クラスター化インデックスはRowNumberでソートされていません。
それで、なぜCPU_cost + I/O_cost >>コストなのか。クラスタ化インデックススキャン用のSQLサーバーは、部分的なスキャンのみであり、レポートのみであっても、テーブル全体のCPUとI/Oのコストを報告するようですようです総コストとして期待値を見つける速度に基づく実際の見積もり。投稿されたプランでまったく同じ動作を見ることができます ここ
そして、何ができるかについては、RowNumberとTransactionDateが常に増加している場合、クエリは次のように書き直すことができます。
SELECT RowNumber FROM Transactions WHERE TransactionDate> = '20191002 04:00:00.000' odrer by TransactionDate LIMIT 1