我正在尝试将 50000 条记录插入到一个五节点 cassandra 集群中。我正在使用 executeAsync 以提高性能(减少应用程序端的插入时间)。我尝试使用多个批量大小的 Batchstatement,但每次我都会遇到以下异常。
Exception in thread "main" com.datastax.driver.core.exceptions.WriteTimeoutException: Cassandra timeout during write query at consistency ONE (1 replica were required but only 0 acknowledged the write)
at com.datastax.driver.core.exceptions.WriteTimeoutException.copy(WriteTimeoutException.java:54)
at com.datastax.driver.core.DefaultResultSetFuture.extractCauseFromExecutionException(DefaultResultSetFuture.java:259)
at com.datastax.driver.core.DefaultResultSetFuture.getUninterruptibly(DefaultResultSetFuture.java:175)
at
我插入了数据,即 10000、20000 到 40000 条记录,没有任何问题。以下是我写的java代码。
for (batchNumber = 1; batchNumber <= batches; batchNumber++) {
BatchStatement batch = new BatchStatement();
for (record = 1; record <= batchSize; record++) {
batch.add(ps.bind(query));
}
futures.add(session.executeAsync(batch));
}
for (ResultSetFuture future : futures) {
resultSet = future.getUninterruptibly();
}
其中 ps 是准备好的语句,batches 是批处理的数量,batchSize 是批处理中的记录数。
我无法理解问题的根本原因。我以为某些节点已关闭,当我检查时所有节点都在正常运行。
我应该如何调试异常?
最佳答案
我看到了一些错误:
- 您似乎正在尝试找出可以批处理在一起的查询的最大数量是多少。
- 您似乎认为批处理多个语句会给您带来某种性能提升。
- 您在循环中错误地重用了相同的准备语句。
- 您没有节流您的应用程序以一些摄取率。
- 您没有执行任何异常处理,例如在某些批处理失败时重试。
让我们重新开始。
- 批处理中的最大语句数应小于 10。越少越好。顺便说一下,批处理的总大小必须 小于 YAML 配置文件中的任何值。通常,如果您的批处理大于 5kb,那么您的日志中会出现一条警告。如果您的批处理大于 50kb,则批处理将失败。您可以调整这些值,但您应该记住
BATCH
会使协调器节点过载。批处理越大(无论是 kb 还是语句数),协调器的过载就越大。 - 将不相关的语句批处理在一起不会有任何好处。相反,您实际上会失去 性能。这是由于
BATCH
的工作方式所致。选择一个节点来协调所有语句,并且该节点将负责所有语句。通常协调器是根据第一条语句选择的,如果你的语句命中多个节点,你的协调器也需要协调属于不同节点的东西。相反,如果您触发多个单独的 异步查询,每个节点将只负责它们的 语句。您会将过载分散到所有集群节点上,而不是在一个节点上施加压力。 - 您以错误的方式使用了准备好的语句。您应该添加一个
new BoundStatement(ps).bind(xxxx)
语句。无论如何,这很容易解决。 - 如果您有大量查询要运行,那么您会一直运行它们。您将耗尽您的应用程序内存,因为它会不断向列表中添加
future
,并且最终会因为 OOM 错误而被终止。此外,您并没有为您的集群提供实际摄取您向其发射的所有数据的可能性,因为您可以比集群摄取的速度更快地发射数据。您需要做的是限制列表中的 future 数量。最多保持一些值(例如 1000)。要执行此类任务,您需要使用.getUninterruptibly
inside 移动最终循环。这样,您可以降低摄取速率,并会看到超时异常计数减少。并且根据应用程序,减少超时异常意味着更少的重试,因此更少的查询、更少的开销、更好的响应时间等...... - 在
Future
的列表中使用.getUninterruptibly
进行循环是很好的,但是您应该记住的是,当您的集群是 重载,您将超时。此时,您应该捕获异常并处理,无论是重试,还是重新抛出,无论是其他什么。我建议您围绕幂等 查询设计模型,这样我就可以重试失败的查询,直到它们成功,而不必担心重试后果(这也可能发生在驱动程序级别!)。
希望对您有所帮助。
关于java - 如何解决cassandra中的写超时异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40631203/