【发布时间】:2019-01-07 13:49:05
【问题描述】:
我正在使用 Java 的并发库 ExecutorService 来运行我的任务。写入数据库的阈值是 200 QPS,但是这个程序在 15 个线程的情况下只能达到 20 QPS。我尝试了 5、10、20、30 个线程,它们甚至比 15 个线程还慢。代码如下:
ExecutorService executor = Executors.newFixedThreadPool(15);
List<Callable<Object>> todos = new ArrayList<>();
for (final int id : ids) {
todos.add(Executors.callable(() -> {
try {
TestObject test = testServiceClient.callRemoteService();
SaveToDatabase();
} catch (Exception ex) {}
}));
}
try {
executor.invokeAll(todos);
} catch (InterruptedException ex) {}
executor.shutdown();
1)我检查了运行这个程序的linux服务器的CPU使用率,使用率分别为90%和60%(它有4个CPU)。内存使用率仅为 20%。所以CPU和内存还是不错的。数据库服务器的 CPU 使用率很低(大约 20%)。什么可以阻止速度达到 200 QPS?也许这个服务电话:testServiceClient.callRemoteService()?我检查了该调用的服务器配置,它允许每秒进行大量调用。
2) 如果 ids 中的 id 计数超过 50000,使用 invokeAll 是不是一个好主意?我们是否应该将其拆分为更小的批次,例如每批次 5000 个?
【问题讨论】:
-
是否可以发布正在执行的查询类型? IE。他们都在访问或修改同一行或表吗?它们都是读取、写入还是两者的组合?
-
@M.Deinum "线程数受限于你拥有的核心数...如果远程调用需要很多时间,核心在等待响应,不会被可用于其他任何事情。” - 这至少具有误导性。当然,您可以在任何时间点拥有比 CPU cores 更多的 threads; 尤其是当一个线程需要等待 I/O(网络、磁盘、...)(“阻塞”)时,另一个(“可运行”)线程将被换入以使用 CPU与此同时。因此,特别是如果您在一个线程中有相对较多的等待,您希望使用 更多 个线程来保持 CPU 忙碌。
-
这取决于您的 CPU 架构。此外,更多的线程最终将导致换出线程,这在 java 中将导致相当大的性能损失,因为堆栈和其他所有内容都需要在暂停和取消暂停线程时进行序列化/反序列化。此外,为 IO 繁重的操作使用大量线程也不是一个好的解决方案(它适用于 CPU 密集型操作)。因此,它确实不受内核数量的限制,但在只有 16 个内核的情况下创建 200 个线程可能会使您的程序比仅使用 20 个线程时更慢。
-
@M.Deinum “堆栈的性能受到影响,其他所有内容都需要在暂停和取消暂停线程时进行序列化/反序列化。” - 我不知道你在说什么。 Java 线程 1:1 映射到操作系统线程,根本不存在诸如“序列化/反序列化”堆栈或“暂停”线程之类的事情。正如我所说,如果每个线程都可以使用 100% 的 CPU,那么您将无法通过使用多个线程来获得性能。多线程只是屏蔽 I/O 延迟的方法。
-
@M.Deinum 现代 CPU 上的上下文切换会产生非常小的损失。更大的问题通常是内存访问的局部性丢失,这可能或多或少地使数据缓存无效。但这与Java无关,在大多数情况下也不是什么大问题。例如。即使惩罚总共是 1ms,等待 5ms 等待网络响应仍然是切换到另一个线程的好时机。
标签: java multithreading executorservice