web-dev-qa-db-ja.com

ROLLBACKは高速な操作ですか?

RDBMSシステムがCOMMIT操作用に最適化されているのは本当ですか? ROLLBACK操作はどのくらい遅く/速くなり、なぜですか?

20
garik

SQL Serverの場合、コミット操作はLOP_COMMIT_XACTをログファイルに書き込み、ロックを解放するだけであると主張できます。もちろん、BEGIN TRAN以降、トランザクションが実行するすべてのアクションのROLLBACKよりも高速です。

コミットだけでなくトランザクションのすべてのアクションを検討している場合でも、ステートメントが真実ではないと主張します。たとえば、外部要因、データディスクの速度と比較したログディスクの速度を除外すると、トランザクションによって行われた作業のロールバックは、最初の作業よりも速くなる可能性があります。

ロールバックとは、変更の順次ファイルを読み取り、それらをメモリ内データページに適用することです。元の「作業」では、実行計画の生成、ページの取得、行の結合などを行う必要がありました。

編集:それは少し異なります...

@JackDouglasは この記事 を指摘しました。これは、ロールバックが元の操作よりも大幅に長くかかる状況の1つを説明しています。ロールバックはほとんどがシングルスレッドであるため、ロールバックに48時間以上かかる並列処理を使用する14時間のトランザクションの例です。また、バッファプールを繰り返しチャーンしている可能性が高いため、メモリ内ページへの変更を元に戻す必要はありません。

だから、私の以前の答えの改訂版。ロールバックはどのくらい遅くなりますか?他のすべての考慮事項、通常のOLTPトランザクションの場合、そうではありません。通常の範囲外では、「元に戻す」までに「行う」よりも時間がかかる可能性がありますが、これは潜在的な舌のツイスターですか? ?) "do"がどのように行われたかに依存する理由。

Edit2:コメントでの議論に続いて、ここでは非常に考案された例が行われ、行われている作業が相対的な費用を決定する主要な要因であることを示しています操作としてのコミットとロールバックの比較。

2つのテーブルを作成し、それらを非効率的にパックします(ページごとの無駄なスペース):

SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;
SET NOCOUNT ON;
GO

CREATE TABLE dbo.Foo
(
    col1 INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , col2 CHAR(4000) NOT NULL DEFAULT REPLICATE('A', 4000)
)

CREATE TABLE dbo.Bar
(
    col1 INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , col2 CHAR(4000) NOT NULL DEFAULT REPLICATE('A', 4000)
)
GO

INSERT dbo.Foo DEFAULT VALUES
GO 100000

INSERT dbo.Bar DEFAULT VALUES
GO 100000

「悪い」更新クエリを実行し、作業にかかる時間とコミットの発行にかかる時間を測定します。

DECLARE 
    @StartTime DATETIME2
    , @Rows INT

SET @Rows = 1

CHECKPOINT
DBCC DROPCLEANBUFFERS

BEGIN TRANSACTION

SET @StartTime = SYSDATETIME()

UPDATE
    dbo.bar
SET
    col2 = REPLICATE('B', 4000)
FROM
    dbo.bar b
INNER JOIN
    (
    SELECT TOP(@Rows)
        col1
    FROM
        dbo.foo
    ORDER BY
        NEWID()
    ) f
ON  f.col1 = b.col1
OPTION (MAXDOP 1)

SELECT 'Find and update row', DATEDIFF(ms, @StartTime, SYSDATETIME())

SET @StartTime = SYSDATETIME()

COMMIT TRANSACTION

SELECT 'Commit', DATEDIFF(ms, @StartTime, SYSDATETIME())
GO

同じことを繰り返しますが、ロールバックを発行して測定します。

    DECLARE 
    @StartTime DATETIME2
    , @Rows INT

SET @Rows = 1

CHECKPOINT
DBCC DROPCLEANBUFFERS

BEGIN TRANSACTION

SET @StartTime = SYSDATETIME()

UPDATE
    dbo.bar
SET
    col2 = REPLICATE('B', 4000)
FROM
    dbo.bar b
INNER JOIN
    (
    SELECT TOP(@Rows)
        col1
    FROM
        dbo.foo
    ORDER BY
        NEWID()
    ) f
ON  f.col1 = b.col1
OPTION (MAXDOP 1)

SELECT 'Find and update row', DATEDIFF(ms, @StartTime, SYSDATETIME())

SET @StartTime = SYSDATETIME()

ROLLBACK TRANSACTION

