2016-09-13 137 views
0

我剛開始嘗試使用Cassandra,並使用C#和DataStax driver(v 3.0.8)。我想做一些性能測試,看看Cassandra處理時間序列數據的速度有多快。Cassandra DataStax驅動程序很慢?

結果是磕磕絆絆,它需要一個永恆的SELECT。所以我想我做錯了什麼。

我已經安裝卡桑德拉我的本地計算機上和我創建了一個表:

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'; 

我已經填寫此表約15萬行,分爲約10000分區,每個分區包含多達10000行。

以下是我正在運行測試(更新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 

如果我正確理解這一點痕跡,卡桑德拉花費小於1.4毫秒執行我的查詢。那麼DataStax驅動程序在剩下的時間裏做了什麼?

(作爲參考,我也做了同樣的性能測試對造成約1-2毫秒執行從C#相同的查詢本地SQL Server實例。)

更新:

我試圖做一些分析,這是不是很容易與異步代碼,你不擁有...

我的結論是,大部分時間是花費解析響應。每個響應包含2000 - 3000行之間,解析每個響應大約需要9毫秒。反序列化需要大部分時間,大約6.5毫秒,小數點是最差的,每場大約3毫秒。其他字段(text,int,date和timestamp)每個字段大約需要0.5 ms。

看着我的測量時間,我應該懷疑這一點:響應中的行越多,需要的時間越長,並且幾乎是線性的。

+0

你在本地卡桑德拉ENV執行這些測試?只有一個節點?我想要分析你的代碼。 – k0ner

+0

@ k0ner這些都是在我的本地機器上完成的,只有一個節點。它用於評估Cassandra,學習如何使用它並瞭解它的表現。 –

+0

你是否試圖分析你的代碼? – k0ner

回答

2

簡短的回答,你想保持羣集對象卡桑德拉打開並重新使用它跨請求。

集羣對象本身的創建是昂貴的,但給喜歡自動負載平衡,令牌意識,自動故障轉移,福利等等等等

+2

爲了測試您的假設,我按順序運行了20個不同的查詢,全部使用相同的集羣。你是正確的,第一個查詢需要更長的時間,但下面的查詢仍然需要大約10-15毫秒來執行。 –

+0

請更新您的新代碼 – phact

+0

我已經更新了代碼,完整的測試 –

0

你爲什麼執行

using (var session = _cluster.Connect("dm")) 
每個查詢

?您應該構建一次Cluster實例,連接到羣集並獲取Session一次,然後在任何地方重複使用它們。我認爲Cluster對象配置重要參數,如故障轉移,負載平衡等。Session對象爲您管理它們。每次連接會給你的表現處罰。

編輯

似乎你每10毫秒,15毫秒的延遲執行SELECT。你是否在每個查詢中獲得相同的跟蹤數(例如1.4ms)?什麼是你的存儲IO系統?如果您在旋轉磁盤上,可能會導致磁盤子系統的查找時間損失。

+0

這是一個我想模擬現實生產環境的性能測試。此外,在[phact's answer](http://stackoverflow.com/a/39467586/617658)之後,我做了一個測試,其中會話只創建一次,執行時間僅從約15 ms減少到12 ms。 –

+0

@TorbjörnKalin您還需要保持連接打開。將保持資源繁忙,但一個很好的理由。 – xmas79

7

@ xmas79突出顯着的一點。你不應該創建太多的會話實例(更好地爲每個密鑰空間使用1),但也有另一個準則可以幫助你。按照下面的指南和參考:每(物理)羣集(每個應用程序生命週期)

  • 使用每密鑰空間最多隻有一個會話,或者使用一個會話,並明確

    • 使用一個羣集實例中指定密鑰空間的查詢
    • 如果執行語句不止一次,可以考慮使用PreparedStatement
    • 可以減少網絡的往返次數,並且還通過使用批次的 原子操作

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

    編輯

    此外,考慮你的代碼第二次看,你正在爲你的每一個執行相同的查詢一份準備好的聲明。準備的語句應該只創建一次,並且您應該使用其引用來執行查詢。準備好的語句的作用是將經常執行的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(); 
    } 
    
  • +0

    如果這是生產代碼,我會同意你的意見。然而,這是一個測試,我正在嘗試對類似生產的環境進行測量。 –

    +0

    我會深入瞭解準備好的語句的用法。看看我上面的評論。你也可以在這裏看到https://datastax.github.io/java-driver/manual/statements/prepared/準備好的語句是如何工作的 –

    +0

    我改變了我的代碼以重用準備好的語句,現在每個語句的速度大約快1ms請求,所以這是很好的建議(儘管我不確定我是否能夠在生產後期做同樣的事情)。大部分剩餘的10 ms用於解析響應,請參閱我的問題的更新。 –

    相關問題