アプリケーションのスローダウンを診断しようとしています。このために、SQL Server 拡張イベントを記録しました。
- この質問では、1つの特定のストアドプロシージャを見ています。
- しかし、リンゴからリンゴへの調査として同様に使用できる12個のストアドプロシージャのコアセットがあります。
- ストアドプロシージャの1つを手動で実行すると、常に高速に実行されます
- また、ユーザーが再試行した場合、高速で実行されます。
ストアドプロシージャの実行時間は大きく異なります。このストアドプロシージャの実行の多くは1秒未満で返されます。
そして、その「高速」バケットについては、1秒よりはるかに少ないです。実際には約90ミリ秒です。
しかし、2秒、3秒、4秒待たなければならない長いユーザーがいます。12、13、14秒待たなければならない人もいます。それから、22、23、24秒待たなければならない本当に貧しい魂がいます。
そして、30秒後、クライアントアプリケーションはあきらめ、クエリを中止し、ユーザーは30秒待つ必要がありました。
原因を見つけるための相関
だから私は相関させようとしました:
- 期間と論理読み取り
- 継続時間と物理読み取り
- 期間とCPU時間
また、相関関係を与えるものはありません。原因はないようです
継続時間と論理読み取り:少しでも多くの論理読み取りでも、継続時間は大きく変動します:
期間対物理読み取り:クエリがキャッシュから提供されず、多くの物理読み取りが必要な場合でも、期間には影響しません。
duration vs cpu time:クエリが0秒のCPU時間を使用した場合でも、2.5秒のCPU時間を使用した場合でも、期間の変動は同じです。
ボーナス:Duration v Physical ReadsとDuration v CPU timeが非常に似ていることに気付きました。これは、CPU時間を物理読み取りと相関させようとすると証明されます。
多くのCPU使用率がI / Oに由来することが判明しました。誰かわかったね!
それで、実行時間の違いを説明できるクエリを実行する行為について何もなければ、それはそれがCPUまたはハードドライブとは無関係な何かであることを意味しますか?
CPUまたはハードドライブがボトルネックだった場合。それがボトルネックではないでしょうか?
ボトルネックはCPUであると仮定した場合、このサーバーのCPUの電力が不足していること:
- その後、より多くのCPU時間を使用した実行に時間がかかりませんか?
- 過負荷のCPUを使用して他の人と完了する必要がありますか?
ハードドライブについても同様です。ハードドライブがボトルネックであると仮定した場合、ハードドライブがこのサーバーに十分なランダムスループットを持たないこと:
- その後、より多くの物理読み取りを使用した実行には時間がかかりませんか?
- 過負荷のハードドライブI / Oを使用して他の人と完了する必要があるためですか?
ストアドプロシージャ自体は、書き込みも実行も要求もしません。
- 通常、0行(90%)を返します。
- 時折、1行(7%)を返します。
- まれに2行(1.4%)が返されます。
- そして最悪の場合、2行以上を返しました(一度に12行を返しました)
したがって、異常な量のデータを返しているわけではありません。
サーバーのCPU使用率
サーバーのプロセッサ使用率は平均で約1.8%で、ときどき最大18%のスパイクがあります。したがって、CPU負荷が問題であるとは思えません。
そのため、サーバーのCPUが過負荷になっていないようです。
しかし、サーバーは仮想です...
宇宙の外に何か?
私が想像できる唯一のことは、サーバーの宇宙の外に存在するものです。
- 論理読み取りでない場合
- 物理的な読み取りではありません
- CPUの使用ではありません
- そして、それはCPU負荷ではありません
また、ストアドプロシージャのパラメーターとは異なります(同じクエリを手動で発行し、27秒はかからないため、0秒ほどかかります)。
サーバーが同じコンパイル済みストアドプロシージャを実行するのに、0秒ではなく30秒かかることもあると考えられます。
- チェックポイント?
仮想サーバーです
- ホストが過負荷ですか?
- 同じホスト上の別のVM?
サーバーの拡張イベントを通過します。クエリが突然20秒かかる場合、特に何も起こりません。正常に実行された後、正常に実行しないことを決定します。
- 2秒
- 1秒
- 30秒
- 3秒
- 2秒
そして、私が見つけることができる他の特に精力的なアイテムはありません。2時間ごとのトランザクションログバックアップではありません。
他に何がありますか?
「サーバー」以外に言えることはありますか?
編集:時刻で関連付ける
期間をすべてに関連付けていることに気付きました:
- 論理読み取り
- 物理的な読み取り
- CPU使用率
しかし、私がそれを相関させなかった一つのことは、時間帯でした。おそらく、2時間ごとのトランザクションログバックアップは問題です。
または、チェックポイント中にチャックでスローダウンが発生する可能性がありますか?
いいえ:
Intel Xeon Goldクアッドコア6142。
編集-人々はクエリ実行計画を仮定しています
人々は、クエリの実行プランが「高速」と「低速」で異なる必要があると仮定しています。ではない。
そして、これを検査からすぐに見ることができます。
質問の期間が長いのは、実行計画が「悪い」ためではないことがわかります。
- より論理的な読み取りを行ったもの
- より多くの結合とキー検索からより多くのCPUを消費したもの
読み取りの増加、またはCPUの増加がクエリの継続時間の増加の原因である場合は、上記で既に見たでしょう。相関関係はありません。
ただし、CPU読み取り領域の製品メトリックと期間を相関させてみましょう。
相関関係はさらに少なくなります-これは逆説です。
編集:散布図を更新して、多数の値を持つExcel散布図のバグを回避しました。
次のステップ
次のステップは、ブロックされたクエリのイベントをサーバーで生成するように誰かに依頼することです-5秒後:
EXEC sp_configure 'blocked process threshold', '5';
RECONFIGURE
クエリが4秒間ブロックされるかどうかは説明されません。ただし、クエリを5秒間ブロックしているものは、おそらく4秒間もブロックします。
スロープラン
実行される2つのストアドプロシージャのスロープランは次のとおりです。
- `EXECUTE FindFrob @CustomerID = 7383、@ StartDate = '20190725 04:00:00.000'、@ EndDate = '20190726 04:00:00.000'
- `EXECUTE FindFrob @CustomerID = 7383、@ StartDate = '20190725 04:00:00.000'、@ EndDate = '20190726 04:00:00.000'
同じストアドプロシージャを同じパラメータで、連続して実行します。
| Duration (us) | CPU time (us) | Logical reads | Physical reads |
|---------------|---------------|---------------|----------------|
| 13,984,446 | 47,000 | 5,110 | 771 |
| 4,603,566 | 47,000 | 5,126 | 740 |
コール1:
|--Nested Loops(Left Semi Join, OUTER REFERENCES:([Contoso2].[dbo].[Frobs].[FrobGUID]) OPTIMIZED)
|--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]))
| |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[RowNumber]) OPTIMIZED)
| | |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
| | | |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
| | | | |--Index Seek(OBJECT:([Contoso2].[dbo].[TransactionPatronInfo].[IX_TransactionPatronInfo_CustomerID_TransactionGUID] AS [tpi]), SEEK:([tpi].[CustomerID]=[@CustomerID]) ORDERED FORWARD)
| | | | |--Index Seek(OBJECT:([Contoso2].[dbo].[Transactions].[IX_Transactions_TransactionGUIDTransactionDate]), SEEK:([Contoso2].[dbo].[Transactions].[TransactionGUID]=[Contoso2].[dbo
| | | |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions2_MoneyAppearsOncePerTransaction]), SEEK:([Contoso2].[dbo].[FrobTransactions].[TransactionGUID]=[Contos
| | |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions_RowNumber]), SEEK:([Contoso2].[dbo].[FrobTransactions].[RowNumber]=[Contoso2].[dbo].[Fin
| |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[Frobs].[PK_Frobs_FrobGUID]), SEEK:([Contoso2].[dbo].[Frobs].[FrobGUID]=[Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]), WHERE:([Contos
|--Filter(WHERE:([Expr1009]>(1)))
|--Compute Scalar(DEFINE:([Expr1009]=CONVERT_IMPLICIT(int,[Expr1012],0)))
|--Stream Aggregate(DEFINE:([Expr1012]=Count(*)))
|--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactins_OnFrobGUID]), SEEK:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]=[Contoso2].[dbo].[Frobs].[LC
呼び出し2
|--Nested Loops(Left Semi Join, OUTER REFERENCES:([Contoso2].[dbo].[Frobs].[FrobGUID]) OPTIMIZED)
|--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]))
| |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[RowNumber]) OPTIMIZED)
| | |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
| | | |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
| | | | |--Index Seek(OBJECT:([Contoso2].[dbo].[TransactionPatronInfo].[IX_TransactionPatronInfo_CustomerID_TransactionGUID] AS [tpi]), SEEK:([tpi].[CustomerID]=[@CustomerID]) ORDERED FORWARD)
| | | | |--Index Seek(OBJECT:([Contoso2].[dbo].[Transactions].[IX_Transactions_TransactionGUIDTransactionDate]), SEEK:([Contoso2].[dbo].[Transactions].[TransactionGUID]=[Contoso2].[dbo
| | | |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions2_MoneyAppearsOncePerTransaction]), SEEK:([Contoso2].[dbo].[FrobTransactions].[TransactionGUID]=[Contos
| | |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions_RowNumber]), SEEK:([Contoso2].[dbo].[FrobTransactions].[RowNumber]=[Contoso2].[dbo].[Fin
| |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[Frobs].[PK_Frobs_FrobGUID]), SEEK:([Contoso2].[dbo].[Frobs].[FrobGUID]=[Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]), WHERE:([Contos
|--Filter(WHERE:([Expr1009]>(1)))
|--Compute Scalar(DEFINE:([Expr1009]=CONVERT_IMPLICIT(int,[Expr1012],0)))
|--Stream Aggregate(DEFINE:([Expr1012]=Count(*)))
|--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactins_OnFrobGUID]), SEEK:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]=[Contoso2].[dbo].[Frobs].[LC
計画が同一であることは理にかなっています。同じストアドプロシージャを同じパラメータで実行しています。