【发布时间】:2011-02-03 03:21:32
【问题描述】:
假设您从数据存储区检索一组记录(例如:select * from MyClass where reserved='false')。
如何确保其他用户未设置保留仍为假?
我查看了 Transaction 文档,对 google 的解决方案感到震惊,即捕获异常并在循环中重试。
我缺少的任何解决方案 - 很难相信在这种环境中没有办法进行原子操作。
(顺便说一句 - 我可以在 servlet 中使用“syncronize”,但我认为它无效,因为无法确保 servlet 对象只有一个实例,不是吗?同样适用于静态变量解决方案)
你知道怎么解决吗?
(这是谷歌解决方案:
http://code.google.com/appengine/docs/java/datastore/transactions.html#Entity_Groups
看看:
Key k = KeyFactory.createKey("Employee", "k12345");
Employee e = pm.getObjectById(Employee.class, k);
e.counter += 1;
pm.makePersistent(e);
'这需要一个事务,因为在此代码获取对象之后,但在保存修改的对象之前,另一个用户可能会更新该值。如果没有事务,用户的请求将使用其他用户更新之前的计数器值,保存将覆盖新值。通过事务,应用程序被告知其他用户的更新。如果实体在事务期间更新,则事务失败并出现异常。应用程序可以重复事务以使用新数据'
可怕的解决方案,不是吗?
【问题讨论】:
-
不,这并不可怕。一般原则称为“乐观锁定”,在争用相当罕见的情况下,它比互斥(“悲观锁定”)更有效。它对大规模分布式实现也更加友好。
-
如果您有许多客户试图同时预订一个房间,并且在每次预订之前您需要执行一个 select 语句,那就太可怕了!
-
更不用说在选择之后你持有所有未预订的房间,所以当时任何其他客户请求都会导致异常......锁定机制不是我的发明......它是微不足道的需要...
-
还不错。在互斥的情况下,除了一个之外,所有这些客户都将无所事事,而在 Google 的解决方案中,除了一个之外,所有客户都处于一个循环中,抛出异常并再次尝试。 Somebody 无论哪种方式都成功,因为只有在有人写了一些东西(即成功预订房间)时才会抛出异常。这是一种锁定机制,它只是你不习惯的那种。在节点之间有损通信的分布式系统上实现互斥绝非易事。我只能建议你顺其自然。
-
不,锁也不是更好,因为它们总是施加开销,即使在绝大多数没有冲突的情况下也是如此。乐观并发仅在检测到实际冲突时会导致额外开销。
标签: google-app-engine servlets