尽管QUORUM与复制因子3的一致性水平,Cassandra仍然不一致

时间:2015-07-08 07:26:54

标签: cassandra cassandra-2.0 datastax-java-driver consistency

我遇到了Cassandras一致性问题。我在群集中有3个Cassandra节点(版本2.0.14.352),我正在使用一致性级别QUORUM 进行读写,而我的复制因子是3 。 如果我理解this就我的情况而言Cassandra应该是一致的,因为2 + 2> 3。但我在java中编写了一个测试,在那里我使用datastax-driver非常快速地将一些数据插入到cassandra中:

final Instant t1 = Instant.parse("2000-01-01T00:00:00.000Z");
final Instant t2 = Instant.parse("2000-02-01T00:00:00.000Z");

for (int i = 0; i < 100; i++) {
    dataProvider.setValue(t1, new Double(1));
    //If the next line is removed, the test will pass
    dataProvider.setValue(t2, new Double(3));

    dataProvider.saveToDB();
    dataProvider.clear();
    assertEquals("i=" + i, new Double(3), dataProvider.getValue(t2));
    assertEquals("i=" + i, new Double(1), dataProvider.getValue(t1));

    dataProvider.setValue(t1, new Double(2));
    dataProvider.saveToDB();
    dataProvider.clear();
    assertEquals("i=" + i, new Double(2), dataProvider.getValue(t1));

    dataProvider.setValue(t1, new Double(101));
    dataProvider.saveToDB();
    dataProvider.clear();
    assertEquals("i=" + i, new Double(101), dataProvider.getValue(t1));
}

带有相应的表格

CREATE TABLE keyspace.table(
  id text,
  year int,
  month int,
  time timestamp,
  value double,
  PRIMARY KEY ((id, year, month), time)
)

dataProvider.setValue()internaly将给定值放入NavigableMap。 dataProvider.saveToDB()将数据插入到Cassandra中。在这里,我尝试一方面插入数据异步并等待所有ResultSetFuture完成,另一方面我执行语句同步。但这只影响了性能。详细说,save方法看起来像

final List<ResultSetFuture> sets = newLinkedList();
Batch batch = QueryBuilder.batch();
int batchsize=0;
for (Map.Entry<Instant, Double> entry : valueMap) {
    final Instant instant = entry.getKey();
    final ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("UTC"));
    final Date date = Date.from(instant);
    final Insert insert = QueryBuilder.insertInto(table)
            .value(ID, id)
            .value(YEAR, zonedDateTime.getYear())
            .value(MONTH, zonedDateTime.getMonthValue())
            .value(TIME, date)
            .value(VALUE, entry.getValue());
    batch.add(insert);
    ++batchsize;
    if(batchsize % 200 == 0){
        sets.add(cassandraConnector.executeAsync(batch));
        batch = QueryBuilder.batch();
    }
}
if(batchsize % 200 != 0) { //es gibt noch nicht abgeschickte Statements
    sets.add(cassandraConnector.executeAsync(batch));
}
cassandraConnector.waitForFinish(sets);

cassandraConnector管理连接。我等待所有ResultSet完成

public boolean waitForFinish(List<ResultSetFuture> sets) {
    ResultSet result = null;
    for (final ResultSetFuture resultSetFuture : sets) {
        // Wait until finished
        try {
            result = resultSetFuture.get();
        } catch (InterruptedException e) {
            resultSetFuture.cancel(true);
            e.printStackTrace();
            return false;
        } catch (ExecutionException e) {
            e.printStackTrace();
            if (result != null) {
                ExecutionInfo executionInfo = result.getExecutionInfo();
                System.out.println("Timout from server with IP: " + executionInfo.getTriedHosts());
            }
            return false;
        }
    }
    return true;
}

好奇心是,如果我删除评论下的那一行,测试就会通过,而且我执行它的频率并不重要。但是如果我在没有删除线路的情况下运行测试,有时它会在第一个循环中失败,但有时会运行3个循环直到失败。此外,它总是在不同的线路上失败。例如

java.lang.AssertionError: i=0 
Expected :101
Actual   :2

我也有

java.lang.AssertionError: i=2
Expected :2
Actual   :101

所以似乎Cassandra写了1并且在那之后而不是写着2 Cassandra恢复了我在1之前写的101.有没有人对这种行为有解释?如果我删除线路,为什么测试通过?我正在写不同的分区。我试图将一致性级别更改为ALL,但行为没有改变。

1 个答案:

答案 0 :(得分:1)

我解决了。显然,时钟不是100%同步的。当我创建insert语句时,我添加了 .using(timestamp(System.nanoTime()/ 1000)); ,现在测试通过。