web-dev-qa-db-ja.com

クエリの削除には時間がかかります

私は合理的な単純なクエリを取得しました:

With RowsToDelete AS
(
    SELECT TOP 500 Id
    FROM ErrorReports
    WHERE IncidentId = 611
)
DELETE FROM RowsToDelete 

ただし、完了しません。何度か試してみました。最後に8分待ってからキャンセルしました。

ErrorReportsには約22,000行が含まれています。 ErrorReportOriginsほぼ同じ。

推定実行計画:

enter image description here

実際の実行計画(top 10の場合、完了するまでに28秒かかります):

enter image description here

実行計画: https://www.brentozar.com/pastetheplan/?id=S1jUXDruz

クライアント統計:

enter image description here

私が試したこと:

  • ErrorReportOriginsにはクラスター化インデックス(id)がなく、ErrorReports.IdへのFKしかありませんでした。 id列(pk&identity)を追加しました。
  • すべてのインデックスを再構築しました( this を使用)。
  • 最初にErrorReportOriginsから削除しようとしました(同じCTEを使用)。変わりはない
  • (元のCTEにはORDER BY Idがありましたが、差があるかどうかを確認するために削除しました)

道に迷いました。一体どうしてこんなに時間がかかるの?すべてのSELECTステートメントは高速です。そして、DBはそれほど大きくありません。 ErrorReportsテーブルは最大のものです。

(これはエラスティックプール内のSQL Azure DBです)

更新

CREATE TABLE [dbo].[ErrorReports](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [IncidentId] [int] NOT NULL,
    [ErrorId] [varchar](36) NOT NULL,
    [ApplicationId] [int] NOT NULL,
    [ReportHashCode] [varchar](20) NOT NULL,
    [CreatedAtUtc] [datetime] NOT NULL,
    [SolvedAtUtc] [datetime] NULL,
    [Title] [nvarchar](100) NULL,
    [RemoteAddress] [varchar](45) NULL,
    [Exception] [ntext] NOT NULL,
    [ContextInfo] [ntext] NOT NULL,
PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)

インデックス:

CREATE NONCLUSTERED INDEX [Application_GetWeeklyStats] ON [dbo].[ErrorReports]
(
    [ApplicationId] ASC,
    [CreatedAtUtc] DESC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

CREATE NONCLUSTERED INDEX [ErrorReports_IncidentId] ON [dbo].[ErrorReports]
(
    [IncidentId] ASC,
    [CreatedAtUtc] DESC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

データはかなり大きい(クエリでdatalengthを使用):

enter image description here

2
jgauffin

非常に大きなntextデータは非常に断片化されている可能性が高いため、削除するLOBフラグメントを検索するときに大量のランダムI/O(またはその他の非効率)が発生します。たぶんエラスティックなものには、より多くのI/O馬力も必要です。

この問題を解決するには、データをエクスポートして再ロードする必要がある場合があります。新しいテーブルにコピーし、古いテーブルを削除してから、新しい名前を変更することもできます。

特別な理由がない限り、データの再読み込み中に、データ型を古い非推奨のntextから代わりのnvarchar(max)に変更することをお勧めします。

また、LOBが大きい場合のSQL Serverの制限である可能性もあります。関連するQ&A SQL ServerでのLOBデータのスロー削除 を参照してください。

通常の推奨事項は、データの平均が1MB以上の場合に代替ストレージソリューションに移行することです。詳細については、Paul S. RandalによるSQL Serverテクニカルペーパー SQL Server 2008のFILESTREAMストレージ を参照してください。これは まだサポートされていません 残念ながらAzure SQLデータベースではです。

代わりに、大きなLOBデータをAzure Blobストレージに格納し、データベース自体にリンクのみを保持する方がよいでしょう。

10
Paul White 9

それが違いをもたらすかどうかはわかりませんが、この構文はうまくいくはずです:

DELETE TOP (500)
FROM ErrorReports
WHERE IncidentId = 611

シンプルなテーブルでテストし、単一のインデックススキャンで非常にシンプルなクエリプランを取得しました。

0
paparazzo

IMO、_[Exception] ,[ContextInfo]_のサイズを再検討する必要があります。

たとえば、len(Exception)> 1000のテーブルに行がありますか

varchar(1500)で十分だと思います。指定したクエリが改善されます。

サイズは変更できない場合があります。

あるいは、RBARに行くこともできます

_DECLARE @IncidentId INT = 611
DECLARE @TopSize INT = 10 --try 15,20 etc
DECLARE @BatchSize INT = 10 --try 15,20 etc
DECLARE @MaxLimit INT = 500
DECLARE @RowCount INT = 0

BEGIN TRY
    WHILE (@TopSize <= @MaxLimit)
    BEGIN
        DELETE TOP (@TopSize)
        FROM ErrorReports
        WHERE IncidentId = @IncidentId

        SET @RowCount = @@RowCount

        --PRINT @TopSize
        IF (
                @RowCount = 0
                OR @RowCount IS NULL
                )
            BREAK;
        ELSE
            SET @TopSize = @TopSize + @BatchSize
    END
END TRY

BEGIN CATCH
    --catch error
END CATCH
_
0
KumarHarsh