首页 文章

Cassandra DataStax驱动慢?

提问于
浏览
0

我刚开始尝试使用Cassandra,我正在使用C#和DataStax driver(v 3.0.8) . 我想做一些性能测试,看看Cassandra处理时间序列数据的速度有多快 .

结果很糟糕,因为它需要永恒才能做到 SELECT . 所以我想我做错了什么 .

我在本地计算机上安装了Cassandra,并创建了一个表:

CREATE KEYSPACE dm WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}  AND durable_writes = true;

CREATE TABLE dm.daily_data_by_day (
    symbol text,
    value_type int,
    as_of_day date,
    revision_timestamp_utc timestamp,
    value decimal,
    PRIMARY KEY ((symbol, value_type), as_of_day, revision_timestamp_utc)
) WITH CLUSTERING ORDER BY (as_of_day ASC, revision_timestamp_utc ASC)
    AND bloom_filter_fp_chance = 0.01
    AND caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'}
    AND comment = ''
    AND compaction = {'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy', 'max_threshold': '32', 'min_threshold': '4'}
    AND compression = {'chunk_length_in_kb': '64', 'class': 'org.apache.cassandra.io.compress.LZ4Compressor'}
    AND crc_check_chance = 1.0
    AND dclocal_read_repair_chance = 0.1
    AND default_time_to_live = 0
    AND gc_grace_seconds = 864000
    AND max_index_interval = 2048
    AND memtable_flush_period_in_ms = 0
    AND min_index_interval = 128
    AND read_repair_chance = 0.0
    AND speculative_retry = '99PERCENTILE';

我在这个表中填充了大约1500万行,分为大约10000个分区,每个分区最多包含10000行 .

这里's the test I' m正在运行(更新on request by phact):

[Test]
public void SelectPerformance()
{
    _cluster = Cluster.Builder().AddContactPoint("127.0.0.1").Build();
    _stopwatch = new Stopwatch();
    var items = new[]
        {
            // 20 different items...
        };

    foreach (var item in items)
    {
        var watch = Stopwatch.StartNew();
        var rows = ExecuteQuery(item.Symbol, item.FieldType, item.StartDate, item.EndDate);
        watch.Stop();
        Console.WriteLine($"{watch.ElapsedMilliseconds}\t{rows.Length}");
    }

    Console.WriteLine($"Average Execute: {_stopwatch.ElapsedMilliseconds/items.Length}");
    _cluster.Dispose();
}

