# ClickHouse 是什麼?一個新手的學習與實作紀錄
### 引言
近期在工作上開始需要接觸 **ClickHouse**,但對我來說這是一個完全陌生的資料庫系統。過去大多使用的是 Relational Database (關聯式資料庫),對於 ClickHouse 這類主打高效能分析查詢的 OLAP(線上分析處理)Database,其實並沒有太多實務經驗。
因此撰寫這篇文章,一方面是作為自己學習 ClickHouse 的紀錄,另一方面也希望透過「寫出來」的方式,幫助自己更好地理解與內化相關概念。
本文內容將以新手角度出發,著重在 ClickHouse 的基本概念、使用情境與實際操作體驗,不會深入過於底層或艱澀的實作細節。如果你也正準備第一次接觸 ClickHouse,希望這篇文章能成為一個友善的入門起點。
### Why? Why we use this cool stuff?
這邊不免俗的先說一下官方對這個產品的應用場景
1. 實時分析
2. 可觀察性
3. 作為資料庫
4. 機器學習和生成式 AI
簡單來說,ClickHouse 最擅長處理的是 **需要高速分析和查詢大規模資料** 的場景
#### 那他的大規模是多大呢?
他的官方文件在 Abstract 的地方提及他可以處理 PB 級 (Petabyte, 千兆 byte) 的資料量
> This paper presents an overview of ClickHouse, a popular opensource OLAP database designed for high-performance analytics over petabyte-scale data sets with high ingestion rates.
> [ClickHouse Architecture Overview](https://clickhouse.com/docs/academic_overview?utm_source=chatgpt.com)
> 取自 Abstract
### What? What is ClickHouse?
在我們進入主題之前,必須先釐清兩個經常被一起提到、但用途完全不同的概念:
OLTP 與 OLAP。
#### What is OLTP?
> OLTP
> Online Transaction Precessing, 線上交易處理
是我們日常開發中最熟悉的資料庫使用情境
他的特點是:
- 高頻率、短時間的操作
- 以 新增(INSERT)/ 更新(UPDATE)/ 刪除(DELETE) 為主
- 通常只影響少量資料
- 強調交易正確性與一致性(ACID)
像 MySQL、PostgreSQL、MSSQL 這類傳統的 Relational DB,大多都是針對 OLTP 場景進行最佳化設計
> Relational DB 可以同時支援 OLTP 和 OLAP,只是通常來說是 OLTP
#### What is OLAP?
> OLAP
> Online Analytical Processing, 線上分析處理
> 它描述的是一種針對大量資料做複雜查詢與分析的方式,而不是一般的交易式查詢。
OLAP 關心的不是「單筆資料是否成功寫入」,而是:
- 對 大量歷史資料 進行查詢
- 複雜的聚合、統計與分群分析
- 查詢時會掃描大量資料,但寫入相對較少
- 目的是產生報表、洞察趨勢或分析結果
#### OLTP V.S. OLAP
> OLTP 關心的是「一筆資料快不快、準不準」
> OLAP 關心的是「一大堆資料算得快不快」
雖然傳統 relational database 理論上可以同時支援 OLTP 與 OLAP,但在實務上:
- 當資料量變大
- 查詢變得複雜(大量 GROUP BY / 聚合)
- 分析需求頻繁出現
OLAP 查詢往往會:
- 變得非常慢
- 影響原本的交易系統效能
| 項目 | OLTP | OLAP |
|---|---|---|
| 主要目的 | 處理日常交易與即時操作 | 分析大量歷史資料、產生報表與洞察 |
| 常見操作 | INSERT / UPDATE / DELETE | SELECT(大量聚合、統計、分群) |
| 查詢特性 | 短、快速、影響資料量小 | 複雜、耗資源、掃描大量資料 |
| 資料量 | 相對較小(單筆或少量資料) | 非常大(百萬、億級甚至以上) |
| 寫入頻率 | 高頻率寫入 | 寫入較少,讀取為主 |
| 查詢延遲需求 | 極低(毫秒等級) | 可接受較高延遲,但仍希望快 |
| 資料結構 | 高度正規化(Normalization) | 常為反正規化(Denormalization) |
| 儲存方式 | 多為行式儲存(Row-oriented) | 多為列式儲存(Column-oriented) |
| 典型使用情境 | 使用者登入、下單、付款 | 報表、趨勢分析、指標計算 |
| 常見資料庫 | MySQL、PostgreSQL、Oracle | ClickHouse、BigQuery、Redshift |
> 也就是說,當我們的需求從「穩定處理交易」轉向「快速分析大量資料」時,
就需要一種在設計上就為 OLAP 最佳化的資料庫。
>
> ClickHouse,正是在這樣的背景下誕生的。
#### What is ClickHouse?
在了解 OLAP 的概念之後,其實 ClickHouse 是什麼也就呼之欲出了。
**ClickHouse 就是一個專門用來「快速分析大量資料」的資料庫。**
當你的需求是:
- 對大量歷史資料做統計與分析
- 常常需要 `GROUP BY`、聚合、指標查詢
- 查詢效能開始成為瓶頸
那麼 ClickHouse,通常就是一個很合理的選擇。
ClickHouse 並不是要取代原本的 relational database,
而是用來處理 **OLAP(分析型)** 的需求,讓交易系統可以專心做好 OLTP。
### How? How can I connect to ClickHouse?
> 本篇文章是以 C# 作為範例,也可以去看看官方文件其他語言是怎麼連的
> [Language Client](https://clickhouse.com/docs/integrations/language-clients)
首先我們要在 proj 裡新增 ClickHouse.Driver 這個 package
```
dotnet add package ClickHouse.Driver
```
也可以使用 NuGet
```
Install-Package ClickHouse.Driver
```
要連接到 ClickHouse,官方文件推薦使用 ClickHouseConnection
下面是最基本的連線範例
#### 連到 ClickHouse
```
using ClickHouse.Driver.ADO;
using var connection = new ClickHouseConnection(
"Host=localhost;Port=8123;Database=default;Username=default;Password=mypassword;Protocol=http"
);
await connection.OpenAsync();
```
`Host`: ClickHouse Server location
`Username`, `Password`: 登入帳密
我這邊透過 docker 已經運行起 ClickHouse 了
可以參考我的 `docker.compose.yaml`
```
version: '3.8'
services:
clickhouse:
image: clickhouse/clickhouse-server:latest
container_name: clickhouse-server
environment:
CLICKHOUSE_DB: clickhousedemo
CLICKHOUSE_USER: default
CLICKHOUSE_PASSWORD: mypassword
ports:
- "8123:8123"
- "9000:9000"
ulimits:
nofile:
soft: 262144
hard: 262144
volumes:
- clickhouse_data:/var/lib/clickhouse
volumes:
clickhouse_data:
```
#### 建立 Table
```
var createTable = @"
CREATE TABLE IF NOT EXISTS demo_logs
(
timestamp DateTime,
service String,
latency_ms UInt32
)
ENGINE = Memory";
using (var cmd = connection.CreateCommand())
{
cmd.CommandText = createTable;
await cmd.ExecuteNonQueryAsync();
}
```
#### INSERT 一筆資料
```
using (var insert = connection.CreateCommand())
{
insert.CommandText = "INSERT INTO demo_logs (timestamp, service, latency_ms) VALUES (now(), 'demo_service_1', 123)";
await insert.ExecuteNonQueryAsync();
}
```
#### INSERT 多筆資料
```
using (var insertMany = connection.CreateCommand())
{
insertMany.CommandText = @"
INSERT INTO demo_logs (timestamp, service, latency_ms) VALUES
(now(), 'demo_service_2', 456),
(now(), 'demo_service_3', 78),
(now(), 'demo_service_4', 90)";
await insertMany.ExecuteNonQueryAsync();
}
```
#### 基礎的 SELECT 語法
```
using (var select = connection.CreateCommand())
{
select.CommandText = "SELECT timestamp, service, latency_ms FROM demo_logs";
using var reader = await select.ExecuteReaderAsync();
Console.WriteLine("Query Result : ");
while (await reader.ReadAsync())
{
var timestamp = reader.GetFieldValue<DateTime>(0);
var service = reader.GetFieldValue<string>(1);
var latency = reader.GetFieldValue<uint>(2);
Console.WriteLine($"[{timestamp}] Service: {service}, Latency: {latency} ms");
}
}
```
#### UPDATE and DELETE 資料
> ClickHouse 是針對 OLAP,所以不像 MySQL 那樣適合頻繁更新或刪除單筆資料
```
// UPDATE
using (var update = connection.CreateCommand())
{
update.CommandText = @"
ALTER TABLE demo_logs
UPDATE latency_ms = 200
WHERE service = 'demo_service_1'";
await update.ExecuteNonQueryAsync();
}
// DELETE
using (var delete = connection.CreateCommand())
{
delete.CommandText = @"
ALTER TABLE demo_logs
DELETE WHERE service = 'demo_service_4'";
await delete.ExecuteNonQueryAsync();
}
```
### 結語
ClickHouse 的核心價值在於 **快速分析大資料**,在 OLAP 場景中非常強大,但設計上偏向 **讀取與分析**,不適合做頻繁單筆更新