これら2つのSQL Serverロールバックはどのように異なりますか?


13

SQL Server 2008 R2では、これら2つのロールバックの違いは次のとおりです。

  1. ALTER数分間ステートメントを実行し、「実行のキャンセル」を押します。完全にロールバックするには数分かかります。

  2. 同じALTERステートメントを実行しますが、これはLDFファイルが正常に完了するのに十分な大きさでないことを確認します。いったんLDF制限が満たされるとNO「自動拡張」が許されない、クエリの実行はすぐに停止します(またはロールバックが起こる)、このエラーメッセージが表示されて:

The statement has been terminated.
Msg 9002, Level 17, State 4, Line 1
The transaction log for database 'SampleDB' is full. 
To find out why space in the log cannot be reused, see the 
log_reuse_wait_desc column in sys.databases

次の点で、これら2つの違いは何ですか?

  1. 2番目の「ロールバック」が瞬時に行われるのはなぜですか?ロールバックと呼べるかどうかは完全にはわかりません。私の推測では、トランザクションログは実行の進行とともに書き込まれ、タスクを完全に完了するための十分なスペースがないことがわかると、コミットせずに「終了」メッセージで停止します。

  2. 最初のロールバックに非常に長い時間がかかるとどうなりますか(ロールバックはシングルスレッドです)。
    2.1。SQL Serverは戻って、LDFファイルに作成されたエントリを元に戻しますか?
    2.2。LDFファイルサイズは(からのロールバックの終わりに小さくなりますDBCC SQLPERF(LOGSPACE)

  3. もう1つの質問:2番目のシナリオでは、SQL ServerがLDFファイルをすぐに消費し始めます。私の場合、最初の数分(<4分)で使用率が18%から90%に増加しました。しかし、99%に達すると、さらに8分間そこに留まり、使用率は99.1%から99.8%の間で変動します。エラーがスローされる前に、数回(99.8%)上下し(99.2%)、再度上昇(99.7%)し、下降(99.5%)します。舞台裏で何が起こっていますか?

これをさらに説明するのに役立つMSDNリンクを歓迎します。

アリラゼギの提案で、perfmonを追加しています。 Disk Bytes/sec

シナリオ1:

シナリオ1

シナリオ2:

シナリオ2


簡単なコメント:ファイルサイズ!=ファイル内で使用されるスペース。
アーロンバートランド

@Aaronはい、私はそれに精通しています。DBCC SQLPERF(LOGSPACE)を使用して使用量を測定しました。ファイルサイズを10 GBに制限したため、LDFファイルは全期間同じままでした。内部使用は異なります。
ToC

1
エラーはロールバックの完了に報告されるため、2番目のロールバック瞬時に表示されると思われます。これらはおそらく、3で観察した8分間であり、LDFの使用はほぼ一定のままです。
ムスタッチョ

@mustaccio私はあなたに同意しますが、アーティファクトは異なる方向を指しています。実際にロールバックが発生した場合は、ログファイルの使用量をより少ない数値に戻す必要があります。エラーメッセージがスローされたときに99.3%に留まらないようにします。
ToC

1
ロールバックにはログ領域が必要だと思います。ロールバック中にログがいっぱいになると、DBが疑わしくなります。これ以上は触れません。これは、瞬時のロールバックのように見えますが、データベースをオンラインに戻すことができるまで(使用可能なディスク領域がある場合)、ロールバックは延期されています。また、ログがいっぱいになると、SQL Serverはチェックポイントによってスペースを確保しようとする場合があり、IOアクティビティの急上昇を説明する場合があります。
usr

回答:


1

上記で示したように、さらにテストを実行した後、計算結果に到達しました。それらすべてをここのブログ投稿にまとめましたが、後世のためにこの投稿にコンテンツをコピーします。

推測(いくつかのテストに基づく)

今のところ、これがなぜなのか明確な説明はありません。しかし、テスト中に収集されたアーティファクトに基づいた私の推定は次のとおりです。

ロールバックは両方のシナリオで発生します。1つは明示的なロールバック(ユーザーが[キャンセル]ボタンを押す)、もう1つは暗黙的です(SQL Server は内部的にその決定を行います)。

どちらのシナリオでも、ログファイルに送られるトラフィックは一貫しています。以下の画像をご覧ください。

シナリオ1:

シナリオ1:

シナリオ2:

シナリオ2

  • この考え方を強化した成果物の1つは、両方のシナリオでSQLトレースをキャプチャすることです。

    • シナリオ1は、「キャンセル」を押すと自明であり、ロールバックします。
    • シナリオ2では、「ロールバック」を暗黙的に実行した後にエラーメッセージが表示されます。Sql Traceでは、メッセージが画面に表示されるずっと前に「データベース 'SampleDB'のトランザクションログがいっぱいです」というエラーメッセージが表示されます。したがって、両方のシナリオでロールバックが発生すると推測しますが、エラーメッセージは、ロールバックが正常に完全に実行された後、シナリオ2が表示されます。
  • シナリオ2はさらに進行するにつれて時間がかかるように見えるため、ロールバックには時間がかかります。

原因不明の動作:

  • ログファイルの使用量が大きく異なるのはなぜですか?
    • 90%に増加し、85%に減少し、99%に増加し、長時間そこに留まります。このように何度か上下に移動します:99.2%、99.8%、99.1%、99.7%。なぜこれが起こるのですか?
    • 考えられる説明の1つは、ログファイルを数分ごとにクリーンアップするバックグラウンドプロセス(ログフラッシュなど)がある可能性があることです。そして、起動するたびに、いくつかのエントリが消去され、利用可能な空きスペースが増えます。

この動作をより良い方法で説明するためのアイデアを歓迎します。


2
Paul Randalに確認して、彼はあなたが正しい結論に達したことを確認しました-ロールバックは両方のケースで同じです。
ポールホワイト9

0

私は次の実験を試みましたが、同様の結果が得られました。どちらの場合でも、fn_dblog()はロールバックの発生を示し、シナリオ1よりもシナリオ2の方が早く発生するようです。

ところで、MDFとLDFの両方を同じ単一の外部(USB 2.0)ディスクに配置しました。

私の最初の結論は、この場合のロールバックの動作に違いはなく、おそらく明らかな速度の違いはI / Oサブシステムに関連しているということです。それは現時点での私の作業仮説です。

シナリオ1:

  • 1MBで始まり、4MBのチャンクで成長し、最大サイズが100MBのログファイルでデータベースを作成します。
  • 明示的なトランザクションを開き、10秒間実行してから、SSMS内で手動でキャンセルします
  • fn_dblog()カウントとログ予約サイズを確認し、DBCC SQLPERF(LOGSPACE)をチェックアウトします

シナリオ2:

  • 1MBで始まり、4MBのチャンクで成長し、最大サイズが100MBのログファイルでデータベースを作成します。
  • 明示的なトランザクションを開き、ログがいっぱいになるまでエラーを表示します
  • fn_dblog()カウントとログ予約サイズを確認し、DBCC SQLPERF(LOGSPACE)をチェックアウトします

パフォーマンスモニターの結果:

シナリオ1: ***シナリオ1 ***

シナリオ2: ***シナリオ2 ***

コード:

USE [マスター];
行く

IF DATABASEPROPERTYEX(N'SampleDB '、N'Version')> 0
ベギン
    ALTER DATABASE [SampleDB] SET SINGLE_USER
        即時ロールバックあり;
    DROP DATABASE [SampleDB];
終わり;
行く

プライマリでデータベース[SampleDB]を作成 
( 
      NAME = N'SampleDB '
    、FILENAME = N'E:\ data \ SampleDB.mdf ' 
    、サイズ= 3MB 
    、FILEGROWTH = 1MB 
)
ログオン 
( 
      NAME = N'SampleDB_log '
    、FILENAME = N'E:\ data \ SampleDB_log.ldf '
    、サイズ= 1MB 
    、MAXSIZE = 100MB 
    、FILEGROWTH = 4MB 
);
行く

使用[SampleDB];
行く

-テーブルを追加する
CREATE TABLE dbo.test
(
    c1 CHAR(8000)NOT NULL DEFAULT REPLICATE( 'a'、8000)
)ON [PRIMARY];
行く

-疑似単純復旧モデルではないことを確認する
バックアップデータベースSampleDB
ディスク= 'NUL';
行く

-ログファイルをバックアップする
バックアップログSampleDB
ディスク= 'NUL';
行く

-使用済みのログスペースをチェックする
DBCC SQLPERF(LOGSPACE);
行く

-fn_dblog()で表示できるレコードの数は?
SELECT * FROM fn_dblog(NULL、NULL); -私の場合は約9

/ **********************************
             シナリオ1
********************************** /
-新しいトランザクションを開いてからロールバックする
取引を開始

    dbo.testのデフォルト値に挿入します。
    GO 10000-Letを10秒間実行し、SSMSクエリウィンドウでキャンセルを押します

    -トランザクションをキャンセルする
    -完了するまでに数秒かかります


-キャンセルは既にそれを行っているため、トランザクションをロールバックする必要はありません。
-  やってみなよ。このエラーが発生します
-メッセージ3903、レベル16、状態1、行1
-ROLLBACK TRANSACTION要求には、対応するBEGIN TRANSACTIONがありません。
ロールバックトランザクション。

-使用されているログスペースは?100%以上。
DBCC SQLPERF(LOGSPACE);
行く

-fn_dblog()で表示できるレコードの数は?
選択する * 
FROM fn_dblog(NULL、NULL); -私の場合、約91,926

-fn_dblog()で表示される総ログ予約?
SELECT SUM([ログ予約])AS [合計ログ予約]
FROM fn_dblog(NULL、NULL); -約88.72MB


/ **********************************
             シナリオ2
********************************** /
-DBを吹き飛ばしてやり直す
USE [マスター];
行く

IF DATABASEPROPERTYEX(N'SampleDB '、N'Version')> 0
ベギン
    ALTER DATABASE [SampleDB] SET SINGLE_USER
        即時ロールバックあり;
    DROP DATABASE [SampleDB];
終わり;
行く

プライマリでデータベース[SampleDB]を作成 
( 
      NAME = N'SampleDB '
    、FILENAME = N'E:\ data \ SampleDB.mdf ' 
    、サイズ= 3MB 
    、FILEGROWTH = 1MB 
)
ログオン 
( 
      NAME = N'SampleDB_log '
    、FILENAME = N'E:\ data \ SampleDB_log.ldf '
    、サイズ= 1MB 
    、MAXSIZE = 100MB 
    、FILEGROWTH = 4MB 
);
行く

使用[SampleDB];
行く

-テーブルを追加する
CREATE TABLE dbo.test
(
    c1 CHAR(8000)NOT NULL DEFAULT REPLICATE( 'a'、8000)
)ON [PRIMARY];
行く

-疑似単純復旧モデルではないことを確認する
バックアップデータベースSampleDB
ディスク= 'NUL';
行く

-ログファイルをバックアップする
バックアップログSampleDB
ディスク= 'NUL';
行く

-では、トランザクション内でログファイルを爆破しましょう
取引を開始
    dbo.testのデフォルト値に挿入します。
    GO 10000

-ロールバックは発生しません。それを試してみてください。エラーが発生します。
-メッセージ3903、レベル16、状態1、行1
-ROLLBACK TRANSACTION要求には、対応するBEGIN TRANSACTIONがありません。
ロールバックトランザクション。

-ログファイルは100%満杯ですか? 
DBCC SQLPERF(LOGSPACE);

-fn_dblog()で表示できるレコードの数は?
選択する * 
FROM fn_dblog(NULL、NULL); -私の場合、約91,926
行く

-fn_dblog()で表示される総ログ予約?
SELECT SUM([ログ予約])AS [合計ログ予約]
FROM fn_dblog(NULL、NULL); -88.72MB
行く

詳細なテストをありがとう。さらにテストを実行した後、同様の結論に達しました。そこで、ブログ記事を書きました。SQL Serverはロールバックする必要がより多くのシナリオ1よりも実現する前に仕事の量は、シナリオ2で実行するので私にとってはシナリオ2は、ロールバックに時間がかかります
目次
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.