Cassandra - 将PreparedStatement与ListenableFuture一起使用

时间:2015-12-09 16:32:08

标签: java cassandra

我尝试使用 ListenableFuture 对Cassandra群集进行异步写入,如下所示:

private static Cluster cluster = null;
private ListeningExecutorService executorService;
private PreparedStatement preparedStatement;
private Session session = null;

... 

executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(POOL_SIZE));
...

public void writeValue(Tuple tuple) {
    ListenableFuture<String> future = executorService.submit(new Callable<String>() {
       @Override
       public String call() throws Exception {
           if(session == null) {
               session = getCluster().connect("dbname");
               preparedStatement = session.prepare(queryString);
           }

           try {
               BoundStatement boundStatement = preparedStatement.bind(tuple values);
               session.execute(boundStatement);
           } catch(Exception exception) {
                // handle exception
           }

           return null;
       }
    });

如果我将 POOL_SIZE 设置为1,一切正常 如果我将 POOL_SIZE 设置为&gt; 1我得到如下错误:

  

引起:com.datastax.driver.core.exceptions.InvalidQueryException:尝试执行未知的准备查询:0x75c5b41b9f07afa5384a69790503f963。您可能已经使用了使用另一个Cluster实例创建的PreparedStatement。

所以我sessionpreparedStatement进入当地的变种。然后我收到有关Re-preparing already prepared query ...的警告以及每次创建新会话的警告。

我想尽可能多地重用。我做错了什么,我有什么选择?

使这个类静止会有帮助吗?

2 个答案:

答案 0 :(得分:5)

这里有各种各样的竞争条件,并且执行不是线程安全的。

ClusterSessionPreparedStatement中的每一个都是应用程序范围的单例,即。您只需要一个(PreparedStatement的每个查询一个)。

但是,您正在重新创建Session并可能多次准备PreparedStatement

唐&#39;吨。在构造函数或仅运行一次的某个位置初始化Session一次,并同时准备语句。然后在适当的地方使用SessionPreparedStatement

使用单线程执行程序,一切都像同步一样运行。当您添加更多线程时,其中许多可能会调用

session.prepare(queryString);

同时。或者你在这里使用的PreparedStatement

BoundStatement boundStatement = preparedStatement.bind(tuple values);
session.execute(boundStatement);

可能与您初始化的

不同
preparedStatement = session.prepare(queryString);

甚至在同一执行线程内。或者您可能尝试使用与用于初始化它的PreparedStatement不同的Session执行static final String OFFSETS_BC_FUSE ="CREATE TABLE IF NOT EXISTS "+OFFSETS_B_FUSE+"("+ID+" integer primary key autoincrement, "+OFFSET_VALUE+" INT);";

Here are some things you should be doing when using CQL drivers.

  
      
  1. 准备好的语句是绑定在一个会话上还是可以在另一个会话中使用?

         

    预准备语句派生自特定会话实例。   因此,当您准备一个语句并将其发送到服务器时,它   被发送到与此会话实例关联的集群   用。

  2.   

Session州的javadoc

  

会话实例是线程安全的,通常是单个实例   足够的每个应用程序。

答案 1 :(得分:2)

您可能还想使用驱动程序的异步API。而不是调用execute(它将在查询期间阻塞您的线程),而是调用executeAsync并在生成的future上注册回调以处理结果。

如果该回调很昂贵并且您不想阻止驱动程序的内部I / O线程,那么您可以提供自己的执行程序:

ListenableFuture<ResultSet> future = session.executeAsync(statement);
Futures.addCallback(future, new FutureCallback<ResultSet>() {
    public void onSuccess(ResultSet rs) { ... }
    public void onFailure(Throwable t) { ... }
  }, 
  executorService);
文档中的

This page提供了一些关于异步编程的技巧。