文章封面圖: ClickHouse 是什麼:特色與一步步安裝(2026 指南)

ClickHouse 是什麼:特色與一步步安裝(2026 指南)

發佈於:

閱讀時間: 5 min

主題: 技術

作者: Leandro Valencia

#ClickHouse#OLAP#資料分析#開源#自架#SQL

給獨立開發者的 ClickHouse 實踐指南:它是什麼、為什麼這麼快、關鍵特性,以及如何用 curl 或 Docker 在 5 分鐘內裝好。

目錄

ClickHouse 是什麼(不講行銷話術)

ClickHouse 是一個開源的欄式分析資料庫,用 C++ 寫成,設計目的是以「看起來像量測誤差」的速度對大資料量做彙總查詢。

關鍵詞是分析。在資料庫行話裡,這指的是 OLAP(線上分析處理),與 PostgreSQL 或 MySQL 做的 OLTP(線上交易處理)相對。

這個差別比看起來重要:

OLTP(Postgres、MySQL、SQLite) 被最佳化來非常快地讀寫單列。「給我使用者 4821」。「更新這個訂單的狀態」。每秒數千次小操作,帶交易和強一致性。

OLAP(ClickHouse、DuckDB、BigQuery) 被最佳化來讀很多列但很少欄。「給我過去 90 天按國家彙總的收入」。它不在乎某具體一列,它在乎的是掃掉數百萬列並壓成一個數。

如果你想看這個類別的完整背景——歷史、使用情境和供應商地圖——我在 OLAP 資料庫是什麼 裡展開講過。

讓它成為可能的技術關鍵是欄式儲存。一個按列的資料庫在磁碟上這樣存資料:

[id:1, país:ES, ingresos:42, timestamp:...][id:2, país:MX, ingresos:17, timestamp:...]

你要加總 ingresos 欄,就得把所有東西都讀出來,包括你並不關心的 paístimestamp。ClickHouse 把每一欄存到自己的檔案裡:

ingresos: [42, 17, 88, 12, ...]
país:     [ES, MX, ES, AR, ...]

要加總 ingresos,你只讀這個檔案。如果你的表有 40 欄,而你的查詢用 2 欄,你只讀了 5% 的資料。魔法就是從這開始的,剩下的一切都在放大它。

為什麼這麼快:四個真正的原因

1. 殘酷的壓縮。 當你把同型別的值放在一起、而且還排好序,模式就會重複,壓縮演算法變得非常高效。ClickHouse 文件給的壓縮比,從自由文字的 2x 一路到排好序的低基數欄位的 1,000x 以上。磁碟上位元組更少 = I/O 更少 = 查詢更快。

2. 稀疏主索引。 ClickHouse 不為每列建一條 B 樹索引。它建的是稀疏索引:每 8,192 列(一個「顆粒」)一條。這意味著每 TB 資料只要幾 MB 索引,輕鬆裝進記憶體。代價是 ClickHouse 永遠不查精確的一列,而是查它所在的區塊。對分析來說,這是完全正確的取捨。

3. 向量化 + 平行引擎。 操作不是逐列執行,而是用 SIMD 指令在欄的區塊上執行。而且會積極地平行到所有可用核心。在 8 核筆電上你就感覺得到差別;在 64 核伺服器上是另一個級別。

4. 讀寫分離。 插入會建立新的「part」,在背景合併(LSM 風格的架構)。插入不阻塞查詢,查詢也不阻塞插入。

你應該知道的主要特性

這些才是真正會改變你專案設計的東西,而不是 datasheet 上的完整清單。

單一二進位、零依賴

這聽起來像個小細節,但對小專案來說很可能是最重要的一點。ClickHouse 以單一可執行檔散布,內含伺服器、用戶端和本地模式。沒有 JVM、沒有強制的 ZooKeeper、沒有編排器。你下載一個檔案,執行它,你就有一個分析資料庫。

把它和啟動 Apache Druid 對比一下——Druid 需要六種不同的行程。對單打獨鬥的獨立開發者來說,這個差別決定了專案是上線還是死在 docker-compose.yml 裡。

clickhouse-local:對檔案跑 SQL、不用起伺服器

你可以直接從磁碟或 URL 查 CSV、Parquet 或 JSON,不用匯入任何東西,也不用起伺服器:

./clickhouse local --query "
  SELECT country, count() AS visitas
  FROM file('logs.csv', CSVWithNames)
  GROUP BY country
  ORDER BY visitas DESC
  LIMIT 10
"

這能取代用 pandas 寫的快速探索腳本,在大檔案上明顯更快。

