允許過濾與不過濾;Cassandra 數據模型問題
我在家裡的一些 RaspberryPi 上執行了一個玩具 Cassandra 集群。我目前正在將 CryptoCoin 數據記錄到其中,希望能更多地了解 Cassandra 以及沿途的其他一些事情。
我今天的問題是確定我是否在這張表上正確地建構了我的模式。
該表沒有很多欄位,主鍵是名稱欄位和時間戳欄位。我想從所有硬幣中查詢最後 N 小時的數據(每分鐘記錄一次數據)。如果我使用簡單的 WHERE 子句,我會收到“ALLOW FILTERING”警告。我理解它為什麼會發生,但我正在努力理解正確的前進道路以確保可擴展的解決方案。現在該表只有大約 320k 條記錄,我可以毫無問題地使用 ALLOW FILTERING,但我意識到這可能並非總是如此。
我設置了一個測試來查看執行兩種不同的查詢方法需要多長時間。ALLOW FILTERING 方法目前是最快的,但它可能會保持這種狀態嗎?這就是我缺乏知識的地方。
我有一個想法添加另一個欄位,即星期幾,也可能是一個月欄位。想法是這可能允許在查詢中進行更多過濾,因此我不必像下面那樣遍歷所有硬幣,但我不知道這是否是個好主意。如果我這樣做,我是否將它們設為 PrimaryKey?認為這是我與 Cassandra 最混淆的地方,但並非完全如此;也許只是不夠自信。
CQL表說明:
CREATE TABLE cryptocoindb.worldcoinindex ( name text, timestamp int, label text, price_btc double, price_cny double, price_eur double, price_gbp double, price_rur double, price_usd double, volume_24h double, PRIMARY KEY (name, timestamp) ) WITH CLUSTERING ORDER BY (timestamp 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';
Python中的程式碼:
# First method using ALLOW FILTERING: startTime = time.time() oneDaySec = 60*60*24 prior24hr = int(time.time()-oneDaySec) query = "SELECT * FROM {}.{} WHERE timestamp > {} ALLOW FILTERING;".format(CASSANDRA_DB, CASSANDRA_TABLE, prior24hr) rslt = session.execute(query, timeout=None) worldcoinindex = rslt._current_rows elapseTime = time.time()-startTime print("Elapsed Time for this method: {}".format(elapseTime))
> > 此方法經過的時間:0.6223547458648682 > > >
# Second method using multiple queries... startTime = time.time() # I get the unique coin names here. qryGetCoinList = "SELECT DISTINCT name FROM {}.{};".format(CASSANDRA_DB, CASSANDRA_TABLE) rslt = session.execute(qryGetCoinList, timeout=None) rsltGetCoinList = rslt._current_rows rsltGetCoinList = rsltGetCoinList.name.tolist() oneDaySec = 60*60*24 prior24hr = int(time.time()-oneDaySec) # This iterates over the unique coin names and queries # the last 24 hrs worth of data per coin. # NOTE: There are 518 unique coins. rsltTodayPrices = pd.DataFrame() for coin in rsltGetCoinList: qryTodayPrices = """ SELECT * FROM {}.{} WHERE name = '{}' AND timestamp > {}; """.format(CASSANDRA_DB, CASSANDRA_TABLE, coin, prior24hr) rslt = session.execute(qryTodayPrices, timeout=None) TodayPrices = rslt._current_rows rsltTodayPrices.append(TodayPrices) elapseTime = time.time()-startTime print("Elapsed Time for this method: {}".format(elapseTime))
> > 此方法經過的時間:1.4576539993286133 > > >
謝謝!
現在該表只有大約 320k 條記錄,我可以毫無問題地使用 ALLOW FILTERING,但我意識到這可能並非總是如此。
所以事情是這樣的:Cassandra非常擅長通過特定鍵查詢數據。它還擅長檢索分區內的一系列數據。
"SELECT * FROM {}.{} WHERE timestamp > {} ALLOW FILTERING;"
但是由於它的分佈式特性,它並不擅長掃描整個表來編譯結果集。這就是您要求它對上述查詢進行的操作。
網路流量很昂貴。因此,Cassandra 的主要目標是確保您的查詢由單個節點提供服務。在
ALLOW FILTERING
不指定分區鍵(名稱)的情況下使用時,您的查詢需要一個協調節點,並檢查集群中的每個節點是否有可能與您的 WHERE 子句匹配的值。本質上,集群中的節點越多,對
ALLOW FILTERING
性能的不利影響就越大(除非您至少指定分區鍵……只有這樣才能保證您的查詢可以由單個節點提供服務)。請注意,您較慢的查詢實際上做到了這一點,並為您解決了這個問題。我有一個想法添加另一個欄位,即星期幾,也可能是一個月欄位。
這是個好主意!
它解決了兩個問題。
- 它確保您的查詢將由單個節點提供服務。
- 它可以保護您的分區不會變得太大。
Cassandra 每個分區有 20 億個單元的限制。由於您的分區鍵是“名稱”並且您不斷在其中添加唯一的時間戳,因此您將朝著該限制前進,直到達到該限制,或者您的分區變得太大而無法使用(可能是後者)。
以下是我將如何解決這個問題:
CREATE TABLE cryptocoindb.worldcoinindex_byday ( daybucket text, name text, datetime timestamp, label text, price_btc double, price_cny double, price_eur double, price_gbp double, price_rur double, price_usd double, volume_24h double, PRIMARY KEY (daybucket, datetime, name) ) WITH CLUSTERING ORDER BY (datetime DESC, name ASC);
現在你可以這樣查詢:
SELECT * FROM cryptocoindb.worldcoinindex WHERE daybucket='20170825' AND datetime > '2017-08-25 17:20';
此外,通過按“日期時間”降序對行進行分群,您可以確保最新數據位於每個單元格的頂部(使 Cassandra 無需解析)。
我將“名稱”移動到最後一個分群列,只是為了保持唯一性。如果您永遠不會按“名稱”進行查詢,那麼將其用作分區鍵是沒有意義的。
希望這可以幫助。
注意:我將您的更改
timestamp int
為,datetime timestamp
因為它增加了範例的清晰度。您可以使用任何適合您的方式,但請注意以數據類型命名列所引起的混淆。編輯 20170826
以下程式碼與您的程式碼相同還是不同?
PRIMARY KEY ((daybucket, datetime), name)
不,那不一樣。那是使用一種稱為複合分區鍵的東西。它會在集群中為您提供更好的數據分佈,但會使您的查詢更加困難,並且基本上會讓您重新進行表掃描。
對於 Cassandra 主鍵的良好、全面的描述,Carlo Bertuccini在 StackOverflow 上有很好的回答:
有沒有辦法改變 Cassandra 讀取時間戳的方式,或者有一種簡單的方法來更改整個數據欄位以改變時間戳,以便正確讀取它?
並不真地。Cassandra 時間戳可能很難使用。它們以毫秒精度儲存,但在查詢時實際上並沒有顯示完整的精度。此外,作為 2.1 更新檔之一,它會自動以 GMT 顯示時間;所以這也會讓人們感到困惑。如果您在應用程序端管理時間戳的方式適合您,請堅持下去。