web-dev-qa-db-ja.com

フィルタリングを許可するかどうか; Cassandraデータモデルの質問

私はおもちゃを持っていますCassandraクラスターが自宅の一部のRaspberryPisで実行されています。現在Cassandraおよび途中で他のいくつかのもの。

今日ここで私の質問は、この1つのテーブルでスキーマを正しく構造化しているかどうかを確認することです。

テーブルには多くのフィールドがありません。主キーは名前フィールドとタイムスタンプフィールドです。すべてのコインから過去N時間のデータ(データは毎分ログに記録されます)を照会したいと思います。単純なWHERE句を使用すると、「ALLOW FILTERING」警告が表示されます。なぜそうなるのかは理解していますが、拡張可能なソリューションを実現するための正しい道筋を理解するのに苦労しています。現在、テーブルには約320kのレコードしかなく、ALLOW FILTERINGを問題なく使用できますが、常にそうであるとは限らないことに気付きました。

2つの異なるクエリメソッドを実行するのにかかる時間を確認するテストをセットアップしました。現在、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

ありがとうございました!

6
Matt Camp

現在、テーブルには約320kのレコードしかなく、ALLOW FILTERINGを問題なく使用できますが、常にそうであるとは限らないことに気付きました。

Cassandraはvery特定のキーによるデータのクエリに適しています。また、特定の範囲内のデータの範囲を取得するのにも優れています。パーティション。

_"SELECT * FROM {}.{} WHERE timestamp > {} ALLOW FILTERING;"
_

しかし、その分散された性質のため、結果セットをコンパイルするためにテーブル全体をスキャンすることは得策ではありません。そして、それはあなたが上記のクエリでそれをするように求めていることです。

ネットワークトラフィックは高価です。したがって、Cassandraの主な目標は、クエリが単一のノードによって確実に処理されるようにすることです。パーティションキー(名前)を指定せずに_ALLOW FILTERING_を使用すると、クエリでコーディネーターノードが必要になり、クラスター内の各ノードでWHERE句に一致する可能性のある値を確認します。

基本的に、クラスターに含まれるノードが多いほど、パフォーマンスに悪影響を与える_ALLOW FILTERING_は(パーティションキーをに指定しない限りを除いて)、...クエリが単一のノードで処理できることを保証します)。遅いクエリは実際にこれを正しく行い、その問題を解決することに注意してください。

曜日となる可能性のある別のフィールドと、おそらく月フィールドも追加するというアイデアがありました。

そして、これは良い考えです!

2つの問題を解決します。

  1. これにより、クエリが単一のノードによって提供されることが保証されます。
  2. パーティションが大きくなりすぎないように保護します。

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

さらに、「datetime」の降順で行をクラスター化することにより、最新のデータが各セルの最上部にあることを保証します(Cassandra解析する必要が少なくなります)。

一意性を維持するために、「名前」を最後のクラスタリング列に移動しました。 「名前」でクエリを実行するつもりがない場合は、パーティションキーとして使用しても意味がありません。

お役に立てれば。

注:_timestamp int_を_datetime timestamp_に変更しました。これにより、例が明確になったためです。機能するものは何でも使用できますが、データ型に基づいて列に名前を付けることに起因する混乱に注意してください。

20170826を編集

以下はあなたのコードと同じですか、それとも異なりますか? PRIMARY KEY ((daybucket, datetime), name)

いいえ、同じではありません。これは、複合パーティションキーと呼ばれるものを使用しています。これにより、クラスター内のデータ分散が向上しますが、クエリが困難になり、基本的にはテーブルスキャンを実行できるようになります。

Cassandra主キーの適切で包括的な説明については、Carlo BertucciniがStackOverflowでgreatと答えています:

https://stackoverflow.com/questions/24949676/difference-between-partition-key-composite-key-and-clustering-key-in-cassandra/24953331#24953331

Cassandraがタイムスタンプを読み取る方法を変更する方法、またはデータフィールド全体を変更してタイムスタンプを変更して正しく読み取られるようにする簡単な方法はありますか?

あんまり。 Cassandraタイムスタンプは扱いが難しい場合があります。ミリ秒の精度で保存されますが、実際にはクエリ時に完全な精度showではありません。また、 2.1パッチの1つでは、時刻がGMTで自動的に表示されるため、ユーザーも混乱する可能性があります。アプリケーション側でタイムスタンプを管理する方法が機能している場合は、そのまま使用してください。

9
Aaron