web-dev-qa-db-ja.com

読み取りトランザクションをコミットまたはロールバックする必要がありますか?

トランザクション内で実行する読み取りクエリがあるため、分離レベルを指定できます。クエリが完了したら、どうすればよいですか?

  • トランザクションをコミットする
  • トランザクションをロールバックする
  • 何もしない(これにより、usingブロックの最後でトランザクションがロールバックされます)

それぞれを行うことの意味は何ですか?

using (IDbConnection connection = ConnectionFactory.CreateConnection())
{
    using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted))
    {
        using (IDbCommand command = connection.CreateCommand())
        {
            command.Transaction = transaction;
            command.CommandText = "SELECT * FROM SomeTable";
            using (IDataReader reader = command.ExecuteReader())
            {
                // Read the results
            }
        }

        // To commit, or not to commit?
    }
}

編集:問題は、トランザクションを使用すべきかどうか、またはトランザクションレベルを設定する他の方法があるかどうかではありません。問題は、何も変更しないトランザクションがコミットまたはロールバックされるという違いが生じるかどうかです。パフォーマンスに違いはありますか?他の接続に影響しますか?他の違いはありますか?

89
Stefan Moser

コミットします。限目。他に賢明な代替手段はありません。トランザクションを開始した場合は、閉じてください。コミットすると、ロックが解除された可能性があり、ReadUncommittedまたはSerializable分離レベルでも同様に賢明です。暗黙的なロールバックに依存することは、おそらく技術的には同等ですが、形式が悪いだけです。

それでも納得がいかない場合は、コードの途中に更新ステートメントを挿入し、発生する暗黙のロールバックを追跡してデータを削除する次の人を想像してください。

51
Mark Brackett

何も変更していない場合は、COMMITまたはROLLBACKを使用できます。いずれかが取得した読み取りロックを解放し、他の変更を行っていないため、それらは同等になります。

25
Graeme Perrow

トランザクションを開始する場合、ベストプラクティスは常にコミットすることです。 use(transaction)ブロック内で例外がスローされると、トランザクションは自動的にロールバックされます。

6
Neil Barnwell

ネストされたトランザクションを検討してください。

ほとんどのRDBMSは、ネストされたトランザクションをサポートしていないか、非常に限られた方法でそれらをエミュレートしようとします。

たとえば、MS SQL Serverでは、内部トランザクション(実際のトランザクションではないため、MS SQL Serverはトランザクションレベルをカウントするだけです!)でのロールバックは、outmostトランザクション(これが実際のトランザクションです)。

一部のデータベースラッパーは、内部トランザクションでのロールバックをエラーが発生したことの兆候と見なし、最も外側のトランザクションがコミットまたはロールバックされたかどうかに関係なく、最も外側のトランザクションですべてをロールバックします。

したがって、コンポーネントが一部のソフトウェアモジュールで使用されていることを除外できない場合、COMMITは安全な方法です。

これは質問に対する一般的な回答であることに注意してください。コード例は、新しいデータベース接続を開くことにより、外部トランザクションの問題を巧妙に回避します。

パフォーマンスに関して:分離レベルに応じて、SELECTにはさまざまな程度のLOCKと一時データ(スナップショット)が必要になる場合があります。これは、トランザクションが閉じられるとクリーンアップされます。これがCOMMITまたはROLLBACKのどちらで実行されるかは関係ありません。費やされるCPU時間にわずかな違いがある可能性があります。COMMITの解析は、ROLLBACK(2文字未満)やその他の小さな違いよりもおそらく高速です。明らかに、これは読み取り専用操作にのみ当てはまります!

完全に求められていない:コードを読む可能性のある別のプログラマーは、ROLLBACKがエラー状態を意味すると想定するかもしれません。

3
Klaws

私見では、トランザクションで読み取り専用クエリをラップすることは理にかなっています(特にJavaで)トランザクションを「読み取り専用」にすると、JDBCドライバはクエリの最適化を検討できます(しかし、そうする必要はありません。それでもINSERTを発行できなくなります)。例えば。 Oracleドライバーは、読み取り専用とマークされたトランザクションでクエリのテーブルロックを完全に回避します。

3
Oliver Drotbohm

ちょっとしたメモですが、次のようにコードを書くこともできます。

using (IDbConnection connection = ConnectionFactory.CreateConnection())
using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted))
using (IDbCommand command = connection.CreateCommand())
{
    command.Transaction = transaction;
    command.CommandText = "SELECT * FROM SomeTable";
    using (IDataReader reader = command.ExecuteReader())
    {
        // Do something useful
    }
    // To commit, or not to commit?
}

また、少しだけ再構築すると、IDataReaderのusingブロックを最上部に移動できる場合があります。

2
Joel Coehoorn

SQLをストアドプロシージャに入れ、これをクエリの上に追加する場合:

set transaction isolation level read uncommitted

その後、C#コードのフープをジャンプする必要はありません。ストアドプロシージャでトランザクション分離レベルを設定しても、その接続の将来の使用すべてに設定が適用されるわけではありません(接続はプールされるため、他の設定で心配する必要があります)。ストアドプロシージャの最後で、接続が初期化されたものに戻ります。

1
Eric Z Beard

ROLLBACKは、エラーまたは例外的な状況の場合に主に使用され、正常に完了した場合にCOMMITが使用されます。

重要ではないように見える読み取り専用トランザクションの場合でも、COMMIT(成功の場合)およびROLLBACK(失敗の場合)でトランザクションを閉じる必要があります。実際、一貫性と将来の保証のために重要です。

読み取り専用トランザクションは、たとえば次のようなさまざまな方法で論理的に「失敗」する可能性があります。

  • クエリが期待どおりに正確に1行を返さない
  • ストアドプロシージャが例外を発生させる
  • フェッチされたデータに一貫性がないことがわかりました
  • 時間がかかりすぎるため、ユーザーはトランザクションを中止します
  • デッドロックまたはタイムアウト

COMMITとROLLBACKが読み取り専用トランザクションに適切に使用されている場合、DB書き込みコードが何らかの時点で追加された場合、期待どおりに動作し続けます。キャッシュ、監査、または統計用。

暗黙的なROLLBACKは、アプリケーションがクラッシュしたり、回復不能なエラー、ネットワーク障害、電源障害などで終了した場合の「致命的なエラー」状況にのみ使用してください。

1
Sam Watkins

READが状態を変更しないことを考えると、私は何もしません。コミットを実行しても、リクエストをデータベースに送信するサイクルを無駄にする以外は何もしません。状態を変更した操作を実行していません。ロールバックについても同様です。

ただし、必ずオブジェクトをクリーンアップし、データベースへの接続を閉じてください。このコードが繰り返し呼び出されると、接続を閉じないと問題が発生する可能性があります。

0
Brett McCann

AutoCommitをfalseに設定すると、YESになります。

JDBC(Postgresqlドライバー)の実験で、選択クエリが(タイムアウトのために)壊れると、ロールバックしない限り新しい選択クエリを開始できないことがわかりました。