增量具體化檢視

這是最改變我構建方式的一個特性。ClickHouse 的具體化檢視不是會刷新的快取:它是一個插入觸發器。每次你往源表插入,這個區塊上的彙總就被算出來、寫到目標表裡。

結果是:你把成本從查詢時刻挪到了插入時刻。你的 dashboard 查的是一張 4,000 筆的預彙總表,而不是 4 億筆的原始資料。我在進階指南裡深入講,但先把這個概念記住。

完整 SQL 支援加上真 JOIN

多年來對 ClickHouse 的標準批評是「JOIN 差」。現在已經站不住腳了。它支援所有標準 JOIN 型別,有一個用欄位統計來重排 JOIN 的最佳化器,還加了標準 SQL 裡沒有的型別,比如 ASOF JOIN(按最接近的時間點而不是精確值來連接),這對時序和金融資料是純金。

它還用數百個分析函式擴充 SQL:uniqExactquantileTDigestwindowFunnelsequenceMatch。在 Postgres 裡要寫 60 列 CTE 的東西,在這裡是一個函式。

相容 70+ 種格式和資料湖

它能讀寫 Parquet、Iceberg、Delta Lake、Avro、Protobuf、各種 JSON。它能直接查 S3 上的檔案而不攝入。這意味著它不會把你鎖住:哪天你想遷移,一個陳述式資料就以 Parquet 出來了。

不爆 schema 的 JSON

你可以用原生 JSON 型別攝入半結構化資料,它內部以欄式儲存。不用提前設計完美 schema 就能攝入形狀各異的事件,而仍然保有欄式資料庫的效能。

我的看法:什麼時候 ClickHouse 是對的選擇、什麼時候不是

這是大多數教學會讓你失望的地方,因為它們是想賣你東西的人寫的。

ClickHouse 是好主意,如果:

你有一張停不下來地增長的事件表——產品分析、日誌、指標、點擊、IoT 遙測——而你的查詢是對時間範圍做彙總。這是經典使用情境,也是它會像魔法的地方。

你在做面向使用者的分析:你產品裡的一個 dashboard,很多客戶同時查。ClickHouse 以毫秒級延遲處理高並發,而這正是 Snowflake 這類數倉變貴的地方。

你的 BigQuery 或 Snowflake 帳單讓你焦慮。一台每月 30-60 歐的 ClickHouse 專屬伺服器,能扛下在按用量計費的數倉上貴一個數量級的負載。對 bootstrap 的專案來說這不是最佳化,是可行和不可行的差別。

ClickHouse 是壞主意,如果:

你需要頻繁按列更新和刪除。ClickHouse 在這方面進步很大(輕量 mutation、ReplacingMergeTree、輕量刪除),但它仍然是一個為「寫一次、讀多次」設計的引擎。如果你的負載是 CRUD,用 Postgres。

你需要多表 ACID 交易。交易支援有限,而且不是專案目標。別把訂單狀態放在這裡。

你的資料少於幾百萬筆。50 萬筆的表,Postgres 加一個像樣的索引就能毫秒級回答。加 ClickHouse 是沒有收益的維運複雜度。小資料集的正確答案是不換資料庫。

你的團隊是 0 個人,而你已經要維護三個服務。這是又一個要監控、備份、升級的服務。只有效能痛點是真實的時候才值得,而不是預期中的時候。

我的實操規則:如果在你最大的表上跑一個 GROUP BY 在 Postgres 裡要超過 5 秒,而這條查詢是你產品的核心,就是時候看 ClickHouse 了。 在此之前,不要。

還有一點補充:對我最有效的模式不是取代 Postgres,而是把兩者放在一起。Postgres 繼續做交易的真相來源——使用者、訂單、設定——而 ClickHouse 透過 CDC 或複製任務接收事件表的副本。各自做各自擅長的事。

ClickHouse 一步步安裝

來點實操。根據你的需要,有三條路。

方式 1:用 curl 快速安裝(macOS、Linux、FreeBSD)

最直接的辦法。下載適合你系統的二進位:

curl https://clickhouse.com/ | sh

在 Linux 和 macOS 上,這還會把 clickhousectl(別名 chctl)裝到 ~/.local/bin,這是用來管理多個本地版本、在背景啟動伺服器、與 ClickHouse Cloud 連接的官方 CLI。

如果你只想要二進位、不要管理 CLI:

curl https://clickhouse.com/ | CLICKHOUSE_ONLY=1 sh

