如果2个或更多的人在更新的确切同时记录会发生什么? [英] What happens if 2 or more people update a record at the exact same time?

查看:119
本文介绍了如果2个或更多的人在更新的确切同时记录会发生什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用NHibernate的版本属性,每一个我聚合根更新时自动递增。如果2个或更多的人更新在完全相同的时间相同的记录,会发生什么?

I'm using NHibernate with the version property that automatically increments every time my aggregate root is updated. What happens if 2 or more people update the same record at the exact same time?

另外,我会怎么测试呢?

Also, how would I test this?

请注意,这不是我一直在,只是想知道的情况。

Note that this isn't a situation I've been in, just wondering.

推荐答案

由于其他人指出,在SQL Server的更新是原子操作。然而,与NHibernate(或任何O / RM)更新数据时,通常第一个选择中的数据,进行更改的对象,那么更新与更改数据库。事件该序列不会的原子。即使选择并更新了彼此毫秒内进行,机会存在另一个更新的中间滑动。如果两个客户端读取相同数据的同一版本,他们可以在不知不觉中互相覆盖,其他的变化,如果他们认为他们是唯一的编辑该数据在那个时候。

What's Atomic, and What's Not

As the others have stated, updates in SQL Server are atomic operations. However, when updating data with NHibernate (or any O/RM), you typically first select the data, make your changes to the object, then update the database with your changes. That sequence of events is not atomic. Even if the select and update were performed within milliseconds of each other, the chance exists for another update to slip in the middle. If two clients fetched the same version of the same data, they could unwittingly overwrite each-other's changes if they assumed that they were the only ones editing that data at that time.

如果我们不警惕这种并发更新方案,奇怪的事情都可能发生 - 偷偷摸摸的错误应该看起来不可能的。假设我们有这样的模拟水的状态变化的类:

If we didn't guard against this concurrent-update scenario, weird things could happen - sneaky bugs that shouldn't seem possible. Suppose we had a class that modeled the state changes of water:

public class BodyOfWater
{
    public virtual int Id { get; set; }
    public virtual StateOfMatter State { get; set; }

    public virtual void Freeze()
    {
        if (State != StateOfMatter.Liquid)
            throw new InvalidOperationException("You cannot freeze a " + State + "!");
        State = StateOfMatter.Solid;
    }

    public virtual void Boil()
    {
        if (State != StateOfMatter.Liquid)
            throw new InvalidOperationException("You cannot boil a " + State + "!");
        State = StateOfMatter.Gas;
    }
}

让我们说的水了以下机身被记录在数据库:

Let's say the following body of water is recorded in the database:

new BodyOfWater
{
    Id = 1,
    State = StateOfMatter.Liquid
};



两个用户从数据库中获取此记录在大致相同的时间,修改它,并保存更改回数据库。用户A冻结水:

Two users fetch this record from the database at roughly the same time, modify it, and save the changes back to the database. User A freezes the water:

using (var transaction = sessionA.BeginTransaction())
{
    var water = sessionA.Get<BodyOfWater>(1);
    water.Freeze();
    sessionA.Update(water);

    // Same point in time as the line indicated below...

    transaction.Commit();
}

用户B试图把水烧开(现在的冰!)...

User B tries to boil the water (now ice!)...

using (var transaction = sessionB.BeginTransaction())
{
    var water = sessionB.Get<BodyOfWater>(1);

    // ... Same point in time as the line indicated above.

    water.Boil();
    sessionB.Update(water);
    transaction.Commit();
}



......,是成功的!什么?用户A冻结了水。应该也不例外被抛出说:你不能熬固体!?用户B获取的数据的的用户A救了他的变化,所以这两个用户,水中出现最初是液体,所以这两个用户被允许以节省他们相互矛盾的状态变化。

... and is successful!!! What? User A froze the water. Shouldn't an exception have been thrown saying "You cannot boil a Solid!"? User B fetched the data before User A had saved his changes, so to both users, the water appeared to initially be a liquid, so both users were allowed to save their conflicting state changes.

要避免这种情况,我们可以添加一个版本版本/>财产类并在NHibernate的使用<映射它映射:

To prevent this scenario, we can add a Version property to the class and map it in NHibernate with a <version /> mapping:

public virtual int Version { get; set; }

这是一个简单的数字,NHibernate的递增每次更新记录时,它会检查以确保没有其他人已经递增的版本,而我们并没有看。取而代之的是并发天真SQL更新的喜欢...

This is simply a number that NHibernate will increment every time it updates the record, and it will check to make sure no-one else has incremented the version while we weren't watching. Instead of a concurrency-naive sql update like...

update BodyOfWater set State = 'Gas' where Id = 1;



... NHibernate的将现在使用一个更聪明的查询是这样的:

... NHibernate will now use a smarter query like this:

update BodyOfWater set State = 'Gas', Version = 2 where Id = 1 and Version = 1;

如果受查询的行数为0,则NHibernate的知道出事了 - 要么有人其他更新的行,这样的版本号现在是不正确,或有人删除的行,这样的标识不再存在。随后的NHibernate会抛出一个 StaleObjectStateException

If the number of rows affected by the query is 0, then NHibernate knows something went wrong - either someone else updated the row so that the version number is now incorrect, or someone deleted the row so that that Id no longer exists. NHibernate will then throw a StaleObjectStateException.

更多的时间之间存在初始选择的数据,并随后更新的,更大机会对于这种类型的并发问题。考虑一个Web应用程序典型的编辑的形式。一个实体的现有数据是从数据库中选择,放置到HTML格式,并发送给浏览器。用户可以花费几分钟的时间将其发送回服务器之前修改表单中的值。可能有一个像样的机会,别人在编在同一时间相同的信息,我们也才保住了变化。

The more time there is between the initial select of the data and the subsequent update, the greater the chance for this type of concurrency problem. Consider a typical "edit" form in a web app. The existing data for an entity is selected from the database, placed into the HTML form, and sent to the browser. The user may spend several minutes modifying the values in the form before sending it back to the server. There may be a decent chance that someone else was editing the same information at the same time, and they saved their changes before we did.

确保版本不期间,我们实际上保存更改可能不足以在这样的场景中的几毫秒改变。为了解决这个问题,你可以版本号发送到浏览器作为隐藏字段的表单字段的其余一起,然后检查,以确保该版本并没有改变,当你在保存之前获取实体背出数据库。此外,您可以限制的时间量介于两者之间的初始选择和最后一个更新通过提供独立的视图和编辑的观点,而不是仅仅用一切编辑的观点。用户花费上的编辑的观点的时间就越少,越少的机会,他们将与烦人的错误消息,他们的更改无法保存提交。

Making sure the version doesn't change during the few milliseconds we're actually saving the changes might not be enough in a scenario like this. To address this problem, you could send the version number to the browser as a hidden field along with the rest of the form fields, then check to make sure the version hasn't changed when you fetch the entity back out of the database before saving. In addition, you can limit the amount of time in-between the initial select and the final update by providing separate "view" and "edit" views instead of just using an "edit" view for everything. The less time the user spends on an "edit" view, the less chance that they'll be presented with an annoying error message saying that their changes could not be saved.

这篇关于如果2个或更多的人在更新的确切同时记录会发生什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