はじめに
ClickHouse とは、オープンソースソフトウェアの列指向のデータベースで、オンライン分析処理に特化した拡張性の高いソフトウェアです。
公式サイト
オンライン分析処理 ( OLAP ) とは蓄積されたデータを様々な視点から素早く分析を行う処理や操作のことをいいます。
最近は生成 AI の台頭により、大規模なリアルタイムデータ処理と分析ができるデータベースのニーズが増えてきました。
今回はその流行りに乗っかって、ClickHouse を実際に構築してみたいと思います。
環境
- macOS Sequoia v15.6.1
- Apple M1 Pro
ClickHouse の構築
公式ドキュメントでは、ソースからビルドする方法も紹介されていますが、Homebrew を使ったインストール方法も紹介されています。今回は後者のインストール方法で構築します。
インストール
Homebrew で以下のコマンドを実行します。
brew install --cask clickhouse
インストールが完了すると、以下のようなメッセージが表示されます。
🍺 clickhouse was successfully installed!
開発者認証エラーの対処
macOS では、初回実行時に「Apple は、このアプリに Mac に損害を与えたり、プライバシーを侵害する可能性のあるマルウェアが含まれていないことを検証できません」というエラーが表示される場合がありそうです。
ターミナルコマンドから解除もできますが、自分はシステム設定より解除しました。
こちらのセキュリティ緩和に関しては自己責任でお願いします。
- システム設定を開く
- プライバシーとセキュリティに移動
- 「このまま開く」ボタンをクリック
動作確認
インストールが正常に完了したか確認します。
clickhouse --version
ClickHouse local version 25.10.3.100 (official build).
バージョンが表示されれば、インストール成功です。
ClickHouse を動かしてみる
サーバーの起動
ClickHouse サーバーを起動します。
clickhouse server
起動すると、以下のようなログが表示されます。
2025.11.27 00:37:42.782748 [ 15304327 ] {} <Information> Application: Listening for http://[::1]:8123
2025.11.27 00:37:42.782748 [ 15304327 ] {} <Information> Application: Listening for native protocol (tcp): [::1]:9000
2025.11.27 00:37:42.783212 [ 15304327 ] {} <Information> Application: Ready for connections.
「Ready for connections.」と表示されれば、サーバーが正常に起動しています。
クライアントからの接続
別のターミナルを開いて、クライアントで接続します。
clickhouse client
接続に成功すると、以下のようなプロンプトが表示されます。
ClickHouse client version 25.10.3.100 (official build).
Connecting to localhost:9000 as user default.
Connected to ClickHouse server version 25.10.3.
your-hostname :)
簡単なクエリを実行
データベース一覧を確認してみましょう。
SHOW DATABASES
┌─name───────────────┐
1. │ INFORMATION_SCHEMA │
2. │ default │
3. │ information_schema │
4. │ system │
└────────────────────┘
4 つのデータベースが表示されれば、ClickHouse が正常に動作しています。
ClickHouse を使って列指向データベースの特性を確認する
列指向データベースである ClickHouse の特性を確認するため、行指向データベースの代表格である MySQL と同じ条件で性能を比較してみました。
テスト条件
- データ件数: 5000 万件
- テーブル: ClickHouse 公式チュートリアルで紹介されている UK 不動産価格データを模したテスト用テーブル
- 集計クエリ: 地区ごとの件数・平均価格・最大価格を集計
テーブル作成
ClickHouse と MySQL で同じ構造のテーブルを作成しました。
ClickHouse
CREATE TABLE uk_price_paid
(
price UInt32,
date Date,
postcode1 LowCardinality(String),
postcode2 LowCardinality(String),
type Enum8('terraced' = 1, 'semi-detached' = 2, 'detached' = 3, 'flat' = 4, 'other' = 0),
is_new UInt8,
duration Enum8('freehold' = 1, 'leasehold' = 2, 'unknown' = 0),
addr1 String,
addr2 String,
street LowCardinality(String),
locality LowCardinality(String),
town LowCardinality(String),
district LowCardinality(String),
county LowCardinality(String)
) ENGINE = MergeTree
ORDER BY (postcode1, postcode2, addr1, addr2);
MySQL
CREATE TABLE uk_price_paid (
price INT UNSIGNED,
date DATE,
postcode1 VARCHAR(10),
postcode2 VARCHAR(10),
type ENUM('terraced', 'semi-detached', 'detached', 'flat', 'other'),
is_new TINYINT,
duration ENUM('freehold', 'leasehold', 'unknown'),
addr1 VARCHAR(100),
addr2 VARCHAR(100),
street VARCHAR(100),
locality VARCHAR(100),
town VARCHAR(100),
district VARCHAR(100),
county VARCHAR(100),
INDEX idx_district (district)
) ENGINE=InnoDB;
データ挿入の比較
ClickHouse
INSERT INTO uk_price_paid
SELECT
rand() % 1000000 + 100000 AS price,
today() - rand() % 3650 AS date,
['SW1A', 'W1A', 'EC1A', 'N1', 'E1', 'SE1', 'NW1', 'WC1'][rand() % 8 + 1] AS postcode1,
toString(rand() % 10) || 'AA' AS postcode2,
['terraced', 'semi-detached', 'detached', 'flat', 'other'][rand() % 5 + 1] AS type,
rand() % 2 AS is_new,
['freehold', 'leasehold', 'unknown'][rand() % 3 + 1] AS duration,
toString(rand() % 100) AS addr1,
'' AS addr2,
['High Street', 'Main Road', 'Church Lane', 'Park Avenue'][rand() % 4 + 1] AS street,
['Central', 'North', 'South', 'East', 'West'][rand() % 5 + 1] AS locality,
'London' AS town,
['Westminster', 'Camden', 'Islington', 'Hackney', 'Tower Hamlets'][rand() % 5 + 1] AS district,
'Greater London' AS county
FROM numbers(50000000);
MySQL
INSERT INTO uk_price_paid (price, date, postcode1, postcode2, type, is_new, duration, addr1, addr2, street, locality, town, district, county)
SELECT
FLOOR(RAND() * 1000000) + 100000,
DATE_SUB(CURDATE(), INTERVAL FLOOR(RAND() * 3650) DAY),
ELT(FLOOR(RAND() * 8) + 1, 'SW1A', 'W1A', 'EC1A', 'N1', 'E1', 'SE1', 'NW1', 'WC1'),
CONCAT(FLOOR(RAND() * 10), 'AA'),
ELT(FLOOR(RAND() * 5) + 1, 'terraced', 'semi-detached', 'detached', 'flat', 'other'),
FLOOR(RAND() * 2),
ELT(FLOOR(RAND() * 3) + 1, 'freehold', 'leasehold', 'unknown'),
FLOOR(RAND() * 100),
'',
ELT(FLOOR(RAND() * 4) + 1, 'High Street', 'Main Road', 'Church Lane', 'Park Avenue'),
ELT(FLOOR(RAND() * 5) + 1, 'Central', 'North', 'South', 'East', 'West'),
'London',
ELT(FLOOR(RAND() * 5) + 1, 'Westminster', 'Camden', 'Islington', 'Hackney', 'Tower Hamlets'),
'Greater London'
FROM
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) t1,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) t2,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) t3,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) t4,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) t5,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) t6,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) t7,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) t8;
結果
| DB | 5000 万件の挿入時間 |
|---|---|
| ClickHouse | 約 24 秒 |
| MySQL | 約 10 分 52 秒 |
ClickHouse は挿入でも約 27 倍速い結果となりました。
集計クエリの比較
以下のクエリを実行しました。
SELECT
district,
COUNT(*) AS count,
AVG(price) AS avg_price,
MAX(price) AS max_price
FROM uk_price_paid
GROUP BY district
ORDER BY avg_price DESC;
| DB | 5000 万件の集計時間 |
|---|---|
| ClickHouse | 0.185 秒 |
| MySQL | 1 分 33 秒 |
ClickHouse は集計クエリで約 500 倍の速度差がありました。
なぜここまで差が出るのか
ClickHouse が高速な理由は、列指向データベースという設計にあります。
- 列指向: 集計に必要な列だけを読み込むため、I/O が最小限
- ベクトル化処理: CPU の SIMD 命令を活用した並列処理
- データ圧縮: 同じ列のデータは類似性が高く、高い圧縮率を実現
一方、MySQL のような行指向データベースは、1 行ずつ全ての列を読み込むため、大量データの集計には向いていません。
まとめ
- ClickHouse はオンライン分析処理に特化した列指向のデータベース
- 大規模データの分析処理には、列指向データベースである ClickHouse が非常に有効である