Cassandra 不一致,尽管 QUORUM 一致性级别与复制因子 3
Cassandra is not consistent despite QUORUM consistency level with replication factor 3
我对 Cassandra 的一致性有疑问。我在集群中有 3 个 Cassandra 节点(版本 2.0.14.352),我正在读写 一致性级别 QUORUM,我的 replicationfactor 是 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));
}
对应table
CREATE TABLE keyspace.table(
id text,
year int,
month int,
time timestamp,
value double,
PRIMARY KEY ((id, year, month), time)
)
dataProvider.setValue() 内部将给定值放入 NavigableMap。 dataProvider.saveToDB() 将数据插入 Cassandra。在这里,我一方面尝试异步插入数据并等待所有 ResultSetFuture 完成,另一方面我同步执行语句。但这仅影响性能。详细的保存方法看起来像
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 管理连接。我正在等待所有结果集完成
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;
}
奇怪的是,如果我删除注释下方的行,测试就会通过,而不管我执行它的频率如何。但是,如果我 运行 在不删除该行的情况下进行测试,有时它会在第一个循环中失败,但有时它会 运行s 3 循环直到失败。此外,它总是在不同的行失败。例如
java.lang.AssertionError: i=0
Expected :101
Actual :2
我也得到了
java.lang.AssertionError: i=2
Expected :2
Actual :101
所以 Cassandra 似乎写了 1,然后写了 2,而不是写了 2 Cassandra 恢复了我在 1 之前写的 101。有人对这种行为有解释吗?如果我删除该行,为什么测试会通过?我正在写不同的分区。我尝试将一致性级别更改为全部,但行为没有改变。
我解决了。显然时钟不是 100% 同步的。当我创建插入语句时,我添加了 .using(timestamp(System.nanoTime() / 1000)); 现在测试通过了。
我对 Cassandra 的一致性有疑问。我在集群中有 3 个 Cassandra 节点(版本 2.0.14.352),我正在读写 一致性级别 QUORUM,我的 replicationfactor 是 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));
}
对应table
CREATE TABLE keyspace.table(
id text,
year int,
month int,
time timestamp,
value double,
PRIMARY KEY ((id, year, month), time)
)
dataProvider.setValue() 内部将给定值放入 NavigableMap。 dataProvider.saveToDB() 将数据插入 Cassandra。在这里,我一方面尝试异步插入数据并等待所有 ResultSetFuture 完成,另一方面我同步执行语句。但这仅影响性能。详细的保存方法看起来像
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 管理连接。我正在等待所有结果集完成
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;
}
奇怪的是,如果我删除注释下方的行,测试就会通过,而不管我执行它的频率如何。但是,如果我 运行 在不删除该行的情况下进行测试,有时它会在第一个循环中失败,但有时它会 运行s 3 循环直到失败。此外,它总是在不同的行失败。例如
java.lang.AssertionError: i=0
Expected :101
Actual :2
我也得到了
java.lang.AssertionError: i=2
Expected :2
Actual :101
所以 Cassandra 似乎写了 1,然后写了 2,而不是写了 2 Cassandra 恢复了我在 1 之前写的 101。有人对这种行为有解释吗?如果我删除该行,为什么测试会通过?我正在写不同的分区。我尝试将一致性级别更改为全部,但行为没有改变。
我解决了。显然时钟不是 100% 同步的。当我创建插入语句时,我添加了 .using(timestamp(System.nanoTime() / 1000)); 现在测试通过了。