これまでMySQL、PostgreSQL、MongoDBをさまざまなプロジェクトで使ってきました — それぞれに強みがあります。しかし、チームが1日5億行のアクセスログを分析する必要が出てきたとき、どれも限界を見せ始めました。PostgreSQLでGROUP BYクエリに40秒かかり、MySQLはもはや論外でした。そこでClickHouseを試したところ、同じクエリが0.3秒で完了 — それ以来、元には戻れなくなりました。
インストールして5分で動かす
Ubuntu/DebianへのClickHouseインストール:
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg
curl -fsSL 'https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key' | sudo gpg --dearmor -o /usr/share/keyrings/clickhouse-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/clickhouse-keyring.gpg] https://packages.clickhouse.com/deb stable main" | sudo tee /etc/apt/sources.list.d/clickhouse.list
sudo apt-get update
sudo apt-get install -y clickhouse-server clickhouse-client
サービスを起動してすぐ接続:
sudo systemctl start clickhouse-server
sudo systemctl enable clickhouse-server
clickhouse-client
テーブルを作成して100万行のダミーデータをインサートしてみる:
CREATE TABLE access_logs (
event_time DateTime,
user_id UInt32,
page String,
status_code UInt16,
response_ms UInt32
) ENGINE = MergeTree()
ORDER BY (event_time, user_id);
-- テスト用に100万行インサート
INSERT INTO access_logs
SELECT
now() - randBinomial(1000000, 0.5),
rand() % 10000,
arrayElement(['/home', '/api', '/login', '/product'], rand() % 4 + 1),
arrayElement([200, 301, 404, 500], rand() % 4 + 1),
rand() % 2000
FROM numbers(1000000);
クエリを実行してみる:
SELECT page, count() AS hits, avg(response_ms) AS avg_ms
FROM access_logs
WHERE status_code = 200
GROUP BY page
ORDER BY hits DESC;
結果は1秒もかからずに返ってきます。これがClickHouseです。
なぜClickHouseはこんなに速いのか?
その秘密はカラム型ストレージにあります。MySQL/PostgreSQLは行単位でデータを保存するため、1行を読むと全カラムのデータを引き連れてきます。ClickHouseはその逆で、カラム単位で保存します。SELECT avg(response_ms)を実行すると、そのカラムだけを読み込み、残りには一切触れません。20カラムのテーブルで2〜3カラムしかクエリしない場合、I/Oが即座に6〜10分の1に削減されます。
さらに3つの要素が相乗効果をもたらします:
- ベクトル化クエリ実行:行を1件ずつ処理するのではなく、デフォルトで8192行のバッチ単位で処理し、CPUのSIMD命令を活用します — データがRAM上にある場合でもClickHouseが速い理由がここにあります
- データ圧縮:各カラムを適切なコーデックで個別に圧縮し、実際のデータはRAWと比べて通常5〜10分の1のサイズになります — 読み込み量が少なければ、それだけ速くなります
- MergeTreeエンジン:バックグラウンドで自動的にデータをソート・マージし、データがあらかじめ整列されているため、時刻やuser_idによる範囲スキャンが非常に高速です
ユースケースに合ったエンジンの選び方
ClickHouseには多くのテーブルエンジンがありますが、90%のケースで知っておくべきは次の3つです:
MergeTree — あらゆる用途のデフォルトエンジン
CREATE TABLE events (
date Date,
user_id UInt64,
action String,
value Float64
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(date)
ORDER BY (date, user_id)
TTL date + INTERVAL 90 DAY; -- 90日より古いデータを自動削除
PARTITION BYでデータを月単位に分割します — 月でフィルタリングするクエリは関連するパーティションだけをスキャンし、残りは完全にスキップします。TTLはログデータでよく使う機能で、古いデータを削除するcronジョブが不要になり、ClickHouseが自動的に処理してくれます。
ReplacingMergeTree — upsertが必要な場合
CREATE TABLE user_profiles (
user_id UInt64,
name String,
email String,
updated_at DateTime
) ENGINE = ReplacingMergeTree(updated_at)
ORDER BY user_id;
このエンジンは同じORDER BYキーを持つレコードの中で、updated_atが最新のものだけを保持します。注意点として、重複排除はバックグラウンドマージで非同期に行われるため、即座には反映されません。そのため、クエリ時にFINALを付けて正確な結果を保証する必要があります:
SELECT * FROM user_profiles FINAL WHERE user_id = 12345;
SummingMergeTree — 自動集計
CREATE TABLE daily_stats (
date Date,
page String,
views UInt64,
clicks UInt64
) ENGINE = SummingMergeTree()
ORDER BY (date, page);
マージのたびに、同じ(date, page)を持つ行のviewsとclicksをClickHouseが自動的に合算して1行にまとめます。数百の小さな行をそのまま保持する代わりに、すでに集計済みの1行だけが残るため、ダッシュボードのクエリが大幅に高速化されます。
CSVやMySQLからの実際のデータインポート
CSVファイルからインポート:
clickhouse-client --query="INSERT INTO access_logs FORMAT CSVWithNames" < access_logs.csv
MySQLからClickHouseへデータを移行する方法 — マイグレーションや分析データの同期時によく使います:
-- MySQLのソーステーブルを作成
CREATE TABLE mysql_orders
ENGINE = MySQL('mysql-host:3306', 'mydb', 'orders', 'user', 'password');
-- ClickHouseのローカルテーブルにコピー
INSERT INTO ch_orders SELECT * FROM mysql_orders WHERE created_at >= '2025-01-01';
プロダクション運用の実践的なTips
メモリ制限の設定
デフォルトではClickHouseはRAMを自由に使います。共有サーバーでは制限が必要です:
<!-- /etc/clickhouse-server/users.d/limits.xml -->
<clickhouse>
<profiles>
<default>
<max_memory_usage>8589934592</max_memory_usage> <!-- 8GB -->
<max_execution_time>60</max_execution_time> <!-- 60秒 -->
</default>
</profiles>
</clickhouse>
遅いクエリのモニタリング
-- 実行中のクエリを確認
SELECT query_id, elapsed, query
FROM system.processes
ORDER BY elapsed DESC;
-- クエリログ(履歴)を確認
SELECT query, query_duration_ms, read_rows, memory_usage
FROM system.query_log
WHERE event_date = today() AND query_duration_ms > 1000
ORDER BY query_duration_ms DESC
LIMIT 20;
HTTPインターフェース — スクリプトやモニタリングに便利
# HTTP経由でクエリ実行(ポート8123)
curl 'http://localhost:8123/?query=SELECT+count()+FROM+access_logs'
# またはPythonで使う場合
pip install clickhouse-connect
import clickhouse_connect
client = clickhouse_connect.get_client(host='localhost', port=8123)
result = client.query('SELECT page, count() FROM access_logs GROUP BY page')
for row in result.result_rows:
print(row)
ClickHouseを使うべきでないケース
よく質問されるので、はっきり書いておきます:ClickHouseはOLTPではありません。以下のようなワークロードであれば:
- 行レベルでの頻繁なUpdate/Delete
- ACIDトランザクション
- 単一の主キーによるクエリ(1行のルックアップ)
引き続きPostgreSQLやMySQLを使いましょう。ClickHouseが真価を発揮するのは数百万行を高速に集計する必要があるとき — 分析ダッシュボード、ログ解析、時系列メトリクス、BIレポートなどです。
実際に運用しているスタック:トランザクションデータにはPostgreSQL、分析にはClickHouseを使い — KafkaまたはバッチETLで5分ごとに同期しています。それぞれが自分の役割を果たし、どちらももう一方の代わりにはなれません。

