我有一个系统,可以使用大约30个线程将大量的任务分成小任务。当每个单独的线程完成时,它将计算结果持久存储到数据库中。我想要实现的是让每个线程将其结果传递给一个新的persisance类,该类将在自己的线程中运行时执行一种双缓冲和数据持久性。
例如,在100个线程将其数据移动到缓冲区的持久性类之后,持久性类交换缓冲区并将所有100个条目保留到数据库。这将允许使用预准备语句,从而减少程序和数据库之间的I / O.
这种多线程双缓冲是否存在模式或良好示例?
答案 0 :(得分:4)
我见过这种模式称为异步数据库写入或后写模式。这是分布式缓存产品(Teracotta,Coherence,GigaSpaces等)支持的典型模式,因为您不希望缓存更新也包括将更改写入底层数据库。
此模式的复杂性取决于您对丢失数据库更新的容忍度。由于完成工作和将结果写入数据库之间的延迟,您可能会因错误,电源故障而丢失更新......(您可以了解相关信息)。
我建议使用某种队列将完成的结果写入数据库,然后在100个批次(使用您的示例)或一段时间后处理它们。使用时间延迟的原因是为了处理不能被100整除的结果集。
如果您对弹性/耐久性没有要求,那么您可以在同一过程中完成所有这些操作。但是,如果您无法容忍任何损失,那么您可以使用持久性JMS队列替换in-vm队列(更慢但更安全)。
答案 1 :(得分:1)
为了降低同步开销,请使用本地线程(对于每个计算线程)来构建批量结果。达到一定数量的结果后,将批处理排入阻塞队列。使用ArrayBlockingQueue来支持持久性类,因为您可能不希望内存使用变得无限制。您可以使用多个数据库写程序线程来获取结果组并将它们保存到数据库中。
class WriteBehindPersister {
ThreadLocal<List<Result>> internalBuffer;
static ArrayBlockingQueue<List<Result>> persistQueue;
static {
persistQueue = new ArrayBlockingQueue(10);
new WriteThread().start();
}
public WriteBehindPersister() {
internalBuffer = new ThreadLocal<List<Result>>();
}
public void persist(Result r) {
List<Result> localResult = internalBuffer.get();
localResult.add(r);
if (localResult.size() > max) {
persistQueue.put(new ArrayList(localResult));
localResult.clear();
}
}
class WriteThread extends Thread {
public void run() {
while (true) {
List<Result> batch = persistQueue.take();
beginTransaction();
for (Result r : batch) {
batchInsert(r);
}
endTransaction();
}
}
}
}
此外,您可以使用执行程序服务(而不是单个写入线程)在使用多个数据库连接的权衡中同时将多个批次持久保存到数据库。如果您的驱动程序支持,请确保使用JDBC批处理API。