web-dev-qa-db-ja.com

SQL Serverが特定の結合で無期限にハングする

データモデルからファクトテーブルを構築するストアドプロシージャがあります。私は通常、説明されている手法を使用しています ここ は、パフォーマンスが大幅に向上することがわかりました。

しかし、SQL Sever 2016(SP1、CU1、Developer Edition)がハングする状況に陥っています。そのような順列の1つは、5日間と23時間ハングしました(私は休暇に行って、何が起こるかを確認するために実行させました)。これの通常のパフォーマンスSPは約1分です。SPのこの特定のセクションは、約40秒で最も遅い部分です。

結合を1つずつ減らすことで、この特定の問題の原因とエンジニアを見つけることができましたが、今はテストを拡張していて、問題が再発しています(10分後に強制終了しましたが、何もわかりません) _sp_whoisactive_は、ロックも待機もないことを示しており、CPU時間は単純に刻々と増加しています。

結合は次のようになります。

_SELECT
--Most everything here is commented out for my testing, it doesn't make a difference.
INTO #GL
    FROM #GLT GL 
    INNER JOIN TransactionDimension TD ON TD.TransactionId = GL.TransactionId AND TD.ClientId = GL.ClientId
    INNER JOIN TransactionLineDimension TLD ON TLD.TransactionLineId = GL.TransactionLineId AND TLD.ClientId = GL.ClientId
    INNER JOIN SourceChartOfAccountsDimension SCOAD ON SCOAD.AccountFullName = GL.QBAccountFullName AND SCOAD.ImportFileId = GL.ImportFileId AND SCOAD.ClientId = GL.ClientId
    INNER JOIN GeneralLedgerDetailSubstitutionDimension GLDSD ON GLDSD.TransactionLineId = GL.TransactionLineId AND GLDSD.ClientId = GL.ClientId
    INNER JOIN HoldingCompanyDimension HCD ON HCD.HoldingCompanyId = GL.HoldingCompanyId AND HCD.ClientId = GL.ClientId
    INNER JOIN ClassNameDimension NoClass ON NoClass.JoinClassName = N'(no class)' + nchar(8203) AND NoClass.ClientId = GL.ClientId
    INNER JOIN ChartOfAccountsMappingDimension COAMD ON 
        COAMD.JoinAccountFullName = GL.JoinAccountFullName 
        AND COAMD.MappedAccountFullName = GL.MappedAccountFullName 
        AND COAMD.ClientId = GL.ClientId
    INNER JOIN ChartOfAccountsCategoryDimension COACD ON COACD.ChartOfAccountsCategoryId = COAMD.ChartOfAccountsCategoryId AND COACD.ClientId = GL.ClientId
    INNER JOIN ImportFileDimension IFD ON IFD.ImportFileId = GL.ImportFileId AND IFD.ClientId = GL.ClientId
    INNER JOIN SplitDimension SD ON SD.AccountFullName = GL.Split AND SD.ClientId = GL.ClientId
    INNER JOIN ConstructionProjectDimension CpCoa ON CpCoa.ConstructionProjectId = COAMD.ConstructionProjectId AND CpCoa.ClientId = COAMD.ClientId
    INNER JOIN ConstructionProjectDimension CpClass ON CpClass.ConstructionProjectId = GL.ClassConstructionProjectId AND CpClass.ClientId = GL.ClientId
    INNER JOIN InvestorDimension InvestorNameD ON InvestorNameD.InvestorId = GL.NameInvestorId AND InvestorNameD.ClientId = GL.ClientId
    INNER JOIN InvestorDimension InvestorCoaD ON InvestorCoaD.InvestorId = COAMD.InvestorId AND InvestorCoaD.ClientId = COAMD.ClientId
    INNER JOIN InvestorDimension InvestorClassD ON InvestorClassD.InvestorId = GL.ClassInvestorId AND InvestorClassD.ClientId = GL.ClientId
    INNER JOIN InvestorDimension InvestorNullD ON InvestorNullD.InvestorId = 0 AND InvestorNullD.ClientId = GL.ClientId
    INNER JOIN HoldingCompanyDimension TransferNameHCD ON TransferNameHCD.HoldingCompanyId = GL.NameHoldingCompanyId AND TransferNameHCD.ClientId = GL.ClientId
    INNER JOIN HoldingCompanyDimension TransferCoaHCD ON TransferCoaHCD.HoldingCompanyId = COAMD.TransferToHoldingCompanyId AND TransferCoaHCD.ClientId = COAMD.ClientId
    INNER JOIN HoldingCompanyDimension TransferClassHCD ON TransferClassHCD.HoldingCompanyId = GL.ClassHoldingCompanyId AND TransferClassHCD.ClientId = GL.ClientId
    INNER JOIN HoldingCompanyDimension TransferNullHCD ON TransferNullHCD.HoldingCompanyId = 0 AND TransferNullHCD.ClientId = GL.ClientId
    INNER JOIN InvestmentDimension InvestmentNameD ON InvestmentNameD.InvestmentId = GL.NameInvestmentId AND InvestmentNameD.ClientId = GL.ClientId
    --INNER JOIN InvestmentDimension InvestmentCoaD ON InvestmentCoaD.InvestmentId = COAMD.InvestmentId AND InvestmentCoaD.ClientId = COAMD.ClientId
    --INNER JOIN InvestmentDimension InvestmentClassD ON InvestmentClassD.InvestmentId = GL.ClassInvestmentId AND InvestmentClassD.ClientId = GL.ClientId
    --INNER JOIN InvestmentDimension InvestmentFileD ON InvestmentFileD.InvestmentId = IFD.InvestmentId AND InvestmentFileD.ClientId = IFD.ClientId
    --INNER JOIN InvestmentDimension InvestmentHcD ON InvestmentHcD.InvestmentId = HCD.InvestmentId AND InvestmentHcD.ClientId = HCD.ClientId
    --This caused SQL Server to break(!) and literally run forever (as in 5 days 23 hours before being killed).
    --INNER JOIN InvestmentDimension InvestmentNullD ON InvestmentNullD.InvestmentId = 0 AND InvestmentNullD.ClientId = GL.ClientId
    --INNER JOIN PropertyDimension PD ON PD.PropertyId = GL.PropertyId AND PD.ClientId = GL.ClientId
    --INNER JOIN PropertyDimension NullPD ON NullPD.PropertyId = 0 AND NullPD.ClientId = GL.ClientId
