1

この他の質問のフォローアップ。

上記の質問で説明したように、並行性の問題に対して悲観的ロックを実装しようとしています (その質問に自由に追加してください)。しかし、それは私にとってはうまくいきません。

私は非常に簡単なテストを行います: 私は 2 つの別々のサイトを実行しており、どちらもカウンターを 500 倍に増やします。私はそれらを同時に実行します。最後に、テーブルの特定の列の値が 1000 であることを期待しています。

これがコードです。もちろん、これは製品コードではありませんが、テスト コードであろうとなかろうと、それでも動作するはずですよね?

for (int i = 0; i < 500; i++)
{
  var tx = this.userRepo.Session.BeginTransaction();
  var user = this.userRepo.GetById(42);
  user.Counter++;
  userRepo.Save(user);
  tx.Commit();
}

GetByIdメソッドは LockMode.Upgrade を使用します。

public T GetById(int id)
{
  T obj = Session.Get<T>(id, LockMode.Upgrade);
  return obj;
}

ここで、NHProfiler を使用すると、次の SQL ステートメントが表示されます。

SELECT Id FROM 'User' WHERE Id = 42 for update

しかし、結果は約530の値になるため、同時実行により失われた更新の約半分です。私は何を間違っていますか?このテストでは、二次キャッシュを無効にしました。間違ったロックモードを使用していませんか? 分離レベルを指定する必要がありますか? 他に何か?前もって感謝します。

編集: FluentNhibernate 構成:

Fluently.Configure()
.Database(MySQLConfiguration.Standard.ConnectionString(connectionstring))
.Mappings(m => assemblyTypes.Select(t => t.Assembly).ToList().ForEach(a => m.FluentMappings.AddFromAssembly(a)))
.ExposeConfiguration(c => c.Properties.Add("hbm2ddl.keywords", "none"));
4

1 に答える 1

0

LockMode.Upgrade機能するには、すべてのトランザクションがトランザクションにLockMode.Upgrade含まれている必要があります。これは、トランザクションを現在のトランザクションにロックするためです。

あなたの問題は、ステートメントがトランザクションに含まれていないことが原因である可能性が最も高いです。

楽観的ロックは、単一のステートメントには適用されませんが、互いに分離された複数のトランザクションに適用されます。例:

  1. 取引を開始します。

  2. Id = 42;でレコードを取得

  3. 取引を終了します。

次に、トランザクションの外で、 を増やしCounterます。

その後:

  1. 取引を開始します。

  2. Id = 42;でレコードを取得

  3. カウンターが最初のトランザクションで受け取った値から変更されていないかどうかを確認します。

    を。変更されていない場合は、増加した値でカウンターを更新します。

    b. 変更されている場合は、変更された値を処理します。

  4. 取引を終了します。

楽観的ロックとは、2 つのトランザクション間で が変更されていないことを「望み」、Counter変更された場合に対処することを意味します。悲観的ロックを使用すると、必要なすべてのレコードがロックされた単一のトランザクション内ですべての変更が確実に行われます。

ところで:チェックメカニズム(Counterその間に変更されたかどうか)は、NHibernateによって自動的に処理できます。

于 2010-11-23T08:32:50.947 に答える