DSE Cassandra - Почему Astyanax быстрее, чем Java-драйвер DataStax
Я переключаю Java-приложение с использования com.netflix.astyanax:astyanax-core:1.56.44 на com.datastax.cassandra:cassandra-driver-core:3.1.0.
Сразу же после простого теста, который вставляет строку со случайно сгенерированным ключом, а затем читает строку 1000 раз, я вижу ужасную производительность по сравнению с кодом, использующим Astyanax. Просто с помощью одноузлового экземпляра Cassandra, работающего локально. Таблица, которую я тестирую, проста - просто столбец первичного ключа blob и столбец даты int.
Вот основной код с драйвером DataStax:
class DataStaxCassandra
{
final Session session;
final PreparedStatement preparedIDWriteCmd;
final PreparedStatement preparedIDReadCmd;
void DataStaxCassandra()
{
final PoolingOptions poolingOptions = new PoolingOptions()
.setConnectionsPerHost(HostDistance.LOCAL, 1, 2)
.setConnectionsPerHost(HostDistance.REMOTE, 1, 1)
.setMaxRequestsPerConnection(HostDistance.LOCAL, 128)
.setMaxRequestsPerConnection(HostDistance.REMOTE, 128)
.setPoolTimeoutMillis(0); // Don't ever wait for a connection to one host.
final QueryOptions queryOptions = new QueryOptions()
.setConsistencyLevel(ConsistencyLevel.LOCAL_ONE)
.setPrepareOnAllHosts(true)
.setReprepareOnUp(true);
final LoadBalancingPolicy dcAwareRRPolicy = DCAwareRoundRobinPolicy.builder()
.withLocalDc("my_laptop")
.withUsedHostsPerRemoteDc(0)
.build();
final LoadBalancingPolicy loadBalancingPolicy = new TokenAwarePolicy(dcAwareRRPolicy);
final SocketOptions socketOptions = new SocketOptions()
.setConnectTimeoutMillis(1000)
.setReadTimeoutMillis(1000);
final RetryPolicy retryPolicy = new LoggingRetryPolicy(DefaultRetryPolicy.INSTANCE);
Cluster.Builder clusterBuilder = Cluster.builder()
.withClusterName("test cluster")
.withPort(9042)
.addContactPoints("127.0.0.1")
.withPoolingOptions(poolingOptions)
.withQueryOptions(queryOptions)
.withLoadBalancingPolicy(loadBalancingPolicy)
.withSocketOptions(socketOptions)
.withRetryPolicy(retryPolicy);
// I've tried both V3 and V2, with lower connections/host and higher reqs/connection settings
// with V3, and it doesn't noticably affect the test performance. Leaving it at V2 because the
// Astyanax version is using V2.
clusterBuilder.withProtocolVersion(ProtocolVersion.V2);
final Cluster cluster = clusterBuilder.build();
session = cluster.connect();
preparedIDWriteCmd = session.prepare(
"INSERT INTO \"mykeyspace\".\"mytable\" (\"uuid\", \"date\") VALUES (?, ?) USING TTL 38880000");
preparedIDReadCmd = session.prepare(
"SELECT \"date\" from \"mykeyspace\".\"mytable\" WHERE \"uuid\"=?");
}
public List<Row> execute(final Statement statement, final int timeout)
throws InterruptedException, ExecutionException, TimeoutException
{
final ResultSetFuture future = session.executeAsync(statement);
try
{
final ResultSet readRows = future.get(timeout, TimeUnit.MILLISECONDS);
final List<Row> resultRows = new ArrayList<>();
// How far we can go without triggering the blocking fetch:
int remainingInPage = readRows.getAvailableWithoutFetching();
for (final Row row : readRows)
{
resultRows.add(row);
if (--remainingInPage == 0) break;
}
return resultRows;
}
catch (final TimeoutException e)
{
future.cancel(true);
throw e;
}
}
private void insertRow(final byte[] id, final int date)
throws InterruptedException, ExecutionException, TimeoutException
{
final ByteBuffer idKey = ByteBuffer.wrap(id);
final BoundStatement writeCmd = preparedIDWriteCmd.bind(idKey, date);
writeCmd.setRoutingKey(idKey);
execute(writeCmd, 1000);
}
public int readRow(final byte[] id)
throws InterruptedException, ExecutionException, TimeoutException
{
final ByteBuffer idKey = ByteBuffer.wrap(id);
final BoundStatement readCmd = preparedIDReadCmd.bind(idKey);
readCmd.setRoutingKey(idKey);
final List<Row> idRows = execute(readCmd, 1000);
if (idRows.isEmpty()) return 0;
final Row idRow = idRows.get(0);
return idRow.getInt("date");
}
}
void perfTest()
{
final DataStaxCassandra ds = new DataStaxCassandra();
final int perfTestCount = 10000;
final long startTime = System.nanoTime();
for (int i = 0; i < perfTestCount; ++i)
{
final String id = UUIDUtils.generateRandomUUIDString();
final byte[] idBytes = Utils.hexStringToByteArray(id);
final int date = (int)(System.currentTimeMillis() / 1000);
try
{
ds.insertRow(idBytes, date);
final int dateRead = ds.readRow(idBytes);
assert(dateRead == date) : "Inserted ID with date " +date +" but date read is " +dateRead;
}
catch (final InterruptedException | ExecutionException | TimeoutException e)
{
System.err.println("ERROR reading ID (test " +(i+1) +") - " +e.toString());
}
}
System.out.println(
perfTestCount +" insert+reads took " +
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) +" ms");
}
Есть ли что-то, что я делаю неправильно, что приведет к плохой производительности? Я надеялся, что это будет приличное улучшение скорости, учитывая, что я использую довольно старую версию Astyanax.
Я пытался не оборачивать политику балансировки нагрузки с TokenAwarePolicy и избавляться от строк "setRoutingKey", просто потому, что я знаю, что эти вещи определенно не должны помогать при использовании только одного узла, как я делаю в настоящее время.
Моя локальная версия Cassandra - 2.1.15 (которая поддерживает собственный протокол V3), но машины в нашей производственной среде работают под управлением Cassandra 2.0.12.156 (которая поддерживает только V2).
Имейте в виду, что это предназначено для среды с кучей узлов и несколькими центрами обработки данных, поэтому я получил настройки так, как я делаю (что фактические значения устанавливаются из файла конфигурации), даже если я знаю, для этого теста я мог пропустить, используя такие вещи, как DCAwareRoundRobinPolicy.
Любая помощь будет принята с благодарностью! Я также могу опубликовать код, который использует Astyanax, сначала я подумал, что было бы хорошо убедиться, что с моим новым кодом ничего плохого нет. Спасибо!
Тесты 10 000 операций записи + чтения занимают около 30 секунд с драйвером DataStax, в то время как с Astyanax они занимают диапазон 15-20 секунд.
Я увеличил количество тестов до 100 000, чтобы увидеть, возможно, есть какие-то издержки с драйвером DataStax, который при запуске просто потребляет ~10 секунд, после чего они могут работать более схожим образом. Но даже с 100 000 чтения / записи:
AstyanaxCassandra 100 000 вставок + считываний заняло 156593 мс
DataStaxCassandra 100 000 вставок + считываний заняло 294340 мс