Clickhouse ํด๋ฌ์คํฐ ํ๊ฒฝ์์ ๋ฐ์ดํฐ ์ฝ์ ๋ฐ ์ ๋ฐ์ดํธ ์ฒ๋ฆฌ ๋ฐฉ์ ๐(part. 1)
์ด์ ๊ธ ๋ฐ์ดํฐ ๋ถ์ ํ๊ฒฝ ๊ตฌ์ถ - 06. Clickhouse ํ ์ด๋ธ ์ข ๋ฅ์ ์ฉ๋์ ์ด์ด Kubernetes ๊ธฐ๋ฐ ClickHouse ํด๋ฌ์คํฐ ํ๊ฒฝ์์ ์ฃผ์ ํ ์ด๋ธ ์์ง๋ณ ๋ฐ์ดํฐ ์ฝ์ ๋ฐ ์ ๋ฐ์ดํธ ์ฒ๋ฆฌ ๋ฐฉ์์ ์ดํด๋ณด๊ณ , ์ต์ ๊ธฐ๋ฅ๊ณผ ์ต์ ํ ๊ธฐ๋ฒ์ ์๊ฐํฉ๋๋ค.
1. ClickHouse Kubernetes ์ํคํ ์ฒ ๊ฐ์ ๐ ๏ธ
Kubernetes ํ๊ฒฝ์์ ClickHouse๋ ์ค๋(Shard)์ ๋ ํ๋ฆฌ์นด(Replica) ๊ตฌ์กฐ๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ๋ถ์ฐ ์ ์ฅํ๊ณ ๊ณ ๊ฐ์ฉ์ฑ์ ๋ณด์ฅํฉ๋๋ค.
1.1 Kubernetes ํด๋ฌ์คํฐ ๊ตฌ์ฑ
- ์ค๋(Shard): ๋ฐ์ดํฐ๋ฅผ ๋ถํ ํ์ฌ ์ ์ฅํ๋ ๋ ธ๋
- ๋ ํ๋ฆฌ์นด(Replica): ๋์ผ ๋ฐ์ดํฐ๋ฅผ ๋ณต์ ํ์ฌ ์ค๋์ ์ฅ์ ๋ฅผ ๋๋นํ๋ ๋ฐฑ์ ๋ ธ๋
- ZooKeeper: ๋ฉํ๋ฐ์ดํฐ ๊ด๋ฆฌ ๋ฐ ๋ ํ๋ฆฌ์นด ๋๊ธฐํ
์์ ์ํคํ ์ฒ
ClickHouse Cluster (2 Shards, 2 Replicas per Shard)
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โ Shard 1 โ โ Shard 2 โ
โ Replica 1, 2 โ โ Replica 1, 2 โ
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
2. ๋ฐ์ดํฐ ์ฝ์ (Insert) ์ฒ๋ฆฌ ๋ฐฉ์ ๐
2.1 Distributed + MergeTree ์กฐํฉ
ClickHouse ํด๋ฌ์คํฐ์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์
ํ ๋, ์ผ๋ฐ์ ์ผ๋ก Distributed
์์ง๊ณผ MergeTree
๊ณ์ด ์์ง์ ์กฐํฉํฉ๋๋ค.
โ ๋ฐ์ดํฐ ์ฝ์ ํ๋ฆ
- ํด๋ผ์ด์ธํธ ์์ฒญ: ์ฌ์ฉ์๊ฐ
Distributed
ํ ์ด๋ธ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ํฉ๋๋ค. - ์ค๋ฉ ๋ผ์ฐํ
:
Distributed
์์ง์ด ์ง์ ๋ ์ค๋ฉ ํค๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ ์ค๋๋ก ๋ถ๋ฐฐํฉ๋๋ค. - ๋ฐ์ดํฐ ์ ์ฅ: ๊ฐ ์ค๋์
MergeTree
ํ ์ด๋ธ์ด ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ๋ณํฉ(merge) ์์ ์ ์ํํฉ๋๋ค.
โ ํ ์ด๋ธ ์์ฑ ์์
-- ์ค๋๋ณ ๋ก์ปฌ ํ
์ด๋ธ
CREATE TABLE shard_table ON CLUSTER my_cluster
(
id UInt32,
event_time DateTime,
event_type String
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(event_time)
ORDER BY (id, event_time);
-- ๋ถ์ฐ ํ
์ด๋ธ
CREATE TABLE distributed_table ON CLUSTER my_cluster
(
id UInt32,
event_time DateTime,
event_type String
)
ENGINE = Distributed(my_cluster, default, shard_table, cityHash64(id));
โ ๋ฐ์ดํฐ ์ฝ์ ์์
INSERT INTO distributed_table VALUES
(1, '2025-04-01 10:00:00', 'login'),
(2, '2025-04-01 10:05:00', 'purchase'),
(3, '2025-04-01 10:10:00', 'logout');
โญ๏ธ ์ค์ ์์ - ์ด๋ฒคํธ ์ฒ๋ฆฌ
๐ฏ ์ค์ ์์ ์์๋ shard 2๊ฐ๋ฅผ ์์ฑํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค.
NAME READY STATUS RESTARTS clickhouse-shard0-0 1/1 Running 0 clickhouse-shard1-0 1/1 Running 0 clickhouse-zookeeper-0 1/1 Running 0
1๏ธโฃ MergeTree ์์ง์ ์ฌ์ฉํ ํ ์ด๋ธ์๋ง ๋ฐ์ดํฐ๋ฅผ ๋ฃ์์ ๋
-- ์ผ๋ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ฑ(Replicated X)
CREATE DATABASE IF NOT EXISTS regular_db;
-- ํ
์ด๋ธ ์์ฑ
CREATE TABLE IF NOT EXISTS regular_db.test_merge_tree (
id UInt64,
event_date Date,
event_time DateTime,
user_id String,
event_type String,
payload String
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_date, id);
-- ์์ ๋ฐ์ดํฐ Insert
INSERT INTO regular_db.test_merge_tree
SELECT
number as id,
toDate('2024-03-01') + (number % 30) as event_date,
toDateTime(toDate('2024-03-01') + (number % 30) + (number % 24) / 24) as event_time,
concat('user_', toString(1 + number % 1000)) as user_id,
multiIf(
number % 5 = 0, 'click',
number % 5 = 1, 'view',
number % 5 = 2, 'purchase',
number % 5 = 3, 'login',
'logout'
) as event_type,
concat('{"data":"', repeat('X', 10 + number % 50), '","value":', toString(number % 100), '}') as payload
FROM numbers(1000000);
์ค๋๋ณ ๋ฐ์ดํฐ ์ ์ฅ ์์น๋ณ ํฌ๊ธฐ(byte)์ row์ ํ์ธ
SELECT hostName(), sum(rows) AS total_rows, sum(bytes) AS total_bytes
FROM cluster('default', 'system', 'parts')
WHERE database = 'regular_db' AND table = 'test_merge_tree'
GROUP BY hostName();
- ๋ณ๋ ์ง์ ์์ด mergetree์ ์ง์ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ์ ๋ shard0-0์ ์ถ๊ฐ๋๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
2๏ธโฃ MergeTree ์์ง์ ์ฌ์ฉํ ํ ์ด๋ธ์ Distributed ์์ง์ ์ฌ์ฉํ ํ ์ด๋ธ์ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์์ ๋
- ๋ฐ์ดํฐ๋ฒ ์ด์ค, ํ ์ด๋ธ์ ์ฌ ์์ฑํ๊ณ ๋ฐ์ดํฐ ์ถ๊ฐ ํ ์คํธ๋ฅผ ์งํํฉ๋๋ค.
-- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ฑฐ ๋ฐ ์ฌ ์์ฑ
DROP DATABASE regular_db;
CREATE DATABASE IF NOT EXISTS regular_db on cluster default;
-- ํ
์ด๋ธ ์ฌ ์์ฑ
CREATE TABLE IF NOT EXISTS regular_db.test_merge_tree on cluster default (
id UInt64,
event_date Date,
event_time DateTime,
user_id String,
event_type String,
payload String
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_date, id);
-- ๋ถ์ฐ ํ
์ด๋ธ ์์ฑ
CREATE TABLE regular_db.distributed_table ON CLUSTER default
(
id UInt64,
event_date Date,
event_time DateTime,
user_id String,
event_type String,
payload String
)
ENGINE = Distributed(default, regular_db, test_merge_tree, cityHash64(id));
-- ๋ถ์ฐ ํ
์ด๋ธ์ ๋ฐ์ดํฐ ์ถ๊ฐ
INSERT INTO regular_db.distributed_table
SELECT
number as id,
toDate('2024-03-01') + (number % 30) as event_date,
toDateTime(toDate('2024-03-01') + (number % 30) + (number % 24) / 24) as event_time,
concat('user_', toString(1 + number % 1000)) as user_id,
multiIf(
number % 5 = 0, 'click',
number % 5 = 1, 'view',
number % 5 = 2, 'purchase',
number % 5 = 3, 'login',
'logout'
) as event_type,
concat('{"data":"', repeat('X', 10 + number % 50), '","value":', toString(number % 100), '}') as payload
FROM numbers(1000000);
์ค๋๋ณ row์์ byte๋ฅผ ํ์ธํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๋ถ์ฐ๋์ด ์ ์ฅ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
3. ๊ฒฐ๋ก ๐ก
Kubernetes ํ๊ฒฝ์์ ClickHouse๋ฅผ ์ด์ํ๋ฉด ํ์ฅ์ฑ๊ณผ ๊ณ ๊ฐ์ฉ์ฑ์ ์ป์ ์ ์์ต๋๋ค. ์ด๋ฅผ ์ํด ์ ์ ํ ํ ์ด๋ธ ์์ง, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ง ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
โ ๋ค์ ์๊ฐ์๋ ReplicatedMergetree ์์ง์ ์ค๋ช ํ๊ณ Distributed + Mergetree ๊ตฌ์ฑ๊ณผ ๋น๊ตํ์ฌ ์ค๋ช ํ๊ฒ ์ต๋๋ค. ๋ํ ์ด๋ค ์ํฉ์์ ๊ฐ ์กฐํฉ์ด ์ฌ์ฉ๋๋์ง ์ค๋ช ํฉ๋๋ค.