給 macOS 使用者的提示: 如果 Gatekeeper 抱怨無法驗證二進位的開發者,到「系統設定 → 隱私與安全性」裡批准執行。這是 macOS 對未簽章二進位的正常行為。

啟動伺服器:

./clickhouse server

在另一個終端機裡啟動用戶端:

./clickhouse client

你應該會看到類似這樣的輸出:

ClickHouse client version 24.5.1.117 (official build).
Connecting to localhost:9000 as user default.
Connected to ClickHouse server version 24.5.1.

local-host :)

資料存在當前目錄,伺服器重啟後還在。

方式 2:Docker(如果你已經在用容器,推薦)

當專案已經有 docker-compose.yml 時,這是我的預設選擇,因為它不弄髒系統、環境可重現。

docker run -d \
  --name clickhouse \
  -p 8123:8123 \
  -p 9000:9000 \
  -v clickhouse_data:/var/lib/clickhouse \
  --ulimit nofile=262144:262144 \
  clickhouse/clickhouse-server

這兩個連接埠很重要,一開始每箇人都搞混:

  • 8123 是 HTTP 介面。大多數 Web 用戶端、BI 工具和 curl 用它。
  • 9000 是原生 TCP 協定。更快更精簡;clickhouse-client 和原生驅動用它。

--ulimit nofile 這個參數實際上不可省:ClickHouse 會開啟很多檔案描述符,不提高上限的話負載下會出莫名其妙的錯誤。

寫成 docker-compose.yml:

services:
  clickhouse:
    image: clickhouse/clickhouse-server
    container_name: clickhouse
    ports:
      - "8123:8123"
      - "9000:9000"
    volumes:
      - clickhouse_data:/var/lib/clickhouse
      - ./config.d:/etc/clickhouse-server/config.d
    ulimits:
      nofile:
        soft: 262144
        hard: 262144
    environment:
      CLICKHOUSE_USER: creacosas
      CLICKHOUSE_PASSWORD: cambia_esto
      CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1

volumes:
  clickhouse_data:

連到容器裡的用戶端:

docker exec -it clickhouse clickhouse-client --user creacosas --password cambia_esto

方式 3:什麼都不裝

如果你只想試一下語法、感受速度,官方 playground 裡有已載入的真實資料集,並能在瀏覽器裡執行查詢。零摩擦。

你的第一批查詢:從零到真實資料

我們來做點接近真實情境的東西:一個網站的事件表。

建庫建表

CREATE DATABASE creacosas;

USE creacosas;