_

この特定のケースでは、コメントアウトされていないリストの最後の結合が原因です。元の問題のある結合も残しました。はい、意図的に異なるパラメーターを使用して同じテーブルに何度も参加しています。

問題が最初の問題だったとき、私はそれを回避できると考えました。しかし、今、この問題の2番目の出現は、手順全体が信頼できないことを示唆しています。現在問題が発生している場合、#GLTに記録がありません。ゼロ。 InvestmentDimensionテーブルには2つあります。テスト中の元の1つは#GLTに約200,000あります。

ランニング

_DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS
_

テストを実行する前は何も変更しません(これは良い解決策ではありませんが、おそらくいくつかの考えられる原因を排除します)。

問題の詳細と回避策はどこで確認できますか?

さらなる洞察:

これを1つの挿入と3つの更新に分割するように書き直しました。これには、選択フィールドリストのコードの複雑さを軽減するという利点があったため、パフォーマンスには影響しますが、今のところひどい妥協ではありません。まだ調査中です。しかし、それを行って実験を続けた後、突然_#GTL_の人口が永遠に垂れ始めました。

その結合リストのようなものはありませんが、共通の1つのタイプの結合があり、それを削除することで問題はすぐに解決しました。それはこの行でした:

_ INNER JOIN InvestorDimension InvestorNullD ON InvestorNullD.InvestorId = 0 AND InvestorNullD.ClientId = GL.ClientId
_

基本的には、セカンダリ(一意でない)IDがハードコードされた番号であり、その他の基準が一時テーブルからのものであるレコードにリンクしています。この行は通常は機能しますが、結合リストが十分に大きくなると、コンパイラーが存在する場合にコンパイラーが死のスパイラルに陥るようです。 _#GLT_で失敗し始めた理由は、このテーブルにInvestorIdが0の2つのレコードがあり、各ClientIdに1つあるためです。以前の実験は、それほど遠くまで来ていませんでした。

これをより直接的に理解する方法(結合を1つずつ取り出すハントやペックではなく)への洞察は、依然として高く評価されています。

OPTION (RECOMPILE)を追加しても効果がありませんでした。トレースフラグとデータベーススコープ設定は基本的にデフォルトです。 _optimize for ad hoc workloads_をオンにしてみましたが、何も変わりませんでした。アクティブなトレースフラグはありません。

4
Yishai

これをマイクロソフトサポートに報告しました。彼らはそれが New Cardinality Estimation の問題であり、これを古い値に設定すると遅延がなくなると結論付けました。

回避策に関しては、OPTION(QUERYTRACEON 9476)またはASSUME_JOIN_PREDICATE_DEPENDS_ON_FILTERSヒントを使用することをお勧めします(詳細は、SQL Server 2016の pdateでUSE HINTクエリヒント引数を導入 を参照してください)。別の方法としては、古いカーディナリティエスティメータに完全に戻すことです。

更新:Microsoft SQL Server 2016(SP1-CU2)(KB4013106)-13.0.4422.0(X64)で問題が解決するようです

3
Yishai

SQL Server 2016を使用しているとのことですが、これは ライブクエリプラン を使用できることを意味します。

SSMSで、[クエリ]、[ライブクエリ統計を含める]の順にクリックして、クエリを実行します。クエリのどの部分が機能しているかを正確に確認できます。

クエリはコンパイルのみであり、実際には実行されていない可能性があることに注意してください。本当に醜いクエリで-そしてあなたが投稿したクエリが適格かもしれない-あなたは 何時間もかかるコンパイル を見ることができます。

4
Brent Ozar