我想知道是否有任何方法可以使用像MySQL Cluster这样的分布式服务器在多个服务器上部署的asp.net Web应用程序上执行'锁定'。
例如,采用更新帐户余额的经典示例。没有两个请求应该同时更新相同的帐户余额。例如:
会员1帐户余额为100。
因此请求A将余额从100更新为200并保存 请求B将余额更新为150,并保存。
这些都恰好同时发生,因此丢失了信息,因为最终结果应该是250.
如何锁定,使得请求B必须等到请求A完成,直到它检索到余额。如果是单个进程,则可以通过应用程序范围的锁实现。但是,由于存在多个独立进程,这不会像服务器1那样工作,因为它没有被锁定,也没有服务器2
任何想法或最佳实践如何做到这一点?
答案 0 :(得分:4)
这就是交易的目的。
在TransactionScope
中包含需要原子化的操作,以便将它们注册到显式事务中。
这并不能完全解决陈旧数据的问题,因为如果其他并发用户使用旧值进行更新,一旦事务完成,您仍然会遇到问题。
解决方案是在表上使用一个字段,该字段会在行更改时发生更改 - 在SQL Server 2008中,这是rowversion类型。
如果rowversion
未更改,则只更新行 - 如果有,则通知用户并在不进行更新的情况下恢复当前值。
答案 1 :(得分:1)
在我创建的ORM中,我实现了一个锁定机制。每种类型的实体(如产品,客户等)都有2个附加字段:'LockedBy(int)'和'LockedAt(datetime)'。
LockedBy包含锁定实体的用户的ID,lockedAt包含锁定该实体的时间戳。
因此,当用户想要开始编辑实体时,代码将完成此过程:
从DB获取实体,'1'是ProductID
Product p = new Product(1);
将实体锁定到当前登录的用户
entity.Lock();
将实体保存/提交到数据库,Save()调用LockCheck(),如果登录用户有权保存实体,则返回true。
entity.Save();
用户有权在以下情况下保存实体:
(LockedBy < 0 || LockedBy == LoggedInUser.UserID || LockedAt < DateTime.Now.AddMinutes(-30))
注意:用户可以编辑超过30分钟前锁定的实体。如果另一个用户打开了实体(在编辑屏幕中),那么每15分钟它会弹出并询问用户是否仍在那里,如果是,它会执行一个请求,将LockedAt增加到Now(如滚动锁定)< / p>
此时没有其他用户可以编辑此实体!
代码/用户可以根据自己的意愿自由编辑这个实体,并确保它背后没有改变:)
用户停止编辑代码调用后:
entity.Unlock()
然后
entity.Save()
然后释放实体以供其他用户编辑
答案 2 :(得分:1)
这就是像Redis' pub/sub mechanism这样的系统可以帮助您的地方。它本质上是一种向订阅者(其他负载平衡的服务器)发送消息以让他们知道发生了事件的方法。 ServiceStack Redis client完全支持此功能。