Java 插入性能和插入稳定性较差的Cassandra群集

Java 插入性能和插入稳定性较差的Cassandra群集,java,database,cassandra,key-value,datastax-java-driver,Java,Database,Cassandra,Key Value,Datastax Java Driver,我必须为每个客户每秒存储大约250个数值,即每小时大约900k个数字。它可能不会是一整天的记录(可能每天5-10个小时),但我会根据客户端id和读取日期对数据进行分区。最大行长约为22-23M,仍然可以管理。不过,我的方案是这样的: CREATE TABLE measurement ( clientid text, date text, event_time timestamp, value int, PRIMARY KEY ((clientid,date), event_t

我必须为每个客户每秒存储大约250个数值,即每小时大约900k个数字。它可能不会是一整天的记录(可能每天5-10个小时),但我会根据客户端id和读取日期对数据进行分区。最大行长约为22-23M,仍然可以管理。不过,我的方案是这样的:

CREATE TABLE measurement (
  clientid text,
  date text,
  event_time timestamp,
  value int,
  PRIMARY KEY ((clientid,date), event_time)
);
密钥空间的复制系数为2,仅用于测试,告密者是
gossippingpropertyfilesnitch
NetworkTopologyStrategy
。我知道复制因子3更符合生产标准

接下来,我在公司的服务器上创建了一个小型集群,三台裸机虚拟机,具有2个CPU x 2个内核、16GB RAM和大量空间。我和他们在千兆局域网里。基于nodetool,集群可以运行

以下是我用来测试设置的代码:

        Cluster cluster = Cluster.builder()
                .addContactPoint("192.168.1.100")
                .addContactPoint("192.168.1.102")
                .build();
        Session session = cluster.connect();
        DateTime time = DateTime.now();
        BlockingQueue<BatchStatement> queryQueue = new ArrayBlockingQueue(50, true);

    try {

        ExecutorService pool = Executors.newFixedThreadPool(15); //changed the pool size also to throttle inserts

        String insertQuery = "insert into keyspace.measurement (clientid,date,event_time,value) values (?, ?, ?, ?)";
        PreparedStatement preparedStatement = session.prepare(insertQuery);
        BatchStatement batch = new BatchStatement(BatchStatement.Type.LOGGED); //tried with unlogged also

        //generating the entries
        for (int i = 0; i < 900000; i++) { //900000 entries is an hour worth of measurements
            time = time.plus(4); //4ms between each entry
            BoundStatement bound = preparedStatement.bind("1", "2014-01-01", time.toDate(), 1); //value not important
            batch.add(bound);

            //The batch statement must have 65535 statements at most
            if (batch.size() >= 65534) {
                queryQueue.put(batch);
                batch = new BatchStatement();
            }
        }
        queryQueue.put(batch); //the last batch, perhaps shorter than 65535

        //storing the data
        System.out.println("Starting storing");
        while (!queryQueue.isEmpty()) {
            pool.execute(() -> {
                try {

                    long threadId = Thread.currentThread().getId();
                    System.out.println("Started: " + threadId);
                    BatchStatement statement = queryQueue.take();
                    long start2 = System.currentTimeMillis();
                    session.execute(statement);
                    System.out.println("Finished " + threadId + ": " + (System.currentTimeMillis() - start2));
                } catch (Exception ex) {
                    System.out.println(ex.toString());
                }
            });

        }
        pool.shutdown();
        pool.awaitTermination(120,TimeUnit.SECONDS);


    } catch (Exception ex) {
        System.out.println(ex.toString());
    } finally {
        session.close();
        cluster.close();
    }
Cluster Cluster=Cluster.builder()
.addContactPoint(“192.168.1.100”)
.addContactPoint(“192.168.1.102”)
.build();
会话=cluster.connect();
DateTime=DateTime.now();
BlockingQueue queryQueue=new ArrayBlockingQueue(50,true);
试一试{
ExecutorService pool=Executors.newFixedThreadPool(15);//还将池大小更改为限制插入
String insertQuery=“插入keyspace.measurement(clientid、日期、事件时间、值)值(?、、?、?)”;
PreparedStatement PreparedStatement=session.prepare(insertQuery);
BatchStatement batch=新的BatchStatement(BatchStatement.Type.LOGGED);//也尝试使用unlogged
//生成条目
对于(inti=0;i<900000;i++){//900000个条目相当于一小时的测量
时间=时间。加上(4);//每个条目之间间隔4ms
BoundStatement bound=preparedStatement.bind(“1”,“2014-01-01”,time.toDate(),1);//值不重要
批量添加(绑定);
//batch语句最多必须有65535条语句
if(batch.size()>=65534){
queryQueue.put(批处理);
batch=新的BatchStatement();
}
}
queryQueue.put(batch);//最后一批,可能比65535短
//存储数据
System.out.println(“开始存储”);
而(!queryQueue.isEmpty()){
pool.execute(()->{
试一试{
long threadId=Thread.currentThread().getId();
System.out.println(“启动:+threadId”);
BatchStatement语句=queryQueue.take();
long start2=System.currentTimeMillis();
执行(语句);
System.out.println(“Finished”+threadId+:“+(System.currentTimeMillis()-start2));
}捕获(例外情况除外){
System.out.println(例如toString());
}
});
}
pool.shutdown();
池。等待终止(120,时间单位。秒);
}捕获(例外情况除外){
System.out.println(例如toString());
}最后{
session.close();
cluster.close();
}
我是通过阅读这里以及其他博客和网站上的帖子来编写代码的。正如我所理解的,客户机使用多线程是很重要的,这就是为什么我这么做的原因。我还尝试使用异步操作

最终的结果是,无论我使用哪种方法,一个批处理都会在5-6秒内执行,尽管它可能需要10秒。如果我只输入一个批处理(因此,只有~65k列),或者如果我使用一个哑单线程应用程序,则需要相同的时间。老实说,我还期待着更多。特别是因为我的笔记本电脑在本地实例上的性能差不多

第二个,也许是更重要的问题,是我以一种不可预测的方式面临的例外。这两个:

com.datastax.driver.core.exceptions.WriteTimeoutException:Cassandra 一致性1(需要1个副本)写入查询期间超时 但只有0确认写入)

com.datastax.driver.core.exceptions.NoHostAvailableException:所有 尝试查询的主机失败(已尝试):/192.168.1.102:9042 (com.datastax.driver.core.TransportException:[/192.168.1.102:9042] 连接已关闭),/192.168.1.100:9042 (com.datastax.driver.core.TransportException:[/192.168.1.100:9042] 连接已关闭),/192.168.1.101:9042 (com.datastax.driver.core.TransportException:[/192.168.1.101:9042] 连接已关闭。)

说到底,我做错什么了吗?我应该重新组织加载数据的方式,还是更改方案。我试着减少行长(所以我有12小时的行),但这并没有带来很大的区别

============================== 更新:

我很粗鲁,在回答问题后忘记粘贴我使用的代码示例。它运行得相当好,但是我正在继续使用KairosDB进行研究,并使用Astyanax进行二进制传输。看起来我可以用它们比CQL获得更好的性能,尽管KairosDB在过载时可能会有一些问题(但我正在研究),而且Astyanax对我来说有点冗长。然而,这是代码,我可能在什么地方弄错了

当信号量插槽数超过5000时,它几乎保持不变,对性能没有影响

String insertQuery = "insert into keyspace.measurement     (userid,time_by_hour,time,value) values (?, ?, ?, ?)";
        PreparedStatement preparedStatement =     session.prepare(insertQuery);
        Semaphore semaphore = new Semaphore(15000);

    System.out.println("Starting " + Thread.currentThread().getId());
    DateTime time = DateTime.parse("2015-01-05T12:00:00");
    //generating the entries
    long start = System.currentTimeMillis();

    for (int i = 0; i < 900000; i++) { 

        BoundStatement statement = preparedStatement.bind("User1", "2015-01-05:" + time.hourOfDay().get(), time.toDate(), 500); //value not important
        semaphore.acquire();
        ResultSetFuture resultSetFuture = session.executeAsync(statement);
        Futures.addCallback(resultSetFuture, new FutureCallback<ResultSet>() {
            @Override
            public void onSuccess(@Nullable com.datastax.driver.core.ResultSet resultSet) {

                semaphore.release();
            }

            @Override
            public void onFailure(Throwable throwable) {
                System.out.println("Error: " + throwable.toString());
                semaphore.release();
            }
        });
        time = time.plus(4); //4ms between each entry
    }
String insertQuery=“插入keyspace.measurement(userid,time,by_hour,time,value)值(?,,,?)”;
PreparedStatement PreparedStatement=session.prepare(insertQuery);
信号量信号量=新信号量(15000);
System.out.println(“开始”+Thread.currentThread().getId());
DateTime time=DateTime.parse(“2015-01-05T12:00:00”);
//生成条目
长启动=系统