private Row[] ExecuteQuery(string symbol, int fieldType, LocalDate startDate, LocalDate endDate)
{
    using (var session = _cluster.Connect("dm"))
    {
        var ps = session.Prepare(
@"SELECT
    symbol,
    value_type,
    as_of_day,
    revision_timestamp_utc,
    value
FROM
    daily_data_by_day
WHERE
    symbol = ? AND
    value_type = ? AND
    as_of_day >= ? AND as_of_day < ?");
        var statement = ps.Bind(symbol, fieldType, startDate, endDate);
        statement.EnableTracing();

        _stopwatch.Start();
        var rowSet = session.Execute(statement);
        _stopwatch.Stop();

        return rowSet.ToArray();
    }
}

秒表告诉我 session.Execute() 需要20-30毫秒才能执行(更新:在更改代码后只创建集群一次,我只需要大约15毫秒) . 所以我启用了一些跟踪并获得了以下结果:

activity                                                                  | source_elapsed 
--------------------------------------------------------------------------------------------
 Parsing SELECT symbol, value_type, as_of_day, revision_timestamp_utc,...; |             47 
                                                       Preparing statement |             98 
                     Executing single-partition query on daily_data_by_day |            922 
                                              Acquiring sstable references |            939 
 Skipped 0/5 non-slice-intersecting sstables, included 0 due to tombstones |            978 
                                   Bloom filter allows skipping sstable 74 |           1003 
                                   Bloom filter allows skipping sstable 75 |           1015 
                                   Bloom filter allows skipping sstable 72 |           1024 
                                   Bloom filter allows skipping sstable 73 |           1032 
                                              Key cache hit for sstable 63 |           1043 
                                 Merged data from memtables and 5 sstables |           1329 
                                       Read 100 live and 0 tombstone cells |           1353

如果我正确理解了这个跟踪,Cassandra执行查询的时间不到1.4毫秒 . 那么DataStax驱动程序在剩下的时间里做什么呢?

(作为参考,我对本地SQL Server实例进行了相同的性能测试,导致从C#执行相同查询大约1-2毫秒 . )

Update:

我试图进行一些分析,这对你不拥有的异步代码来说并不容易 .

我的结论是,大部分时间都花在解析响应上 . 每个响应包含2000到3000行,每次响应解析大约需要9 ms . 反序列化占用大部分时间,大约6.5毫秒,十进制是最差的,每个字段大约3毫秒 . 其他字段(text,int,date和timestamp)每个字段大约需要0.5 ms .

看看我测量的时间,我应该怀疑这一点:响应中的行越多,所需的时间就越长,几乎是线性的 .

3 回答

  • 7

    @ xmas79突出了一个伟大的观点 . 您不应该创建太多的会话实例(最好每个键空间使用1个),但是还有另一个指南可以帮助您 . 请遵循以下指南并参考:

    • 每个(物理)集群使用一个集群实例(每个应用程序生命周期)

    • 每个键空间最多使用一个会话,或使用单个会话并在查询中明确指定键空间

    • 如果多次执行语句,请考虑使用PreparedStatement

    • 您可以通过使用批次减少网络往返次数并进行原子操作

    http://www.datastax.com/dev/blog/4-simple-rules-when-using-the-datastax-drivers-for-cassandra

    EDIT

    另外,再看看你的代码,你正在为你正在执行的每个查询创建一个准备好的语句 . 准备好的语句只应创建一次,您应该使用它的引用来执行查询 . 准备好的语句所做的是将经常执行的CQL发送到服务器,以便服务器已经解析该字符串并向用户返回该标识 . 因此,如果您不打算为每个查询共享PreparedStatment对象,我建议您不要使用它 . 或者将代码更改为以下内容:

    [Test]
    public void SelectPerformance()
    {
        _cluster = Cluster.Builder().AddContactPoint("127.0.0.1").Build();
        var session = _cluster.Connect("dm");
        var ps = session.Prepare(@"SELECT symbol, value_type, as_of_day, revision_timestamp_utc, value FROM daily_data_by_day WHERE symbol = ? AND  value_type = ? AND as_of_day >= ? AND as_of_day < ?");
        var items = new[]
        {
            // 20 different items...
        };
        foreach (var item in items)
        {
            var watch = Stopwatch.StartNew();
            var rows = ExecuteQuery(session, ps, item.Symbol, item.FieldType, item.StartDate, item.EndDate);
            watch.Stop();
            Console.WriteLine($"{watch.ElapsedMilliseconds}\t{rows.Length}");
        }
    
        Console.WriteLine($"Average Execute: {   _stopwatch.ElapsedMilliseconds/items.Length}");
        _cluster.Dispose();
    }
    
    private Row[] ExecuteQuery(Session session, PreparedStatement ps, string symbol, int fieldType, LocalDate startDate, LocalDate endDate)
    {
         var statement = ps.Bind(symbol, fieldType, startDate, endDate);
         // Do not enable request tracing for latency benchmarking
         // statement.EnableTracing();
         var rowSet = session.Execute(statement);
         return rowSet.ToArray();
    }
    
  • 2

    简短的回答您希望将群集对象保持为Cassandra打开并在请求中重复使用它 .

    集群对象本身的创建成本很高,但却带来了自动负载 balancer ,令牌感知,自动故障转移等优势 .

  • 1

    你为什么要执行

    using (var session = _cluster.Connect("dm"))
    

    在每个查询?您应该构建一次 Cluster 实例,连接到群集并获取 Session 一次,并在任何地方重用它们 . 我认为 Cluster 对象配置重要参数,如故障转移,负载均衡等. Session 对象为您管理它们 . 每次连接都会给您带来性能损失 .

    EDIT

    您似乎正在执行 SELECT ,每个延迟为10ms-15ms . 您是否在每个查询中获得相同的跟踪数(例如1.4ms)?你的存储IO系统是什么?如果您在旋转磁盘上,则可能是磁盘子系统的寻道时间损失 .

相关问题