2

現在、複数のテーブルを結合するクエリを実行しているときに、いくつかのパフォーマンスの問題が発生しています。メイン テーブルには 1 億 7000 万のレコードがあり、かなり大きいです。

私が遭遇したのは、上位 1000 句を使用してクエリを実行すると、結果が瞬時に表示されることです。ただし、それを 8000 まで増やすと、クエリは 15 分間簡単に実行されます (その後、強制終了します)。試行錯誤の結果、転換点は Top 7934 (魔法のように動作) と Top 7935 (永遠に続く) にあることがわかりました。

誰かがこの動作を認識し、私が間違っていることを理解していますか? たぶん、私のクエリはいくつかの点で間違っています。

前もって感謝します

SELECT  top 7934 h.DocIDBeg
    ,h.[Updated By]
    ,h.Action
    ,h.Type
    ,h.Details
    ,h.[Update Date]
    ,h.[Updated Field Name]
    ,i.Name AS 'Value Set To'
    ,COALESCE(i.Name,'') + COALESCE(h.NewValue, '') As 'Value Set To'
    ,h.OldValue
FROM
    (SELECT  g.DocIDBeg
            ,g.[Updated By]
            ,g.Action
            ,g.Type
            ,g.Details
            ,g.[Update Date]
            ,CAST(g.details as XML).value('auditElement[1]/field[1]/@name','nvarchar(max)') as 'Updated Field Name'
            ,CAST(g.details as XML).value('(/auditElement//field/setChoice/node())[1]','nvarchar(max)') as 'value'
            ,CAST(g.details as XML).value('(/auditElement//field/newValue/node())[1]','nvarchar(max)') as 'NewValue'
            ,CAST(g.details as XML).value('(/auditElement//field/oldValue/node())[1]','nvarchar(max)') as 'OldValue'
    FROM(
            SELECT a.ArtifactID
                  ,f.DocIDBeg
                  ,b.FullName AS 'Updated By'
                  ,c.Action
                  ,e.ArtifactType AS 'Type'
                  ,a.Details
                  ,a.TimeStamp AS 'Update Date'
            FROM [EDDS1015272].[EDDSDBO].[AuditRecord] a
                        LEFT JOIN [EDDS1015272].[EDDSDBO].AuditUser b
                            ON a.UserID = b.UserID
                        LEFT JOIN [EDDS1015272].[EDDSDBO].AuditAction c
                            ON a.Action = c.AuditActionID
                        LEFT JOIN [EDDS1015272].[EDDSDBO].[Artifact] d
                            ON a.ArtifactID = d.ArtifactID
                        LEFT JOIN [EDDS1015272].[EDDSDBO].[ArtifactType] e
                            ON d.ArtifactTypeID = e.ArtifactTypeID
                        INNER JOIN [EDDS1015272].[EDDSDBO].[Document] f
                            ON a.ArtifactID = f.ArtifactID
            ) g
    ) h
LEFT JOIN [EDDS1015272].[EDDSDBO].[Code] i
ON h.value = i.ArtifactID
4

3 に答える 3

3

以前はデータ ウェアハウスを頻繁に使用していましたが、同様の問題に頻繁に遭遇しました。根本的な原因は明らかにメモリ使用量にあり、既にここで言及されています。1 億 7000 万のすべてのレコードをクエリする必要がある場合、クエリを書き直してもあまり役に立たないと思います。また、メモリ リソースが増えるのを待っても問題ないと思います。だからここに私からの簡単な回避策があります:

クエリを分割してみてください。たとえば、まず、AuditUser テーブルに結合された AuditRecord レコード テーブルから必要なすべてのデータをクエリし、結果を別の (一時テーブルなど) テーブルに格納します。次に、この新しいテーブルを Artifact テーブルなどに結合します。この場合、この手順では必要なメモリを 1 つずつ減らしてから、クエリ全体を実行してハングアウトさせます。したがって、長期的には、クエリではなく、コンソールにいくつかのステータスを出力できるため追跡が容易なスクリプトがあり、終了しないクエリとは異なり、彼の仕事を実行します。

また、このすべてのデータを一度にクエリする必要があることを確認してください。必要なユースケースは思い浮かびませんが、それでもアプリケーションの場合はページングを実装する必要があります。エクスポート機能の場合は、おそらくそこにある可能性がありますデータのバッチ処理に使用できるタイムラインです。たとえば、毎日データをエクスポートし、昨日のデータのみをクエリします。この場合、増分エクスポートを考え出します。

