b/s的并发事务控制
简单三层,在业务层调用数据访问层的UploadModel(Entity)方法更新模型
UploadModel拼接一个形似update model (字段1,字段2........) values (参数1,参数2.....)
现在业务增多后经常会有两个请求同时访问,在执行UploadModel方法时获取的相同Entity,在执行完UploadModel后后一个执行的就把前一个执行结果覆盖
后来在在拼接的语句中加入了一个时间戳判断 where timestamp=@timestamp
这样做避免掉了前面提到的bug,但是却经常提示更新失败。
大家有没有更好的方法?
动漫人物
浏览 423回答 3
3回答
-
侃侃尔雅
并发更新方式有两种:
一种是悲观并发(pessimistic concurrency)。就是先锁定数据,再读取数据,一致后再更新,然后解锁。(可能需要在锁定、读取数据后先和本地数据比较,如果不一致先更新本地数据)。这样通过锁定保证了更新的数据是最新的版本。锁定可以通过数据库提供的行锁定(甚至是表锁定)功能,或者是根据自己的业务逻辑自己做一个锁定表来处理。
另一种是乐观并发(optimistic concurrency)。就是不需要用锁,而是使用版本戳,比如sql server的timestamp类型字段。这样就可以和本地的数据的版本进行比较,不一致则更新失败。失败后需要自己根据业务逻辑来处理,有三种处理方式,一种是server win,就是以数据库当前状态为准,本次更新失败。第二种是client win,就是忽略版本戳再次提交,覆盖掉之前的版本。第三种是merge,需要读最新数据,根据业务来和失败的提交合并,然后在更新数据。这里后两种处理方式在更新的时候还是可能会失败,可能需要根据业务来设计重试的次数,和最终失败的处理。
悲观并发的逻辑更容易写,但是由于锁定,性能会降低。乐观并发需要考虑的情况更多,但是也更灵活。现在一般更多的考虑采用乐观并发的方式。
像是更新库存的业务,我建议使用乐观并发(也就是你目前使用的办法),在失败处理时使用merge,就是失败的时候先读最新库存,然后根据业务加减,再次提交。这时仍然可能失败,要根据系统的并发程度来确定一个重试次数,比如3次,每次都是先读最新库存,然后根据业务加减,再次提交。如果失败超过了重试次数,就放弃这次操作,提示用户失败。
如果再严格一些,更新库存的操作是需要考虑幂等的。就是如果用户可以在UI上本应该一次提交的地方能够进行多次提交(比如网页上刷新提交页面),那么多次提交的结果应该和一次提交一致(也就是防止重复提交)。一般的方法是可提交的页面生成时分配一个唯一id,提交时会同时提交这个id,如果提交成功,这个id就失效了,再次使用这个id的提交就忽略。
-
森林海
简单地设置一个版本号,如 version=1update table set Name='wtf', version+=1 where id=11 and version=1
如果execute 返回的是0 那么就是更新失败。 返回1 就是更新成功,其实和楼上两位说的是一样的。
打开App,查看更多内容