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

Multithreading 如何避免来自同一用户的并发请求的排序问题?,multithreading,concurrency,race-condition,Multithreading,Concurrency,Race Condition,假设您有一个同时处理请求的系统,其最终结果是在数据库中存储一个字段。现在假设出现以下场景,其中id请求id,user是进行调用的用户,val是存储在数据库中的值,t是时间: Client Server Database | id=1, user=x, val=100, t=1 | | |-----------------------

假设您有一个同时处理请求的系统,其最终结果是在数据库中存储一个字段。现在假设出现以下场景,其中
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  |
   |                              |---------------------------->| 
问题在于,同一用户几乎同时发出两个请求,但由于服务器中任务的无序执行,最后一个请求首先被处理,此值被插入数据库中,而第一个请求出现并覆盖此数据,最终使数据库处于不一致状态

我想到了两种解决方案:

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


  • 信号量关联的
    userId
    的用户a
    Map
    。当请求到达时,检查是否使用了与该用户对应的信号量;如果已采取措施,则等待,否则继续。插入数据库后,释放信号量。但是,这有一个限制,即不能同时处理来自同一用户的请求,并且如果有许多用户,也可能有内存问题,但在令人高兴的情况下,速度更快(只有一个db调用)


  • 这些解决方案是否足够好,或者对于这个问题是否有更好的典型解决方案?

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


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

    “但是这有一个限制,即不能同时处理来自同一用户的请求”-这不是您想要的吗?如果顺序很重要,那么您希望按顺序处理请求。顺便问一下,请求id是请求中保证正确排序的实际参数吗?因为我认为您不能保证第二个请求最后到达服务器。与其使用不精确的时间值(可以在时间上向前和向后跳跃),不如让客户端生成更新序列号,并仅在收到更高的数字时写入。这不管用吗?@mp_uu处理顺序无关紧要;仅保存在db mathers中的内容。@m3th0dman是的,但请想象一下客户端消息没有id属性的情况。你如何判断哪封邮件是最先发送的?如果客户端同时发送消息,则第二条消息可能首先到达。即使第二条消息是第二条发送的,如果客户端使用单独的tcp连接发送它们,那么消息也可能首先到达。还是我误解了消息是按顺序到达的?@mp_uu消息是按顺序到达的,由协议保证。服务器未按顺序处理邮件。我无法更改客户端或协议。@m3th0dman:您仍然可以在服务器的入口点分配序列号。如果客户端是单线程和同步的,这仍然有效。这仍然有一个限制,即您需要查询当前序列号。如果第三个端点是另一个系统而不是数据库,那么仍然可以实现吗?您可以将序列号存储在服务器内存中(在请求到达时将序列号附加到请求,并在内存中存储每个客户端的最高序列号)。如果服务器崩溃,失去序列号,那么所有连接和未处理的请求都将被丢弃。