我正在尝试为并发问题实现悲观锁定,正如我在上面的问题中所描述的那样(请随意添加到那个)。但它不适合我。
我做了一个非常简单的测试:我有两个单独的站点运行,这两个站点都增加了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 的值,因此大约一半的更新因并发而丢失。我究竟做错了什么?我在此测试中禁用了二级缓存。我使用错误的锁定模式吗?我应该指定一个isoliation级别吗?还要别的吗?提前谢谢。
编辑: 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"));
答案 0 :(得分:0)
要使LockMode.Upgrade
生效,所有交易都必须包含在交易中,因为LockMode.Upgrade
所做的就是将其锁定到当前交易中。
您的问题很可能是由于这些陈述没有包含在交易中。
乐观锁定不适用于单个语句,而是适用于彼此分离的多个事务。一个例子:
开始交易;
按Id = 42
;
结束交易。
然后,在交易之外,增加Counter
。
之后:
开始交易;
按Id = 42
;
检查计数器是否与第一笔交易中收到的值保持不变;
一个。如果没有更改,请使用增加的值更新计数器;
湾如果已更改,请处理更改的值。
结束交易。
乐观锁定意味着您“希望”Counter
在两个事务之间没有发生变化,并处理它已发生变化的情况。使用悲观锁定,您可以确保在单个事务中完成所有更改,并锁定所有必需记录。
B.t.w。:检查机制(Counter
是否在平均时间内发生了变化)可以由NHibernate自动处理。