SELECT 'Rollback', DATEDIFF(ms, @StartTime, SYSDATETIME())
GO

@ Rows = 1を使用すると、ある程度の一貫性が得られます。

  • 検索/更新に5500ms
  • 3msコミット
  • 1msロールバック

@ Rows = 100の場合:

  • 8500ms検索/更新
  • 15msコミット
  • 15msロールバック

@ Rows = 1000の場合:

  • 15000ms検索/更新
  • 10msコミット
  • 500msロールバック

元の質問に戻ります。作業とコミットにかかる時間を測定している場合、その作業の大部分は実際にデータを変更するのではなく、更新する行を見つけるのに費やされるため、ロールバックは勝者です。コミット操作を単独で見ている場合、コミットがそのように「機能」することはほとんどありません。コミットは「完了」です。

14

Oracleの場合、ロールバックは、ロールバックしている変更を行うのにかかった時間よりも何倍も長くかかることがあります。これはしばしば問題ではありません

  1. トランザクションがロールバックしている間、ロックは保持されません
  2. 優先度の低いバックグラウンドプロセスによって処理されます

SQL Serverの場合、状況が同じかどうかはわかりませんが、そうでない場合は誰かが言うでしょう...

「理由」については、rollbackrareである必要があると思います。通常、問題が発生した場合に限ります。もちろんcommitは、より一般的です-commitのために最適化することは理にかなっています

ロールバックは単に「ああ、気にしない」だけではありません-多くの場合、それは本当にそれがすでに行ったことを取り消す必要があります。元のトランザクションが並列で実行された場合でも、ロールバックはシングルスレッドですが、ロールバック操作が常に元の操作よりも遅いか常に速いという規則はありません。あなたが待っているなら、私は待つだけが最も安全であることをお勧めします。

もちろん、これはSQL Server 2019および Accelerated Database Recovery ですべて変更されます(これも不変であり、不変であるので、データのサイズに関係なく瞬時にロールバックできます)。

9
Aaron Bertrand

すべてのトランザクションで、ロールバックよりもコミットアクティビティのパフォーマンスが向上するわけではありません。そのようなケースの1つは、SQLの削除操作です。トランザクションが行を削除すると、これらの行はゴーストレコードとしてマークされます。コミットが発行され、ゴーストレコードのクリーンアップタスクが開始すると、これらのレコードのみが「削除」されます。

代わりにロールバックが発行された場合は、集中的な挿入ステートメントではなく、これらのレコードからゴーストマーキングを削除するだけです。

8
StanleyJohns

すべてではありません。 2つの操作はディスクI/Oに関して実質的に同一であるため、PostgreSQLはコミットするよりもロールバックに時間がかかりません。私は実際には、コミットのために最適化されているという問題ではなく、他のクエリがどのように最適化されているかという問題だと思います。

基本的な質問は、ディスク上のレイアウトにどのように対処するか、これがコミットとロールバックにどのように影響するかです。コミットよりもゆっくりとロールバックする主要なデータベースは、特にクラスター化されたテーブルからデータをメインデータ構造から移動し、データを更新するときにロールバックセグメントに配置する傾向があります。つまり、コミットするにはロールバックセグメントを削除するだけで、ロールバックするにはすべてのデータをコピーする必要があります。

PostgreSQLの場合、すべてのテーブルはヒープテーブルであり、インデックスは個別です。つまり、ロールバックまたはコミットするときに、データを再配置する必要はありません。これにより、コミットとロールバックの両方が高速になります。

ただし、他の処理が少し遅くなります。たとえば、主キーのルックアップでは、インデックスファイルをトラバースし、次にヒープテーブルをヒットする必要があります(適用可能なカバリングインデックスがない場合)。これは大したことではありませんが、他の情報と可視性をチェックするために、余分なページルックアップまたはおそらくいくつかのランダムなページルックアップ(その行で多くの更新が発生した場合)を追加します。

ただし、ここでの速度は、書き込み操作と読み取り操作のPostgreSQLでの最適化の問題ではありません。いくつかの読み取り操作を他の特権よりも優先することは不本意です。その結果、PostgreSQLは平均して他のdbと同じように動作します。高速または低速になる可能性があるのは、特定の操作だけです。

したがって、実際の答えは、dbは読み取り側の特定のワークロード用に最適化されており、これが書き込み側の課題につながると思います。通常、質問がある場合、コミットはロールバックよりも優先されます。ただし、これはどちらかを実行した場合の影響に依存します(更新は削除とは異なります)。

5
Chris Travers