如何避免来自同一用户的并发请求的排序问题?

时间:2014-11-09 19:22:58

标签: multithreading concurrency race-condition

假设您有一个系统同时处理请求,最终结果是在数据库中存储字段。现在假设出现以下情况,其中id请求ID user是进行调用的用户,val是存储在数据库中的值,t是时间:

Client                          Server                       Database
   | id=1, user=x, val=100, t=1   |                             |
   |----------------------------> |                             |
   | id=2, user=x, val=50,  t=2   |                             |
   |----------------------------> | id=2, user=x, val=50, t=3   |
   |                              |---------------------------->| 
   |                              | id=1, user=x, val=100, t=4  |
   |                              |---------------------------->| 

问题是同一个用户几乎同时发出两个请求,但是由于服务器中任务的无序执行,最后一个请求被处理,这个值被插入到数据库中,而第一个请求来到并覆盖此数据,最终使数据库处于不一致状态。

我想到了两个解决方案:

  1. 在数据库中添加creation_time字段,仅在objectToBeInserted.creationTimestamp > objectAlreadyInDb.creationTimestamp时更新。但这种用途有限;假设您应该向另一个系统发出请求而不是数据库,因此您无法查询数据库。

  2. Map相关联的userId用户semaphore。当请求到达时,检查是否采用了与该用户对应的信号量;如果被采取然后等待继续。插入数据库后,释放信号量。然而,这具有无法同时处理来自同一用户的请求的限制,并且如果有许多用户但是在快乐情况下更快(仅一个数据库调用),则可能存在内存问题。

  3. 这些解决方案是否足够好,或者是否有更好的解决此问题的典型解决方案?

1 个答案:

答案 0 :(得分:1)

最典型的解决方案是添加序列号。或者您可以使用MQ软件来保证有序处理(这些通常在内部使用序列号来保证排序)。使用MQ可能是一个好主意,因为如果丢失消息(如果丢失消息#2,#3,#4不会被处理等),MQ很容易停止处理,以及MQ有防止消息丢失的机制。

编辑:此article中有一个很好的讨论,解释了不同的设计。对于您的问题,第三个端点必须提供事务,但只要它不是,它就不必是数据库。