Linq to Sql并发冲突及处理策略
0. 并发冲突的示例
单用户的系统现在应该比较罕见了,一般系统都会有很多用户在同时进行操作;在多用户系统中,涉及到的一个普遍问题:当多个用户“同时”更新(修改或者删除)同一条记录时,该如何更新呢?
下图展示了开放式并发冲突的一个示例:

假设数据库中有一条记录Record{Field1=5, Field2=6, Field3=7}(以下简写为{5, 6, 7}),A、B两个用户按照如下顺序操作这一条记录:
(1). A读取该记录,取得的值为{5, 6, 7},读取完毕后,不对该记录加排他锁;
(2). B读取该记录,取得的值也为{5, 6, 7},读取完毕后,不对该记录加排他锁;
(3). B将该记录修改为{3, 5, 7},并写回数据库;由于该记录没有被其他用户锁定,且B在修改时该记录的值与第(2)步中读取的值一致,因此可以正常写入数据库;
(4). A将该记录修改为{5, 8, 9},并写回数据库;由于A在修改时该记录的值已更新为{3, 5, 7},与第(1)步中读取的值{5, 6, 7}不一致,因此引发并发冲突;
1. 开放式并发(乐观并发) & 封闭式并发(悲观并发)
开始之前,先介绍两个概念(From 《SQL Server 2008联机丛书》)。
|
乐观并发控制 |
在乐观并发控制中,用户读取数据时不锁定数据。当一个用户更新数据时,系统将进行检查,查看该用户读取数据后其他用户是否又更改了该数据。如果其他用户更新了数据,将产生一个错误。一般情况下,收到错误信息的用户将回滚事务并重新开始。这种方法之所以称为乐观并发控制,是由于它主要在以下环境中使用:数据争用不大且偶尔回滚事务的成本低于读取数据时锁定数据的成本。 |
|
悲观并发控制 |
一个锁定系统,可以阻止用户以影响其他用户的方式修改数据。如果用户执行的操作导致应用了某个锁,只有这个锁的所有者释放该锁,其他用户才能执行与该锁冲突的操作。这种方法之所以称为悲观并发控制,是因为它主要用于数据争用激烈的环境中,以及发生并发冲突时用锁保护数据的成本低于回滚事务的成本的环境中。 |
举个例子来说:
乐观并发:本文起始位置的图片,展示的是乐观并发情况下引发的冲突。
悲观并发:继续沿用图片中示例的请求顺序,如果第(1)步中A读取这条记录后,给记录加上排他锁,并且一直持有,在更新完毕之前不释放该锁;则第(2)步中B尝试读取该记录时,请求会被阻塞,直到A释放该记录,或者请求超时。因此新的执行顺序(假设请求没有超时)为:A读取并锁定记录{5, 6, 7}-->B尝试读取该记录[B被阻塞等待]-->A写回{5, 8, 9}到数据库,并释放该记录-->B读取到{5, 8, 9},并锁定该记录-->B修改该记录并写回数据库……
2. Linq to SQL中的乐观并发控制
L2S支持开放式并发控制。使用L2S执行修改和删除操作时,同时打开SQL Server Profile来查看生成的SQL代码,我们可以看到类似这样的代码(假设表上加了TimeStamp字段):
UPDATE TableName SET Field1=@p0, Field2=@p1 WHERE PrimaryKey=@p2 AND TimeStampField=@p3
//(省略)....
DELETE TableName WHERE PrimaryKey=@p0 AND TimeStampField=@p1
//(省略)....
当记录被更新时,则其TimeStamp字段会被自动更新。因此,如果在用户读取该记录后、且更新该记录之前,有其他用户更新过这条记录,则更新会失败(根据受影响行数为0来判断),L2S会抛出ChangeConflictException异常。
以上描述的是表上有加timeStamp字段的情况,如果表上没有TimeStamp字段,L2S会对映射为UpdateCheck = UpdateCheck.Always 或 UpdateCheck.WhenChanged的字段成员进行开放式并发检查,可以根据Sql server Profile来查看,不再赘述。
3. 冲突解决
既然有了冲突,就需要把冲突给和谐掉。
还是以本文起始位置的例子来说,最后A更新时,该更新为啥呢?可以存在如下三种选择:
(1). 覆盖数据值库:{5,8,9}?
(2). 保留数据库值:{3,5,7}?
(3). 合并为:{3,8,9}?
这三种方式分别对了L2S的三种解决方案:
3.1 通过覆盖数据库值解决并发冲突
try
{
db.SubmitChanges(ConflictMode.ContinueOnConflict); //需要指定为ConflictMode.ContinueOnConflict
}
catch (ChangeConflictException e)
{
foreach (ObjectChangeConflict occ in db.ChangeConflicts)
{
occ.Resolve(RefreshMode.KeepCurrentValues); //保留当前值,覆盖数据库中的值
}
}
db.SubmitChanges(ConflictMode.FailOnFirstConflict); //处理完冲突后,重试
3.2 通过保留数据库值解决并发冲突
try
{
db.SubmitChanges(ConflictMode.ContinueOnConflict);
}
catch (ChangeConflictException e)
{
foreach (ObjectChangeConflict occ in db.ChangeConflicts)
{
occ.Resolve(RefreshMode.OverwriteCurrentValues);//以数据库中的值,重写当前值
}
}
db.SubmitChanges(ConflictMode.FailOnFirstConflict); //处理完冲突后,重试
3.3 通过与数据库值合并解决并发冲突
try
{
db.SubmitChanges(ConflictMode.ContinueOnConflict);
}
catch (ChangeConflictException e)
{
foreach (ObjectChangeConflict occ in db.ChangeConflicts)
{
occ.Resolve(RefreshMode.KeepChanges);//保留数据库中的值和当前值,进行合并处理
}
}
db.SubmitChanges(ConflictMode.FailOnFirstConflict); //处理完冲突后,重试
Linq to Sql并发冲突及处理策略的更多相关文章
- Linq to Sql : 并发冲突及处理策略
原文:Linq to Sql : 并发冲突及处理策略 1. 通过覆盖数据库值解决并发冲突 try { db.SubmitChanges(ConflictMode.ContinueOnConflict) ...
- Linq to sql并发与事务
本文转载:http://www.cnblogs.com/lovecherry/archive/2007/08/20/862365.html 检测并发 首先使用下面的SQL语句查询数据库的产品表: se ...
- [Linq To Sql]解决join时的Collation冲突
背景 现在两表 A:
- LINQ to SQL大全
LINQ to SQL语句 (1)之Where Where操作 适用场景:实现过滤,查询等功能. 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的 ...
- [转]LINQ To SQL 语法及实例大全
转载自:http://blog.csdn.net/pan_junbiao/article/details/7015633 LINQ to SQL语句(1)之Where Where操作 适用场景:实现过 ...
- LINQ to SQL语句非常详细(原文来自于网络)
LINQ to SQL语句(1)之Where Where操作 适用场景:实现过滤,查询等功能. 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的子 ...
- LINQ To SQL 语法及实例大全
http://blog.csdn.net/pan_junbiao/article/details/7015633 http://blog.csdn.net/pan_junbiao/article/de ...
- 转载linq to sql 的详解
[转]LINQ To SQL 语法及实例大全 2011-11-26阅读38651 评论9 LINQ to SQL语句(1)之Where Where操作 适用场景:实现过滤,查询等功能. 说明:与SQL ...
- Linq to sql语法
LINQ to SQL语句(1)之Where Where操作 适用场景:实现过滤,查询等功能. 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的子 ...
随机推荐
- MyBatis中使用#{}和${}的区别
select * from table_name where id=#{id}; select * from table_name where id=${id}; 区别: 在动态SQL解析阶段,#{} ...
- Mac 10.12使用free命令(fish)
前提:要使用free命令,可以安装fish shell,然后在里面安装free插件. 安装fish brew install fish 安装free fish fisher free 使用 fish ...
- AspNetPager 控件使用
使用方法: 1.添加对AspNetPager.dll的引用 2.在页面上拖放控件 3. <%@ Register assembly="AspNetPager" namespa ...
- 用Docker下搭建GitLab
最近试了一下Docker,发现用它搭建服务十分方便,就用它搭建了一个gitlab练练手. 首先下载gitlab镜像: docker image pull gitlab/gitlab-c ...
- HDU 4123 Bob’s Race(树形DP,rmq)
Bob’s Race Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- Git 修复 bug 切换分支时,如何保存修改过的代码(即如何保存现场)?
工作除了开发最新的版本之外还要对原来的版本做例行的维护,修修补补.于是有了在两个分支之间游走切换的问题,最新改版的代码在分支 new 上,旧版本的代码在分支 old 上,我在 new 上开发了一半,忽 ...
- [Mac入门]如何在Mac下显示Finder中的所有文件
在Unix下工作,你可能需要处理一些“特殊“文件或文件夹,例如/usr,/bin, etcf,或一些"dot files"(如.bash_profile).但是Linux/Unix ...
- CSDN学院升级公告
CSDN学院将于2015年8月5日凌晨00:00-10:00进行停站升级,升级期间会影响大家的正常訪问和操作.给各位用户带来的不便敬请谅解. 升级结束后有不论什么问题请发邮件到webmaster@cs ...
- DXT 图片压缩(DXTC/DirectX Texture Compression Overview)
这两天在写 DDS 格式的解码程序.DDS 是微软为 DirectX 开发的一种图片格式,MSDN 上可以查到其文件格式说明: http://msdn2.microsoft.com/en-us/lib ...
- dispatch_group_async 使用详解
dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作.这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成的了.下面是一段例子 ...