CREATE TABLE eventos
(
    fecha       Date,
    timestamp   DateTime,
    usuario_id  UInt32,
    evento      LowCardinality(String),
    pais        LowCardinality(String),
    url         String,
    duracion_ms UInt32
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(fecha)
ORDER BY (evento, fecha, usuario_id);

這裡有三處值得展開的決策,因為它們是ClickHouse 式的決策:

ENGINE = MergeTree 是預設表引擎,90% 的情況你都用它。整個 MergeTree 家族(ReplacingMergeTreeSummingMergeTreeAggregatingMergeTree)共享同一個底座,在 part 合併上加了額外行為。

ORDER BY 是你整個 schema 裡最重要的決策。它定義了資料在磁碟上的實體順序,預設也是主鍵。規則:把你最常用來過濾、且基數更低的欄位放前面。我們幾乎總是按事件型別和日期過濾,所以它們在前。這裡搞錯,是「ClickHouse 慢」的頭號原因。

LowCardinality(String) 是一個會做字典編碼的型別。如果一欄的不同值少於約 10,000 個(國家、事件型別、方案名),把它包進 LowCardinality 會減少儲存並明顯加速過濾。這是一個幾乎沒人一開始會用的免費收益。

插入測試資料

我們來產生 1000 萬筆合成資料,好有東西玩:

INSERT INTO eventos
SELECT
    toDate('2026-01-01') + (number % 220)                       AS fecha,
    toDateTime(fecha) + (number % 86400)                        AS timestamp,
    (number % 50000) + 1                                        AS usuario_id,
    ['pageview','click','signup','purchase'][(intHash32(number) % 4) + 1]      AS evento,
    ['ES','MX','AR','CO','CL','US'][(intHash32(number + 7) % 6) + 1]           AS pais,
    concat('/pagina/', toString(number % 500))                  AS url,
    (number % 5000) + 100                                       AS duracion_ms
FROM numbers(10000000);

numbers() 函式即時產生列:這是 ClickHouse 裡不下載任何東西就能造測試資料集的標準辦法。我對類別欄位不用 number % N 而用 intHash32(),是因為如果兩欄都用以有公因子的模數(比如 4 和 6),它們會相關,結果會人為地均勻。

查詢

SELECT
    pais,
    evento,
    count()                         AS total,
    round(avg(duracion_ms))         AS duracion_media,
    uniqExact(usuario_id)           AS usuarios_unicos
FROM eventos
WHERE fecha >= '2026-03-01'
  AND evento = 'purchase'
GROUP BY pais, evento
ORDER BY total DESC;

注意用戶端輸出的最後一行。它告訴你處理了多少列、讀了多少位元組、速度是多少:

6 rows in set. Elapsed: 0.021 sec. Processed 1.14 million rows, 8.32 MB (54.28 million rows/s., 396.19 MB/s.)

那個數字——總共 1000 萬列裡的「processed 1.14 million rows」——才是關鍵。ClickHouse 只讀了表的 11%,因為 ORDER BY (evento, fecha, ...) 讓它跳過了其他所有東西。最佳化 ClickHouse 幾乎總是圍繞降低這個數字。

一個小技巧:看磁碟上的實際大小

SELECT
    table,
    formatReadableSize(sum(data_compressed_bytes))   AS comprimido,
    formatReadableSize(sum(data_uncompressed_bytes)) AS sin_comprimir,
    round(sum(data_uncompressed_bytes) / sum(data_compressed_bytes), 2) AS ratio
FROM system.columns
WHERE database = 'creacosas'
GROUP BY table;

系統表(system.columnssystem.partssystem.query_log)是 ClickHouse 最讚的東西之一:引擎的全部內省都是可查詢的 SQL。

你會犯的錯(我都犯過)

一列一列地插入。 ClickHouse 討厭小插入。每次 INSERT 都在磁碟上建立一個新「part」,之後還要合併。一千次單列插入會產生一千個 part,伺服器會因 Too many parts 報錯而窒息。每次至少 1,000 筆批次插入——理想是 10,000 到 100,000——或者開啟 async_insert=1 非同步插入。

習慣性地用 Nullable 每個 Nullable 欄位都會多加一欄遮罩、並停用一些最佳化。如果你能用哨兵值(0、空字串、1970-01-01),就用。

把高基數欄位放在 ORDER BY 第一位。 如果你把 usuario_id 或 UUID 放前面,你就破壞了壓縮和跳區塊能力。低基數在前,永遠如此。

期望 UPDATE 像 Postgres 那樣運作。 能用,但操作很重。如果你的設計依賴不斷更新列,請重新考慮設計(或重新考慮資料庫)。

結論

ClickHouse 不是通用資料庫,而這正是它的價值:它一個方向的事——對大體量做彙總——做得比幾乎所有人都好,而且以相對於其能力而言小得離譜的維運佔用做到。一個二進位、零依賴,從筆電到數百節點用同一個引擎跑。

對單打獨鬥的獨立開發者來說,最後這一點才是決定性的。你不需要一個資料團隊來維運 VPS 上的 ClickHouse。你需要的是好好理解 ORDER BY,以及不要逐列插入。

如果你是從 Postgres 過來的、dashboard 奄奄一息,那就給它一個下午。初始學習曲線是幾個小時而不是幾週,而效能躍升是那種不用量測都能感覺到的。

在本系列的下一批文章裡,我們深入裝好之後的兩個問題:


常見問題

ClickHouse 免費嗎?

引擎是 Apache 2.0 開源的,你可以無成本、無功能限制地自架。ClickHouse Cloud 是付費的託管服務,採用運算與儲存分離、按用量計費的模型,無活動時可以縮到零。

ClickHouse 能取代 PostgreSQL 嗎?

一般情況下不能。它們是幹不同活的工具:Postgres 做交易和應用狀態,ClickHouse 做分析。常見模式是兩個都用,Postgres 當真相來源,ClickHouse 接收事件資料。

多大資料量才值得?

作為實操參考,事件表從幾千萬筆開始,差別就很明顯了。少於幾百萬筆的話,索引良好的 Postgres 就夠了,維運也更簡單。

在 Windows 上能跑嗎?

可以,透過 Docker 或 WSL2,這是推薦路徑。也可以透過 clickhousectl 安裝。

該用什麼表引擎?

90% 的情況用 MergeTree。需要按鍵去重就用 ReplacingMergeTree,作為彙總具體化檢視的目標就用 SummingMergeTreeAggregatingMergeTree

相關文章

繼續探索您可能感興趣的相似內容

ClickHouse 是什麼:特色與一步步安裝(2026 指南)