Cassandra如何处理阻塞datastax java驱动程序中的execute语句

时间:2016-01-22 14:41:34

标签: cassandra datastax datastax-java-driver

阻止com.datastax.driver.core.Session执行fethod

public ResultSet execute(Statement statement);

评论此方法:

  

此方法会阻止,直到收到至少一些结果为止   数据库。但是,对于SELECT查询,它不能保证   结果已全部收到。但它确实保证了一些   已从数据库收到响应,特别是   保证如果请求无效,将抛出异常   通过这种方法。

来自com.datastax.driver.core.Session

的非阻塞执行方法
public ResultSetFuture executeAsync(Statement statement);
  

此方法不会阻止。它会在查询完成后立即返回   传递给底层网络堆栈。特别是从...返回   此方法不保证查询有效或具有偶数   已提交到活动节点。任何与失败有关的例外   访问{@link时将抛出查询   ResultSetFuture}。

我有关于他们的02个问题,因此如果你能帮我理解它们会很棒。

让我们说我有100万条记录,我希望所有记录都能到达数据库(没有丢失)。

问题1:如果我有n个线程,则所有线程都需要将相同数量的记录发送到数据库。所有这些都继续使用阻塞执行调用向cassandra发送多个插入查询。如果我增加n的值,它是否也有助于加快我需要将所有记录插入cassandra的时间?

这会导致cassandra出现性能问题吗? Cassandra是否必须确保对于每个插入记录,群集中的所有节点应立即知道新记录?为了保持数据的一致性。 (我认为cassandra节点甚至不会考虑使用本地机器时间来控制记录插入时间。)

问题2:使用非阻塞执行,我如何确保所有插入成功?我知道的唯一方法是等待ResultSetFuture检查插入查询的执行。有什么更好的办法吗?非阻塞执行更容易失败然后阻止执行吗?

非常感谢您的帮助。

2 个答案:

答案 0 :(得分:6)

  

如果我有n个线程,则所有线程将具有发送到数据库所需的相同数量的记录。所有这些都继续使用阻塞执行调用向cassandra发送多个插入查询。如果我增加n的值,它是否也有助于加快我需要将所有记录插入cassandra的时间?

在某种程度上。让我们稍微分离一下客户端实现细节,并从“并发请求数”的角度看待事情,因为如果你使用executeAsync,你不需要为每个正在进行的请求都有一个线程。在我的测试中,我发现虽然拥有大量并发请求有很多价值,但是有一个阈值,其回报递减或性能开始下降。我的一般经验法则是(number of Nodes * native_transport_max_threads (default: 128) * 2),但您可以或多或少找到更优的结果。

这里的想法是,将cassandra一次处理的请求排入队列的次数并不多。在减少飞行请求数量的同时,您可以限制驱动程序客户端和cassandra之间连接的不必要拥塞。

  

问题2:使用非阻塞执行,我如何确保所有插入成功?我知道的唯一方法是等待ResultSetFuture检查插入查询的执行。有什么更好的办法吗?非阻塞执行更容易失败然后阻止执行吗?

通过get等待ResultSetFuture是一条路线,但如果您正在开发完全异步应用程序,则希望尽可能避免阻塞。使用番石榴,你最好的两种武器是Futures.addCallbackFutures.transform

  • Futures.addCallback允许您注册在驱动程序收到响应时执行的FutureCallbackonSuccess在成功案例中执行,否则onFailure

  • Futures.transform可让您有效地将返回的ResultSetFuture映射到其他内容。例如,如果您只想要1列的值,则可以使用它将ListenableFuture<ResultSet>转换为ListenableFuture<String>,而不必在ResultSetFuture上的代码中阻塞,然后获取String值。

在编写dataloader程序的上下文中,您可以执行以下操作:

  1. 为了简单起见,请使用Semaphore或其他具有固定数量许可的构造(这将是您的最大飞行请求数)。每当您使用executeAsync提交查询时,都会获得许可。您实际上只需要1个线程(但可能需要引入一个#cpu cores size of pool),它从Semaphore获取许可并执行查询。在获得许可证之前,它只会阻止获取。
  2. 使用Futures.addCallback表示从executeAsync返回的未来。回调应在Sempahore.release()onSuccess两种情况下调用onFailure。通过发布许可证,这应该允许您在步骤1中的线程继续并提交下一个请求。
  3. 为了进一步提高吞吐量,您可能需要考虑使用BatchStatement并批量提交请求。如果您保持批量较小(50-250是一个很好的数字),并且批量中的插入都共享相同的分区键,这是一个不错的选择。

答案 1 :(得分:0)

除了上述答案之外,

看起来像execute()调用了executeAsync(statement).getUninterruptible(),因此无论您是使用execute()管理自己的“ n个线程池”还是阻塞自己,直到执行完成最多n个正在运行的线程或使用executeAsync ()在所有记录上,cassandra端的性能应大致相同,具体取决于执行时间/计数+超时。

它们的执行都将运行从池借来的连接,每个执行在客户端都有一个streamId,并在将来对此streamId的响应返回时通过将来通知您,受限于客户端每个连接的总请求数和有限的总请求数通过选择在每个节点上执行您的请求的读取线程,任何更大的数字将被缓冲在队列中(不受阻塞),该队列受连接maxQueueSize和maxRequestsPerConnection的限制,任何大于此数量的数字都会失败。这样做的好处是executeAsync()不会在每个请求/执行的新线程上运行。

因此,必须限制通过execute()或executeAsync()运行的请求数量,在execute()中,您要避免超出这些限制。

在性能方面,您将开始看到每个节点都无法承受的代价,因此具有良好大小池的execute()对我来说很有意义。更好的是,使用反应式体系结构来避免创建这么多线程,而这些线程除了等待之外什么都不做,因此大量线程将导致客户端上上下文切换的浪费。对于较少数量的请求,通过避免线程池,executeAsync()会更好。

DefaultResultSetFuture将来=新的DefaultResultSetFuture(...,makeRequestMessage(statement,null));
新的RequestHandler(this,future,statement).sendRequest();