于 2012-12-19T18:01:53.213 に答える
2

「試行錯誤の結果、転換点は Top 7934 (魔法のように機能する) と Top 7935 (永遠に続く) にあることがわかりました」

これは流出のように聞こえます。Adam Mechanic は、下のビデオでこの内部構造の素晴らしいデモを行っています。基本的に、トップはメモリを必要とするソートを強制します。メモリ許可が操作を完了するのに十分な大きさでない場合、その一部はディスク上で実行されます。

https://www.youtube.com/watch?v=j5YGdIk3DXw

1:03:50 に移動して、Adam による流出のデモをご覧ください。彼のクエリでは、668,935 行はスピルしませんが、668,936 行はスピルし、クエリ時間は 2 倍以上になります。

時間があればセッション全体を見てください。パフォーマンスチューニングに最適!

@Remusが示唆したように、転換点になる可能性もありますが、実際の計画を知らずにすべて推測しています.

于 2012-12-19T15:51:19.857 に答える
0

フィルターが適用される前に、サブセレクトがサーバーにすべてをフェッチするように強制していると思います。これにより、より多くのメモリ使用量 (xlm フィールド) が発生し、まともな qry プランを使用することが難しくなります。

top の奇妙な動作について: top は qry プランの生成に大きな影響を与えます。7935 が 1 つの最適な計画のカットオフ ポイントであり、さらにフェッチする必要がある場合に SQL サーバーが別のパスを選択する可能性があります。または、メモリに戻って 7935 のメモリが不足する可能性があります

アップデート:

ネストされた選択を排除するためにqryを作り直しました。今ではそれほど高速になると言っているわけではありませんが、使用されていないいくつかのフィールドが排除され、qryプランに基づいて理解しやすく最適化されるはずです。現在、各テーブルの正確なサイズがわからず、qry を実行してテストすることはほとんどできないため、最良の回答を提供することは不可能です。しかし、私はいくつかのヒントを試すことができます:

1 つのステップは、すべての左の結合が必要かどうかを確認し、必要でない場合はそれらを内部に変換することです。例: AuditUser、AuditRecord は常にユーザーを持つことができますか?

あなたが試すことができるもう1つのことは、できれば小さなテーブルのデータをtmpテーブルに入れ、大きなテーブルをそのtmpテーブルに結合することです。結合する多くのレコードを排除する可能性があります

可能であれば、少し非正規化して、たとえば、ユーザー名を監査レコード 2 に入れて、AuditUser の結合を完全に排除することができます

しかし、それはあなたができる/許可されているワットとデータ/サーバーが必要なワット次第です

SELECT  top 7934 f.DocIDBeg
    ,b.FullName AS 'Updated By'
    ,c.Action
    ,e.ArtifactType AS 'Type'
    ,a.Details
    ,a.TimeStamp AS 'Update Date'
    ,CAST(a.Details as XML).value('auditElement[1]/field[1]/@name','nvarchar(max)') as 'Updated Field Name'
    ,i.Name AS 'Value Set To'
    ,COALESCE(i.Name,'') + COALESCE(CAST(a.Details as XML).value('(/auditElement//field/newValue/node())[1]','nvarchar(max)') as 'NewValue', '') As 'Value Set To'
    ,CAST(a.Details as XML).value('(/auditElement//field/oldValue/node())[1]','nvarchar(max)') as 'OldValue'
FROM [EDDS1015272].[EDDSDBO].[AuditRecord] a
                        LEFT JOIN [EDDS1015272].[EDDSDBO].AuditUser b
                            ON a.UserID = b.UserID
                        LEFT JOIN [EDDS1015272].[EDDSDBO].AuditAction c
                            ON a.Action = c.AuditActionID
                        LEFT JOIN [EDDS1015272].[EDDSDBO].[Artifact] d
                            ON a.ArtifactID = d.ArtifactID
                        LEFT JOIN [EDDS1015272].[EDDSDBO].[ArtifactType] e
                            ON d.ArtifactTypeID = e.ArtifactTypeID
                        INNER JOIN [EDDS1015272].[EDDSDBO].[Document] f
                            ON a.ArtifactID = f.ArtifactID
                        LEFT JOIN [EDDS1015272].[EDDSDBO].[Code] i
                            ON CAST(a.details as XML).value('(/auditElement//field/setChoice/node())[1]','nvarchar(max)') = i.ArtifactID
于 2012-12-19T15:55:14.873 に答える