概述
- 何为并发?并发是指两个或多个事件在同一时间间隔内发生。
- 程序去预防并发的基本思路就是讲并行执行改为串行执行,也就是拉队列。
- 数据库针对并发的处理一般采用悲观并发处理和乐观并发处理。
- 数据库悲观锁是一种串行运算,假定会发生并发冲突,屏蔽一些可能违法数据完整性的操作。悲观锁假定其他用户企图访问或者更改对象的效率是很高的,所以会在程序开始改变对象之前就将对象锁住,知道提交了所做的更改之后才释放所。缺点是不论是页锁还是航所,加锁时间可能会很长,这样可能会长时间锁定一个对象,限制其他对象访问,并发访问性不好。
- 数据库乐观锁是一种并行运算,只是在提交操作时检查是否违反数据完整性。乐观锁不能解决脏读问题。乐观锁假定不会发生并发冲突,因此知道程序准备提交所做的更改时才将对象锁住,当程序读取以及改变对象时并不加锁。乐观锁加锁时间要比悲观锁短,可以用于较大锁力度获得较好的并发访问性能。由于用户提交时,数据库对象可能已经发生了变化,这样用户不得不重新读取对象,增加了用户读取对象的次数。
实践
悲观者方法
加了updlock锁,锁定了更新操作。
ALTER PROCEDURE [dbo].[beiguansuo] AS BEGIN declare @age int begin tran select @age=age from Student with(updlock) where FirstName='fengjie' waitfor delay '00:00:05' update [dbo].[Student] set Age=@age-1 where FirstName='fengjie' commit tran END
乐观者方法
数据库列有一个类型为“timestamp”,是一个时间戳,可以理解为版本号。一旦有访问者修改了该数据,版本号的值就会发生改变。我们在更改数据之前首先获取该记录的版本号,然后在记录更新的时候通过主键和版本号同时去更新。但是这样会出现一个问题,如果我们拿到版本号后,数据已经被修改了,这时候update的影响行数为0。所以在这里程序需要根据返回行做特殊处理,可以提醒用户刷新后再修改,也可以递归去Update,根据具体的业务场景选择不同的方法。
ALTER PROCEDURE [dbo].[leguansuo] AS BEGIN declare @rowversion timestamp declare @age int select @rowversion=RowVersion,@age=Age from Student update Student set Age=@age-1 where FirstName='fengjie' and RowVersion=@rowversion END
Main(){ var upRows = executeLeguansuo(); int retry = 10; while (upRows==0&&retry>0) { Thread.Sleep(500); retry--; upRows = executeLeguansuo(); } }
private int executeLeguansuo() { string sql = "exec leguansuo"; using (myEntities context = new myEntities()) { int upRows = context.Database.ExecuteSqlCommand(sql, new object[] { }); if (upRows==0) { executeLeguansuo(); } return upRows; } }
EF框架如何避免数据库并发
在EF DataBaseFirst中,只需要设置类型为TimeStamp版本号的并发模式属性即可。默认是None,设置为Fixed即可。
在线系统发生并发时,程序会抛出异常,我们捕获这个异常然后结合业务场景选择不同的处理方式即可。
public class SaveChangesForBF : BingFaTestEntities { public override int SaveChanges() { try { return base.SaveChanges(); } catch (DbUpdateConcurrencyException ex)//(OptimisticConcurrencyException) { //并发保存错误 return -1; } } }
本文有内容引用自:http://www.cnblogs.com/chenwolong/p/BF.html