多人操作数据的处理策略--讨论
用户故事如下:
电子商务后台管理,有多个客服人员可以操作同一个数据,当有的人正在对他进行修改的时候,其他人却觉得这个数据是垃圾数据,就直接删除了
如果是你,你在项目中如何处理这一小细节。
比如:当你载入订单修改的适合,有人已经把订单删除了,你去保存,不会有任何错误,反而操作成功
可以去数据库更新一个id不存在的字段,没有任何问题,只会
update qqinfo set name='fdfd
' where id=90;
Query OK, 0 rows affected
Rows matched: 0 Changed: 0 Warnings: 0
我们目前策略如下:
对订单管理,很多工作人员都可以载入订单
特殊处理这个操作数据库的方法,设置一个字段--是否可用
可用才执行操作,操作的适合先把这个字段设为不可用
欢迎大家提出好的想法
----------------
非常谢谢大家的回复,第六页的 不知云 给出了很好的解决策略 大家可以看看
这贴就结了吧<prop key="add*">PROPAGATION_SUPPORTS,-Exception</prop><prop key="modify*">PROPAGATION_SUPPORTS,-Exception</prop><prop key="remove*">PROPAGATION_SUPPORTS,-Exception</prop> 47 楼 wuxianjun 2011-05-26 codermouse 写道songlixiao 写道1. 数据库表增加一个时间戳字段。
2. 查询显示数据时,将世界戳字段查询出,并带到表单中。
3. 表单修改提交后,时间戳字段一同提交,保存前校验此数据时间戳是否与数据库一致,不一致说明已经被别人修改,不允许执行操作。
4. 如果时间戳一致,将数据的时间戳赋予当前时间,并保存到数据库。
原则是所有人都可以查询,但是修改则只有在最后一个版本上修改的才算有效。
+1
和 hibernate 乐观锁差不错。 48 楼 aaa5131421 2011-05-26 不知云 写道aaa5131421 写道感觉上面有些人想的太复杂了
其实这个问题就是个并发的悲观锁的问题
你update之前肯定需要select by id的吧,这时候 加上for update 不就可以了么,让这个方法序列化执行。
此时你再update 的时候就不会出现effect 0 rowcount的情况了。因为此时只有当前线程可以修改或者删除this row,其他人想做修改或删除操作,必须要等你完事才可以继续。其他线程是阻塞状态。
这时如果for update 没有成功,也就是在for update之前就已经被被人删掉的情况,你需在for update 这句下面做一个判断,如果select 出来为null,就throw 个 exception出去,展示异常页面给客户就行了。这样就完美了。
如果你select by i 的时候 加上for update,那第二个用户请求这个id数据的时候就会没响应,一直等待,直到第一个人执行完成为止。如果这样你的结果和本来有啥区别吗?还让第二个用户出现无限等待的状况。
等待的时间只是这个业务逻辑方法执行的时间,最大也只有几十毫秒而已,而且这个冲突的情况并不常出现,只是在当两个客户都同时点的时候才会出现的情况。这种情况下对客户的体验完全没有影响。说白了,这就是hibernate的悲观锁实现方式,当然,你也可以用乐观锁去做,当用乐观锁的时候,需要注意一点,如果你的业务逻辑操作中有任何一步无法回滚,就不能用乐观锁。比如你再业务逻辑中调用的第三方系统,乐观锁只能回滚数据库,回滚不了其他东西。 49 楼 mtnt2008 2011-05-26 kimmking 写道乐观锁,悲观锁
+1
用乐观锁的时候,要向避免楼主的问题,可以在编辑的时候添加一个锁定按钮
50 楼 javabrother 2011-05-26 加個flag 做標記,用軟刪除是否可以? 51 楼 不知云 2011-05-26 aaa5131421 写道不知云 写道aaa5131421 写道感觉上面有些人想的太复杂了
其实这个问题就是个并发的悲观锁的问题
你update之前肯定需要select by id的吧,这时候 加上for update 不就可以了么,让这个方法序列化执行。
此时你再update 的时候就不会出现effect 0 rowcount的情况了。因为此时只有当前线程可以修改或者删除this row,其他人想做修改或删除操作,必须要等你完事才可以继续。其他线程是阻塞状态。
这时如果for update 没有成功,也就是在for update之前就已经被被人删掉的情况,你需在for update 这句下面做一个判断,如果select 出来为null,就throw 个 exception出去,展示异常页面给客户就行了。这样就完美了。
如果你select by i 的时候 加上for update,那第二个用户请求这个id数据的时候就会没响应,一直等待,直到第一个人执行完成为止。如果这样你的结果和本来有啥区别吗?还让第二个用户出现无限等待的状况。
等待的时间只是这个业务逻辑方法执行的时间,最大也只有几十毫秒而已,而且这个冲突的情况并不常出现,只是在当两个客户都同时点的时候才会出现的情况。这种情况下对客户的体验完全没有影响。说白了,这就是hibernate的悲观锁实现方式,当然,你也可以用乐观锁去做,当用乐观锁的时候,需要注意一点,如果你的业务逻辑操作中有任何一步无法回滚,就不能用乐观锁。比如你再业务逻辑中调用的第三方系统,乐观锁只能回滚数据库,回滚不了其他东西。
你说的等待时间只有几十毫秒的时间,指的是一个事务完成业务逻辑的时间,而按照LZ的意思 是在查询出订单就必须加锁,并返回到页面上,再让客服点修改或者删除按钮这段时间,这个订单都必须锁住。这段时间肯定不是你说的几十毫秒。
我引用《heberate 悲观锁和乐观锁》这篇文章的,乐观锁第二段说明一下这段时间吧:
如一个金融系统,当某个操作员读取用户的数据,并在读出的用户数据的基础上进行修改时如更改用户帐户余额),如果采用悲观锁机制,也就意味着整个操作过程中(从操作员读出数、开始修改直至提交修改结果的全过程,甚至还包括操作员中途去煮咖啡的时间),数据库记录始终处于加锁状态,可以想见,如果面对几百上千个并发,这样的情况将导致怎样的后果。
原文地址:http://www.360doc.com/content/07/0327/10/11192_415808.shtml
所以你开始说的悲观锁肯定行不通。
用乐观锁的话,理论上倒是没问题,像你说的lz的系统有分布式的话就不能用了,而且 如果这样的事情真发生 肯定会耽误客服的时间。
我觉得,如果lz的系统不是集群,用的人也不多,可以像前面有朋友说的那样,我再改进一下,首先你的系统只需要加入一个过滤器servlet,你的系统的所有业务的jsp、.do之类的请求由这个servlet过滤。如果是你的查询单一订单的页面请求就加锁,如果是其他页面就解锁。
现在详细说说怎么加锁、解锁。这个过滤器里面就只需要 管理两个静态hashmap,其中一个以订单id作为key,员工id作为value,另一个相反。如果员工进入到打开订单页面,首先查看两个map是否当前订单被锁,如果有就直接跳到其他页面,如果没有 过滤器就把员工id和当前查询出来的订单id,分别作为key和value放入这两个map中。如果员工在其他页面,过滤器就先查询当前员工id是否锁有订单,没有就继续,有就删除两个map里这个员工和它曾经锁住的订单id。
我觉得这个方法比加乐观锁和悲观锁的方式都要强,你只需要添加一个过滤器并配置它。
如果想做得更友好一些,就在你的订单列表页面查询显示前,遍历你的map锁,看当前页面的 订单是否已经有人锁定,展示的时候分别表示。
这样做的好处是,如果十个客服要处理十张订单,而且任何人都有权利处理任何一张订单,当十个客服几乎同时打开列表页,能看到哪些已经被锁定,哪些还没有,最倒霉的那个人最多也就几次就能打开一张没有锁定的订单进行处理了。 52 楼 lai555 2011-05-27 顶顶顶顶顶顶顶顶顶顶 53 楼 yuehan 2011-05-27 JavaEye上面的。。。水平越来越低。哎! 54 楼 kangxdw 2011-05-28 楼主的应用场景只能让我想起一个词:秒杀。
只有这个场景下,会有那么多人对同一行进行操作。
有关这方面的技术,我个人觉得不是单单讨论锁能够解决的。
而且貌似当年秒杀这个概念刚炒热的时候,就有很多相关帖子。楼主可以翻出来看看啊。 55 楼 mht19840918 2011-05-28 很失败的架构,建议设计分单系统,避免锁机制 56 楼 mht19840918 2011-05-28 如果条件不允许,尽量考虑乐观锁,简单一点,version标志位