ClickHouseのインストールと使い方:大規模データ向け高性能OLAPデータベース完全ガイド

Database tutorial - IT technology blog
Database tutorial - IT technology blog

これまで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)を持つ行のviewsclicksを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分ごとに同期しています。それぞれが自分の役割を果たし、どちらももう一方の代わりにはなれません。

Share: