我有以下问题:
我有一个多线程服务器端应用程序,每个请求都执行一个新线程(标准情况)。根据此请求,应用程序会检查所需数据是否已缓存在DB中。如果是,则获取它并将其发送回客户端。如果不是,则调用远程服务,响应存储在DB中,然后获取以返回给客户端。
该服务需要一段时间来计算数据,并且由于对我的应用程序的请求是异步执行的,因此我会陷入一个请求检查数据库的陷阱,看到那里没有任何内容,然后调用该服务。然而,同时具有相同输入的另一个请求将检查DB,并且由于服务需要一段时间,因此仍然没有任何内容......因此它将启动对服务的另一个调用。结果,数据被写入DB两次,这使得任何成功操作变得紧张,依赖于它的唯一性(不能有两个具有相同数据的记录)
我应该选择什么样的解决方案? 一种想法是在DB中放置特定的UNIQUE字段约束。这样,即使应用程序尝试写入两次,数据库也会拒绝它,并且应用程序将抛出异常。但是,该服务仍将执行多次。
我想到的另一个解决方案是同步调用Web服务的方法。这样,在我的应用程序的每个连续请求将在上一个操作完成之前被置于等待堆栈中。这样,如果另一个具有相同输入数据的请求进入,而第一个请求仍在等待来自远程服务的结果,则它将等待操作完成。然后,当第二个请求检查数据库时,数据已经存在,并且不需要再次调用服务。这也将防止在DB中具有多个相同的记录。这样做的问题是我的服务器应用程序的速度将不可避免地降低,因为每个请求都必须等待,即使是通常不应该等待的那些(据说我们已经在数据库中有数据的那些)
还有其他建议吗?我被卡住了。如何实现某种条件同步?
答案 0 :(得分:2)
只需实施此算法:
这样,只有在数据库中不存在数据时才等待锁定。获取锁的第一个并发线程是唯一一个调用该服务的线程,而所有其他线程从数据库获取数据。
这不应该阻止您正确设计数据库,并使用唯一的主键来处理数据。如果你有一组服务器,它仍然是需要的。
答案 1 :(得分:1)
您可以同步计算部分,即使用双重检查锁定:
boolean dataExists = checkDB();
if( !dataExists ) {
synchronized( someSharedObject ) {
//another thread might have been waiting too and now has put the data into the DB
//thus check again
if( !checkDB() ) {
calculateDataAndWriteToDB();
}
}
}
//do whatever you want with the existing data
答案 2 :(得分:0)
您描述的用例看起来像典型的缓存用例。在缓存中,数据通常具有一些键和值,其中键定义唯一性。如果其他请求更新数据,则不会创建新记录,而是使用相同的密钥更新现有记录。如果你这样做,你支付的价格只是调用服务两次,但它不应该影响逻辑。那么你是否有理由每次创建新记录而不是更新?
我强烈反对在您的用例中使用任何方法同步,因为它会自动使您的解决方案无法在多个框中进行扩展。
答案 3 :(得分:0)
在您的第一个方法中,DB中的UNIQUE字段约束将无法解决您的问题,因为endUser必须等待远程服务调用返回,并且将不再使用资源。
另一种方法是取消具有相同输入数据的请求,并在一段时间后告诉endUser刷新请求。但这不好,因为它会增加服务器停机时间。
所以你离开了第二种方法。我建议你同步你的输入数据。 或者您可以检查输入数据是否相同然后等待,直到返回请求,然后从db读取输出。