ストレージ構造
次の例では*MergeTree
、クリックハウスの最も一般的に使用される(マージツリー)サブクラスエンジンを紹介します。
論理的分割
分散テーブルを例にとると、ckデータはクラスターの下の複数のシャードシャードに格納されます。シャードがノード上にない場合、つまり、データは複数のマシンに分散されます。各シャードのデータは、テーブルの作成時に指定されたパーティションに従って分割され、単一のパーティションでは、データ容量が特定のしきい値を超えると、再度分割されます。
# 表结构:
${ck_data}/metadata/path_to_table/*.sql
# 实际数据存放目录:
${ck_data}/data/path_to_table/${partition_*}/**
# 装卸数据目录
${ck_data}/data/path_to_table/detached
柱状ストレージ
Clickhouseは実際の柱状データベース管理システムであり、基本的にデータ自体以外に追加のデータはありません。
クリックハウスのユニークなコンピューティングの利点を見てみましょう。
- マルチサーバー分散処理
ClickHouseでは、データをさまざまなシャードに保存できます。各シャードは、フォールトトレラントレプリカのセットで構成されています。クエリは、複数のサーバーによってすべてのシャードで並行して処理できます。 - ベクトルエンジン
CPUを効率的に使用するために、計算時にckをベクトル(列の一部)として処理できます。実際のデータ処理コストと比較して、ベクトル化処理の方が転送コストが低くなります。これにより、CPUをより効率的に使用できます。
スパースインデックス
Clickhouseで最も強力なテーブルエンジンは、間違いなく、MergeTreeエンジンとシリーズの他のエンジン(* MergeTree)です。MergeTreeエンジンシリーズの基本的な考え方は次のとおりです。テーブルに挿入するデータが大量にある場合は、データフラグメントをバッチで効率的に書き込む必要があり、これらのデータフラグメントが特定のルールに従ってバックグラウンドでマージされることを期待します。挿入中にデータをストレージに絶えず変更(再書き込み)するのと比較して、この戦略ははるかに効率的です。主な利点:
- 保存されたデータは、主キーでソートされます。
これにより、データを高速に取得するための小さなスパースインデックスを作成できます。 - パーティションキーが指定されている場合、パーティションは許可されます。
同じデータセットと同じ結果セットの場合、ClickHouseの一部のパーティション化された操作は、通常の操作よりも高速になります。クエリでパーティションキーが指定されている場合、ClickHouseはパーティションデータを自動的にインターセプトします。これにより、クエリのパフォーマンスも効果的に向上します。 - データコピーをサポートします。
これには、ReplicateMergeTreeシリーズのテーブルが使用されます。
公式ウェブサイトのユースケースをご覧ください。主キーとして(CounterID、Date)を使用します。並べ替えられたインデックスアイコンは次のようになります。
全部数据 : [-------------------------------------------------------------------------]
CounterID: [aaaaaaaaaaaaaaaaaabbbbcdeeeeeeeeeeeeefgggggggghhhhhhhhhiiiiiiiiikllllllll]
Date: [1111111222222233331233211111222222333211111112122222223111112223311122333]
标记: | | | | | | | | | | |
a,1 a,2 a,3 b,3 e,2 e,3 g,1 h,2 i,1 i,3 l,3
标记号: 0 1 2 3 4 5 6 7 8 9 10
次のようにクエリを指定すると、
CounterID in ('a', 'h')
サーバーは[0、3)と[6、8)の間隔でタグ番号のデータを読み取ります。
CounterID IN ('a', 'h') AND Date = 3
、サーバーは[1、3)と[7、8)の間隔でタグ番号のデータを読み取ります。
Date = 3
、サーバーはタグ番号が[1、10]の間隔にあるデータを読み取ります。上記の例は、インデックスの使用は通常、完全なテーブル記述よりも効率的であることを示しています。
インデックスがまばらな場合、追加のデータ読み取りが発生します。各データブロックの単一のプライマリキー間隔範囲のデータを読み取る場合、複数のindex_granularity * 2
行の追加データが読み取られます。ほとんどの場合、index_granularity = 8192
時間の経過とともに、ClickHouseとパフォーマンスは低下しません。
スパースインデックスを使用すると、膨大な数の行を持つテーブルを操作できます。これらのインデックスは永続メモリ(RAM)であるためです。ClickHouseは一意のプライマリキーを必要としません。したがって、同じ主キーで複数の行を挿入できます。以下の実際の構文を見てください。
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
)
ENGINE MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SETTINGS index_granularity=8192
- ENGINE-エンジン名とパラメーター。
ENGINE = MergeTree()。MergeTreeエンジンにはパラメーターがありません。 - PARTITION BY —パーティションキー。
月ごとに分割するには、式toYYYYMM(date_column)を使用できます。ここで、date_columnは日付列です。ここで、パーティション名の形式は「YYYYMM」になります。 - ORDER BY —テーブルのソートキー。
これは、一連の列または任意の式のタプルにすることができます。例:ORDER BY(CounterID、EventDate)。 - PRIMARYKEY-ソートキーとは異なるように設定する場合のプライマリキー。
デフォルトでは、プライマリキーはソートキー(ORDER BY句で指定)と同じです。したがって、ほとんどの場合、PRIMARYKEY句を指定する必要はありません。
ソースコードの実装を保存する
ストレージ部分に関連するロジックのほとんどは、/ src / Storagesの下に配置されます。
テーブルエンジン
テーブルの最上位の抽象化はIStorageであり、このインターフェイスのさまざまな実装はさまざまなテーブルエンジンになります。たとえば、StorageMergeTree、StorageMemoryなど、これらのクラスのインスタンスはテーブルです。
このインターフェイスには、読み取り、書き込み、変更、ドロップなど、多くの一般的なメソッドが含まれています。
- 読み取り
テーブルのreadStreamsメソッドは、複数のIBlockInputStreamオブジェクトを返し、データの並列処理を可能にします。これらの複数のデータブロック入力ストリームは、テーブルからデータを並列に読み取ることができます。次に、さまざまな変換を使用して、これらのデータストリームをカプセル化できます(式の評価など)。 、データフィルタリング)は個別に計算できます。
virtual Pipes read(
const Names & /*column_names*/,
const SelectQueryInfo & /*query_info*/,
const Context & /*context*/,
QueryProcessingStage::Enum /*processed_stage*/,
size_t /*max_block_size*/,
unsigned /*num_streams*/)
{
throw Exception("Method read is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED);
}
/** The same as read, but returns BlockInputStreams.
*/
BlockInputStreams readStreams(
const Names & /*column_names*/,
const SelectQueryInfo & /*query_info*/,
const Context & /*context*/,
QueryProcessingStage::Enum /*processed_stage*/,
size_t /*max_block_size*/,
unsigned /*num_streams*/);
- 書く
virtual BlockOutputStreamPtr write(
const ASTPtr & /*query*/,
const Context & /*context*/)
{
throw Exception("Method write is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED);
}
データフロー
- データブロックストリームは、データの
処理に使用されます。データブロックのデータストリームを使用して、どこかからデータを読み取ったり、データ変換を実行したり、どこかにデータを書き込んだりします。IBlockInputStreamには、次のデータブロックを取得するためのreadメソッドがあります。IBlockOutputStreamには、データのブロックをどこかに送信するための書き込みメソッドがあります。
たとえば、AggregatingBlockInputStreamからデータをプルすると、データソースからすべてのデータが読み取られて集約され、サマリーデータストリームが返されます。別の例:UnionBlockInputStreamは、多くの入力データソースといくつかのスレッドを受け取ります。複数のスレッドを開始して、複数のデータソースから並列にデータを読み取ります。 - データブロック
は、メモリ内のテーブルのサブセットを表すコンテナです。トリプルのコレクションでもあります:(IColumn、IDataType、columnname)
ストレージHA
生産には高可用性ストレージが不可欠です。ckの分散ストレージを見てみましょう。ハイブとの違いはまだ大きいです。まず、分散テーブルを使用してクエリを実現する必要があります。ckの分散テーブルはデータを直接格納しませんが、ビューの存在に似ていることに注意してください。読み取りは自動的に並行して行われます。読み取るとき、リモートサーバーテーブルのインデックス(存在する場合)が使用されます。
公式ウェブサイト構成
4つのノードexample01-01-1、example01-01-2、example01-02-1、example01-02-2を想定します。クラスター名はlogsです。
<remote_servers>
<logs>
<shard>
<!-- Optional. Shard weight when writing data. Default: 1. -->
<weight>1</weight>
<!-- Optional. Whether to write data to just one of the replicas. Default: false (write data to all replicas). -->
<internal_replication>false</internal_replication>
<replica>
<host>example01-01-1</host>
<port>9000</port>
</replica>
<replica>
<host>example01-01-2</host>
<port>9000</port>
</replica>
</shard>
<shard>
<weight>2</weight>
<internal_replication>false</internal_replication>
<replica>
<host>example01-02-1</host>
<port>9000</port>
</replica>
<replica>
<host>example01-02-2</host>
<secure>1</secure>
<port>9440</port>
</replica>
</shard>
</logs>
</remote_servers>
totalDateに従ってパーティション化された、ログクラスターの4つのノードすべてにtest1テーブルを作成します。
CREATE TABLE default.test1 on cluster logs
(`uid` Int32, `totalDate` String )
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/test1', '{replica}')
PARTITION BY totalDate ORDER BY totalDate SETTINGS index_granularity = 8192;
次に、クラスター内に分散テーブルtest1_allを作成します
-- 建分布式表指向test1
CREATE TABLE default.test1_all on cluster logs
as test1
ENGINE = Distributed(logs, default, test1, rand())
次に、いくつかのテストデータを分散テーブルに書き込んでから、特定のノードに移動して、検証のために元のテーブルをチェックします。シリーズ6にも同様の例があるため、ここでは繰り返しません。
利用可能な構成
ckは、レプリケーションテーブル+内部同期の実装の使用を推奨しています。まずinternal_replication
、上記の構成のプロパティを見てみましょう。falseに設定すると、コピーの整合性がチェックされないため、分散テーブルに挿入されたデータが2つのローカルテーブルに挿入されます。時間の経過とともに、コピーデータが多少異なる場合があります。
テーブルのコピー、ckデータのコピーは、サーバーレベルではなくテーブルレベルで提供されるため、サーバー内に複製されたテーブルと複製されていないテーブルの両方が存在する可能性があります。これは、ckとhiveのもう1つの大きな違いです。
以下の4つのレプリケーションモードを見てみましょう。
- 複製されていないテーブルの
場合、internal_replication = false挿入中に問題がなければ、2つのローカルテーブルのデータは同期されたままになります。ネットワークに問題が発生した場合、レプリケーションが発散する傾向があり、どちらが正しいレプリケーションであるかを簡単に判断する方法がないため、これを「貧乏人のレプリケーション」と呼びます。 - 複製
されていないテーブルの場合、internal_replication = trueデータはローカルテーブルにのみ挿入されますが、別のテーブルに転送するメカニズムはありません。したがって、異なるホスト上のローカルテーブルには異なるデータが表示され、分散テーブルにクエリを実行すると予期しないデータが表示される場合があります。明らかに、これはClickHouseクラスターを構成するための誤った方法です。 - テーブルをコピーします。internal_replication= true
分散テーブルに挿入されたデータは、ローカルテーブルの1つにのみ挿入されますが、レプリケーションメカニズムを介して他のホストのテーブルに転送されます。したがって、2つのローカルテーブルのデータは同期されたままになります。これは推奨される構成です。 - テーブルのコピー、internal_replication = false
データは2つのローカルテーブルに挿入されますが、テーブルのコピーのメカニズムは同時に重複データが削除されることを保証します。データは、挿入された最初のノードから他のノードにコピーされます。データの受信後に他のノードがデータの重複を検出した場合、データは破棄されます。この場合、レプリケーションは同期されたままですが、エラーは発生しません。ただし、ストリームの複製が繰り返されるため、書き込みパフォーマンスが大幅に低下します。したがって、この構成は実際には回避し、構成3を使用する必要があります。