1

跟进this other question

正如我在上面的问题中描述的那样,我正在尝试为并发问题实施悲观锁定(请随时添加到那个问题中)。但这对我不起作用。

我做了一个非常简单的测试:我运行了两个单独的站点,它们都将计数器增加了 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. 检查 counter 是否与第一次交易中收到的值没有变化;

    一个。如果它没有改变,用增加的值更新计数器;

    湾。如果已更改,请处理更改的值。

  4. 结束交易。

乐观锁定意味着您“希望”Counter两个事务之间没有更改并处理它已更改的情况。使用悲观锁定,您可以确保所有更改都在单个事务中完成,并且所有必需的记录都被锁定。

顺便说一句:CounterNHibernate 可以自动处理检查机制(同时是否发生变化)。

于 2010-11-23T08:32:50.947 回答