3

私は現在、トランザクションに取り組んでおり、混乱しています。これらのトランザクションは、データベースのストアド プロシージャではなく、データ アクセス レイヤーで作成されます (SQL Server 2008)。トランザクションに設定された分離レベルの通常の動作を理解しています。次のシナリオで何が起こるべきかを理解できません。

  1. 取引を開始する
  2. ID=1 の従業員を選択します。
  3. ID=1 で従業員を更新します。
  4. 専念

同じことをしている複数のスレッドがありますが、ID は異なります。しかし、2 つのスレッドで同じ ID を検索する場合があります。それらをスレッド A と B と呼びましょう。上記の手順は、2 つのスレッドに関して次のように進行します。Isolation Level は Repeatable Read に設定されています。

A1. トランザクション A2 を開始します。ID=1 の従業員を選択します。B1. トランザクション B2 を開始します。ID=1 の従業員を選択します。A3. ID=1 で従業員を更新します。A4. B3 をコミットします。ID=1 で従業員を更新します。B4. 専念

このトランザクションから本当に達成したいことは、スレッド A が特定のレコードを選択したときに、スレッド B がそのレコードを選択することさえできないようにすることです。このシナリオでトランザクションとロックを使用することで、正しい軌道に乗っているかどうかはわかりません。

返信待ち:)

4

4 に答える 4

5

デッドロックを防ぐには、UPDLOCK テーブル ヒントを使用する必要があります。

select * from employee with (updlock) where id = @id
update employee set name = @name where id = @id

select はデフォルトで共有読み取りロックを取得するため、これがないとデッドロックが発生する可能性があります。

  1. トランザクション A が選択 (共有読み取りロック) を行います。
  2. トランザクション B が選択を行います (共有読み取りロック。トランザクション A と同じレコードの一部である可能性があります。たとえば、ページ ロックが取得されている場合)。
  3. トランザクション A は、排他的な書き込みロック (ロックのエスカレーション) を必要とする更新を実行しますが、トランザクション B が共有読み取りロックを解放するのを待つ必要があります。
  4. トランザクション B も更新を行う必要がありますが、トランザクション A が共有読み取りロックを解放するのを待つ必要があります。

そのため、トランザクション A と B は互いに待機しています - 従来のロック エスカレーション デッドロックです。UPDLOCK テーブル ヒントは、select に排他ロックを強制するため、これを回避します。

  1. トランザクション A が選択 (排他更新ロック) を行います。
  2. トランザクション B はその選択を行いたいと考えていますが、トランザクション A が最初にそのロックを解放するのを待たなければなりません。
  3. トランザクション A が更新を実行し、コミットして、選択によって取得された更新ロックを解放します。
  4. トランザクション B がその選択を実行できるようになりました。

編集: UPDLOCK と ROWLOCK を組み合わせて、「with (updlock, rowlock)」などの行レベルのロックを要求できます。尋ねることはできますが、常に得られるとは限りません。 http://msdn.microsoft.com/en-us/library/ms187373(v=sql.100).aspxを参照してください。また、行ロックを使用すると、SQL Server で追跡するロックがさらに多くなるため、行ロックはページ ロックよりもコストがかかる可能性があります。したがって、SQL Server 自体にロックの範囲を選択させます。通常、SQL Server は正常に機能します。この場合、テーブル ロックは必要ありません。行ロックがないと問題が発生する場合にのみ、行ロックを明示的に使用してください。

また、行ロックだけでは、2 つのトランザクションが同じレコード (行) を選択して更新しようとするデッドロックを防ぐことはできないことに注意してください。そのため、常にアップロックが必要です。

于 2012-09-27T08:37:14.033 に答える
1

楽観的ロックを確認する必要があります。これは、レコードが読み取りと書き込みの間で変更されていないかどうかをチェックする更新に追加のチェックを追加することで機能します。トランザクションスコープ外でレコードを読み取ることもできます。これにより、全体的なパフォーマンスが向上します。

Optimistic_concurrency_control wikipedia

于 2012-09-27T07:52:55.693 に答える
1

次のようなことを試してください:

using System;
using System.Transactions;
using System.Data;
using Microsoft.Practices.EnterpriseLibrary.Data;

namespace StackOverflow.Demos
{
    class Program
    {

        static Database db = DatabaseFactory.CreateDatabase("demo");

        public static void Main(string[] args)
        {
            TransactionOptions options = new TransactionOptions();
            options.IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead; //see http://www.gavindraper.co.uk/2012/02/18/sql-server-isolation-levels-by-example/ for a helpful guide to choose as per your requirements
            using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, options))
            {
                using (IDbConnection connection = db.CreateConnection())
                {
                    connection.Open(); //nb: connection must be openned within transactionscope in order to take part in the transaction
                    IDbCommand command = connection.CreateCommand();

                    command.CommandType = CommandType.Text;
                    command.CommandText = "select top 1 status from someTable with(UPDLOCK, ROWLOCK) where id = 1"; //see http://msdn.microsoft.com/en-us/library/aa213026(v=sql.80).aspx
                    string statusResult = command.ExecuteScalar().ToString();

                    if (!statusResult.Equals("closed",StringComparison.OrdinalIgnoreCase))
                    {
                        command.CommandType = CommandType.Text;
                        command.CommandText = "update someTable set status='closed' where id = 1";
                    }

                    scope.Complete();
                }
            }
        }
    }
}

ps。一般に、上記で行ったように、ハードコードされた SQL ではなくストアド プロシージャを使用することをお勧めします。すべてのロジックをストアド プロシージャにプッシュして、プロシージャを 1 回呼び出すだけで、データベース内ですべてのロジックが処理されるようにすることができます。 .

上記の例では、名前空間に気付くでしょう:

Microsoft.Practices.EnterpriseLibrary.Data;

これは、私がエンタープライズ ライブラリから MS の Data Block を使用する傾向があるためです。これにより、OOTB ライブラリに加えて多くの機能が提供されます。興味がある場合は、ここで詳細を読むことができます: http://msdn.microsoft.com/library/cc467894.aspx

于 2012-10-21T00:15:16.417 に答える
-1

私はあなたが使用しているスレッドメカニズムを見ているべきだと私には思えます。(トランザクション中ではなく) 事前に知ることができ、既に処理されている ID でスレッドを開始しないようにする必要があります。または、スレッドは、処理する必要がある ID を持つ共有同期リストにアクセスできる必要があります。そうすれば、2 つのスレッドが同じ ID で動作することはありません。

于 2012-09-27T08:02:35.563 に答える