ClickHouseのアーキテクチャとOLAPの特性
データベースシステムは主にOLTP(オンライントランザクション処理)とOLAP(オンライン分析処理)の2つのカテゴリに分類されます。OLTPは頻繁な挿入、更新、削除操作を伴うトランザクション指向のシステムであるのに対し、OLAPは大量の履歴データを蓄積し、複雑な分析クエリを高速に実行するために設計されています。
OLAPデータベースの主な特徴
- リクエストの大部分は読み取り操作である。
- データの更新は単一行ではなく、数千行以上の大規模なバッチで行われるか、全く更新されない。
- 一度挿入されたデータは変更されない(イミュータブル)。
- クエリ時は多数の行を読み取るが、対象となる列は全体の一部に限定される(列指向ストレージの利点)。
- 多数の列を持つワイドテーブル(ファクトテーブル)が一般的である。
- 同時実行されるクエリ数は比較的少ないが、単一クエリあたりのスループット要件が高い。
- シンプルなクエリであっても、数十ミリ秒程度のレイテンシが許容される。
- 列に格納されるデータは主に数値や短い文字列など、比較的小さなサイズである。
- 厳密なトランザクション(ACID特性)や高いデータ一貫性よりも、分析パフォーマンスが優先される。
- クエリの結果セットは、スキャンされたソースデータよりもはるかに小さくなる(集計処理)。
ClickHouseのインストールと初期設定
Linux環境(RPMベース)におけるClickHouseの導入手順を解説します。
パッケージの取得とインストール
公式リポジトリからRPMパッケージ(clickhouse-common-static, clickhouse-server, clickhouse-client, clickhouse-common-static-dbg)をダウンロードし、任意のディレクトリ(例: /opt/clickhouse/installer)に配置します。
cd /opt/clickhouse/installer
sudo rpm -ivh *.rpm
インストール中にデフォルトユーザーのパスワード設定プロンプトが表示される場合があります。セキュリティを確保するため、本番環境では強力なパスワードを設定してください。設定は /etc/clickhouse-server/users.d/default-password.xml に保存されます。
サービスの起動とステータス確認
sudo systemctl start clickhouse-server
sudo systemctl enable clickhouse-server
sudo systemctl status clickhouse-server
リモートアクセスの有効化
デフォルトではローカルホストからのみ接続が許可されています。外部ツール(DataGripやDBeaverなど)から接続するには、設定ファイル /etc/clickhouse-server/config.xml を編集し、<listen_host> タグのコメントを解除して :: または 0.0.0.0 を指定します。ポート番号を変更する場合は <tcp_port> を修正します(例: 9001)。
sudo systemctl restart clickhouse-server
clickhouse-client --port 9001 --host 127.0.0.1 --password 'YourSecurePassword'
データ型とテーブル定義
ClickHouseは豊富なデータ型を提供しています。テーブル作成時には大文字と小文字が厳密に区別されるため、Int32 を int32 と記述するとエラーになります。また、テーブル作成時には必ずテーブルエンジンを指定する必要があります。
主要なデータ型
- 整数型:
Int8からInt256、および符号なしのUInt8からUInt256までサポート。 - 文字列型: 可変長文字列は
String、固定長文字列はFixedString(N)を使用します。固定長の方が特定の条件下でパフォーマンスが向上します。 - 日付と日時:
Date(日付のみ)、DateTime(秒精度)、DateTime64(ミリ秒/マイクロ秒精度)。 - UUID:
UUID型とgenerateUUIDv4()関数を組み合わせて使用。 - Null許容型:
Nullable(T)を使用すると、特定の型でNULL値を保持できます。ただし、インデックスやパフォーマンスに悪影響を与える可能性があるため、多用は避けます。 - 配列型:
Array(T)で配列を定義します。MergeTreeエンジンでは多次元配列や配列のネストに制限がある場合があります。 - Decimal型:
Decimal(P, S)で精度(P)とスケール(S)を指定し、金融データなどの正確な計算が必要な場合に使用します。
テーブル作成とデータ挿入の例
-- テーブルの作成
CREATE TABLE analytics.user_activities (
activity_id UUID DEFAULT generateUUIDv4(),
user_id UInt64,
action_type String,
properties Array(String),
event_time DateTime64(3),
revenue Decimal(10, 2)
) ENGINE = TinyLog;
-- データの挿入
INSERT INTO analytics.user_activities (user_id, action_type, properties, event_time, revenue)
VALUES
(88321, 'PURCHASE', ['item:992', 'cart:web'], '2023-10-25 14:30:00.123', 150.50),
(88322, 'VIEW', ['page:home'], '2023-10-25 14:31:15.456', 0.00);
-- テーブル構造の確認
DESCRIBE TABLE analytics.user_activities;
データベースエンジンとテーブルエンジン
ClickHouseのアーキテクチャにおいて、エンジンはデータがどのように保存され、処理されるかを決定する中核コンポーネントです。
データベースエンジン
- Atomic: デフォルトのデータベースエンジン。非ブロッキングな
DROP TABLEやRENAME TABLEをサポートし、トランザクションの原子性を保証します。 - MySQL: リモートMySQLサーバーのデータベースをClickHouseにマッピングします。ClickHouse側からMySQLに対して
SELECTやINSERTを実行でき、データレイクハウス構築時のフェデレーションクエリに有用です。
-- MySQLデータベースエンジンの使用例
CREATE DATABASE mysql_mirror
ENGINE = MySQL('db-server.local:3306', 'ecommerce_db', 'reader_user', 'SecurePass123');
テーブルエンジン
ログファミリー (Log, TinyLog, StripeLog)
これらは小規模なデータや一時的なデータ、Write-Onceのユースケースに適しています。インデックスはサポートされず、大量のデータ分析には不向きです。TinyLog は最もシンプルで、StripeLog はデータをストライプ単位でファイルに保存し、並列読み取りを可能にします。
MergeTreeファミリー
ClickHouseの最も強力で一般的なエンジン群です。大量データの挿入、バックグラウンドでのデータマージ、パーティショニング、レプリケーション、サンプリングをサポートします。
-- MergeTreeエンジンの実践例
CREATE TABLE analytics.sales_facts (
transaction_id String,
store_id UInt16,
product_category String,
amount Float64,
sale_date Date
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(sale_date)
ORDER BY (store_id, product_category, transaction_id)
TTL sale_date + INTERVAL 3 YEAR
SETTINGS index_granularity = 8192;
-- データ挿入と手動マージ
INSERT INTO analytics.sales_facts VALUES
('TX-001', 101, 'Electronics', 1200.00, '2023-11-01'),
('TX-002', 102, 'Apparel', 85.50, '2023-11-01');
-- パーティションのマージを明示的にトリガーする(通常はバックグラウンドで自動実行される)
OPTIMIZE TABLE analytics.sales_facts FINAL;
組み込み関数の活用
ClickHouseは多様な組み込み関数を提供しており、クエリ内でのデータ変換や計算を高速に処理します。
算術関数と比較関数
算術演算(plus, minus, multiply, divide)は、オペランドのデータ型に基づいて自動的に最適な戻り値の型を決定します。日付型に対して整数を加算すると、日数として解釈されます。比較演算子(=, !=, <, > など)は常に UInt8 型(0または1)を返します。異なるデータ型グループ(例:文字列と日付)を直接比較することはできず、明示的な型変換が必要です。
型変換関数
toInt32(), toString(), toDate() などの関数を使用して型変換を行います。大きな型から小さな型への変換(例: Int64 から Int8)ではデータが切り捨てられる可能性があるため、注意が必要です。
-- 関数と型変換のクエリ例
SELECT
transaction_id,
amount,
multiply(amount, 1.1) AS amount_with_tax,
intDiv(toUInt32(amount), 100) AS hundreds_block,
toString(sale_date) AS date_string,
equals(store_id, 101) AS is_flagship_store
FROM analytics.sales_facts
WHERE greaterOrEquals(amount, 100.0);