Cassandra java驱动程序 - 使用多个线程提取数据时的高延迟

时间:2016-09-22 11:44:31

标签: java multithreading cassandra

我可以看到使用datastax cassandra驱动程序(3.0)的奇怪行为。我已经创建了一个新集群,然后我使用相同的集群对象启动了一组线程。如果我将线程保持为1或2,我看到平均提取时间为5毫秒,但如果我将线程增加到60,则提取时间增加到200毫秒(每个线程)。奇怪的是,如果我让60个线程应用程序运行并且我在同一台机器上启动另一个只有1个线程的进程,那个单线程应用程序的提取时间再次为5ms。所以它似乎与客户有关。我多次重复相同的测试以避免缓存冷启动问题。 以下是集群对象的配置方式:

    PoolingOptions poolingOptions = new PoolingOptions();
    poolingOptions
        .setConnectionsPerHost(HostDistance.LOCAL,  parallelism, parallelism+20)
        .setConnectionsPerHost(HostDistance.REMOTE, parallelism, parallelism+20)
        .setMaxRequestsPerConnection(HostDistance.LOCAL, 32768)
        .setMaxRequestsPerConnection(HostDistance.REMOTE, 2000);

    this.cluster = Cluster.builder()
            .addContactPoints(nodes)
            .withRetryPolicy(DowngradingConsistencyRetryPolicy.INSTANCE)
            .withReconnectionPolicy(new ConstantReconnectionPolicy(100L))
            .withLoadBalancingPolicy(new TokenAwarePolicy(DCAwareRoundRobinPolicy.builder().build()))
            .withCompression(Compression.LZ4)
            .withPoolingOptions(poolingOptions)
            .withProtocolVersion(ProtocolVersion.V4)
            .build();

有没有人遇到过同样的问题?这似乎是一个客户端配置问题。对Netty可能还有一些额外缺失的配置?

更新1 应用程序正在做的是使用如下查询提取数据块:

select * from table where id=? and ts>=? and ts<?

所以我有60个线程并行提取这些数据。 id是分区键。每个查询都由线程执行:

//Prepare statement
PreparedStatement stmt = ... get the prepared statment cached
BoundStatement bstmt = stmt.bind(...)
//Execute query
long te1 = System.nanoTime();       
ResultSet rs = this.session.execute(bstmt);
long te2 = System.nanoTime();
//Fetch...
Iterator<Row> iterator = rs.iterator();
while (!rs.isExhausted() && iterator.hasNext()) { .... }

会话是一个并且共享所有线程。我测量的是session.execute()方法调用的平均时间。

谢谢!

更新2 这是架构定义

CREATE TABLE d_t (
    id bigint,
    xid bigint,
    ts timestamp,
    avg double,
    ce double,
    cg double,
    p double,
    w double,
    c double,
    sum double,
    last double,
    max double,
    min double,
    p75 double,
    p90 double,
    p95 double,
    squad double,
    sumq double,
    wavg double,
    weight double,
    PRIMARY KEY ((id), xid, ts)
) WITH CLUSTERING ORDER BY (xid DESC, ts DESC)
and  compaction = {'class': 'SizeTieredCompactionStrategy'} 
and gc_grace_seconds=86400
and caching = { 'keys' : 'ALL', 'rows_per_partition':'36000' }
and min_index_interval = 2
and max_index_interval = 20;

更新3 也试过

.setMaxRequestsPerConnection(HostDistance.LOCAL, 1)
.setMaxRequestsPerConnection(HostDistance.REMOTE, 1)

没有变化

1 个答案:

答案 0 :(得分:0)

最终我认为这取决于你的代码在做什么。你能分享一个例子吗?

关于延迟增加,您如何衡量?根据您的陈述:

  

奇怪的是,如果我让60个线程应用程序运行并且我在同一台机器上启动另一个只有1个线程的进程,那个单线程应用程序的提取时间再次为5ms。

60个并发请求实际上并不是太多,一般来说,您不需要使用datastax java驱动程序执行每个请求的线程。您可以使用单个应用程序线程实现高吞吐量,因为驱动程序使用的netty事件循环组将完成大部分工作。

本机协议C *使用允许每个连接多个请求。正如您在此处配置的那样,每个连接最多可达32768个并发请求。实际上,您根本不需要触摸此配置,因为默认情况下(每个连接1000个请求)是明智的,因为在实践中C *不会从cassandra.yaml处理超过native_transport_max_threads的情况。 (默认为128)并排队其余部分。

因此,每个主机不需要很多连接。每个主机的默认1个核心连接应该足以支持60个并发请求。增加每个主机的连接数对您来说不会有太大帮助,在分析中我发现每个主机有8个连接的收益递减,具有高吞吐量(数千个并发请求),并且每个主机通过16个连接后吞吐量变得更糟,虽然您的里程可能因环境而异。

尽管如此,我建议不要将PoolingOptions配置为超出默认值,除了可能将核心和最大值设置为8,以用于尝试实现更高吞吐量(> 10k请求/秒)的情况。