我想解析文件并将内容传输到数据库中。为了加速所有文件应该并行解析 我有一个主线程,它逐行读取文件并创建 Runnable ,它们被提供给 ThreadPoolExecutor 。每个 Runnable 都有自己的 Session 。
每一行都包含一个客户端的唯一标识符,因此可以进行重复。系统尝试通过标识符在数据库中查找客户端 如果找不到想要同一客户端的线程之一,则需要创建客户端。我这里有一个“连接”点,其他线程必须等待允许创建客户端的线程。
c = (Client) s.get("Client", identfier);
if (c == null) {
CountDownLatch lock = isClientResolutionActive(identfier);
if (lock != null) {
lock.await();
LOGGER.info("Lock was released ... " + identfier);
c = (Client) s.get("Client", identfier);
}
}
if (c == null) {
c = createClient(...);
s.save(c);
s.flush();
removeClientResolutionActive(identfier);
}
为了同步它们,我在调用者类中创建了两个方法,一个方法专门用于检查是否有人在进行客户端创建并返回共享对象,另一个方法从列表中删除条目并通知所有等待线程
我在互联网上搜索了很多,并试图找到我的问题或类似问题而没有成功 此外,我不确定我应该使用哪个并发对象。经过研究,我决定使用 CountDownLatch 。它用 1 初始化。应该只有一个线程创建它。 (也许最好使用除 CountDownLatch 之外的东西,但我不知道是什么)
上述方法在地图上包含同步块,其中包含客户端的标识符和 CountDownLatch 的实例。
private CountDownLatch isClientResolutionActive(String identfier) {
synchronized (activeSearches) {
if (activeSearches.containsKey(identfier)) {
// Only create the CountDownLatch if there are multiple threads for
// that identfier
if (activeSearches.get(identfier) == null) {
activeSearches.put(identfier, new CountDownLatch(1));
}
return activeSearches.get(identfier);
} else {
LOGGER.info("Locked " + identfier);
activeSearches.put(identfier, null);
return null;
}
}
}
private void removeClientResolutionActive(String identfier) {
synchronized (activeSearches) {
CountDownLatch cl = activeSearches.get(identfier);
activeSearches.remove(identfier);
if (cl != null) {
LOGGER.info("Unlock " + identfier);
cl.countDown();
}
}
}
一般来说它工作正常,但有时我遇到问题,当释放(并删除)锁存器并且access-synchronized-variable-queue包含另一个线程来搜索已删除的条目(以检查是否有任何线程是它已经这样做了,它试图再次创建一个新的客户端。
18:02:55,611 [pool-1-thread-2] INFO LogImporter Unlock b42fcae346fbb2b1e3c544fb816de2c5
18:02:55,611 [pool-1-thread-3] INFO LogImporter Locked b42fcae346fbb2b1e3c544fb816de2c5
18:02:55,611 [pool-1-thread-4] INFO LogImporter Lock was released ... b42fcae346fbb2b1e3c544fb816de2c5
我想我必须改进同步,但我不知道如何。
一个想法是将客户端搜索移动到同步块或再次锁定数据库之前检查。
也许创建一个缓存或映射,它保存数据库中的所有已知客户端
或者只在应用程序的整个生命周期中使用一个 Session ?
提前感谢任何建议和提示。
答案 0 :(得分:2)
在并行线程中解析相同的文件不会增加速度,但只需要额外的资源
一个问题较少且效率较高的text2db优化包括:
批量插入数据库 - 像这样的mysql:
insert into urtable
values
('val1','val2'),
('val1','val2');
(从http://bytes.com/topic/sql-server/answers/585793-insert-into-using-select-values-inserting-multiple-rows偷来的例子 - 抱歉懒得自己做一个人)
更新----
从我的评论中我可能需要在解析文件时从数据库中获取数据。好吧,如果你必须这样做,你必须这样做。但是:尽量不要这样做。
首先:可以看到读取特定数据是否缓存。在狭隘的理解中,缓存只是通过任何启发式方法将磁盘数据移动到内存中(不知道发生了什么)。我个人试图避免这种情况,因为启发式可以对你不利。在更广泛的理解中,缓存是我在PLUS将数据从磁盘放入内存之前所描述的,您可以精确定位(例如,通过ID或任何过滤条件)。所以我仍然不喜欢这种狭隘的理解部分,而是先选择定义良好的数据。
其次:我的个人经历是这样的:如果你正在研究一个完全规范化的数据模型数据库,那么在文件解析中的读取操作经常会煮到“给我之前我所倾倒的主要密钥”。数据库。当您一次写入多行时,这似乎变得棘手。但是特别是在MySQL中,你绝对可以依赖'每个插入语句(甚至多行插入)是原子的',你从last_insert_id()获得ID,所以你可以跟踪到你之前写的所有记录。我很确定其他数据库系统也存在类似的“失败”。
第三:解析LARGE文件是我尝试作为工作运行的东西,只有一个技术用户触发,确保NOT>这些进程并行运行。否则,您需要解决所有类型的问题,从文件锁定开始进入会话权限读/写管理。因此,将此作为工作分类(至少在我的个人政策中)来分配大量的RAM - 取决于成本和速度的重要性。这意味着我甚至不愿意将100 K行关键字到id表加载到内存中。