1. ClickHouse 入门:从零开始认识这个列式存储怪兽

1.1 发展史:一个俄罗斯创业公司的意外走红

ClickHouse 的故事,得从 2009 年说起。

当时俄罗斯最大的搜索引擎 Yandex 面临一个头疼的问题——网页流量分析系统 Metrica 需要实时处理海量数据。传统的数据库根本扛不住。于是他们内部启动了一个叫 "ClickHouse" 的项目,目标很明确:在 1 秒内完成对数十亿行数据的聚合查询。

2016 年,Yandex 把这个项目开源了。说实话,刚开源那会儿关注度并不高。我记得 2017 年我第一次接触它时,社区文档还只有俄文版,啃起来相当痛苦。但真正用过一次后,我就被它的查询速度震撼到了——同样的 SQL,在 MySQL 上跑 30 秒的聚合查询,ClickHouse 只要 200 毫秒。

后来它迅速在国内外走红。现在 GitHub 上已经有 38k+ star,国内大厂几乎都在用。我个人觉得,它的成功不是因为什么花哨的概念,而是解决了真实世界中「快」这个核心痛点。

1.2 核心特性:为什么它能这么快?

ClickHouse 不是万能的,但在 OLAP 场景下,它确实快得离谱。原因在于几个关键设计:

  • 列式存储:只读取需要的列,IO 量大幅减少。你想想看,一张 200 列的表,如果只查 3 列,行式存储要读全部 200 列,列式存储只读 3 列,差距是数量级的。
  • 向量化执行:利用 CPU 的 SIMD 指令集,一次处理一批数据,而不是一行一行处理。我在项目中实测过,向量化比逐行处理快 5-10 倍。
  • 数据压缩:列式存储天然适合压缩,同一列的数据类型相同,压缩比通常能达到 5:1 甚至 10:1。我曾经把一个 500GB 的 MySQL 表导入 ClickHouse,磁盘占用只有 80GB。
  • 分布式架构:数据自动分片到多台机器,查询可以并行执行。理论上可以线性扩展。

一句话总结:ClickHouse 是为「读多写少、大宽表、高吞吐、低延迟」的 OLAP 场景量身定做的。

1.3 OLAP 与 OLTP:两个世界的对决

很多新手会问:ClickHouse 能不能替代 MySQL?我的回答是:别这么干,会出事的。

它们根本不是一个物种。来看个对比:

维度 OLTP(如 MySQL) OLAP(如 ClickHouse)
典型场景 订单系统、用户登录 报表分析、用户行为分析
读写模式 频繁小批量写入 批量导入,大量读取
查询特征 点查(查一条记录) 聚合扫描(查几亿行)
数据更新 支持频繁 UPDATE/DELETE 不支持或代价极高
存储格式 行式存储 列式存储
并发能力 高并发(数千 QPS) 低并发(数十 QPS)

避坑指南:我曾经见过一个团队把 ClickHouse 当 MySQL 用,频繁执行单行 INSERT 和 UPDATE,结果性能惨不忍睹。记住:ClickHouse 不适合做事务型系统,它只适合做分析型系统。

1.4 适用场景:什么时候该用它?

根据我的实战经验,以下场景 ClickHouse 表现极佳:

  • 用户行为分析:埋点数据、页面 PV/UV、漏斗分析
  • 监控与日志:服务器指标、应用日志、网络流量
  • BI 报表:销售日报、财务汇总、多维交叉分析
  • 时序数据:IoT 设备数据、金融行情数据

不适合的场景:

  • 需要频繁行级更新的业务系统
  • 需要事务 ACID 保证的场景
  • 单条数据查询(点查)

1.5 环境搭建:单机模式

先来个最简单的单机部署。我习惯用 Docker,省心。

# 拉取镜像
docker pull clickhouse/clickhouse-server:latest

# 启动容器
docker run -d --name clickhouse-server \
  -p 8123:8123 \
  -p 9000:9000 \
  clickhouse/clickhouse-server:latest

# 进入容器
docker exec -it clickhouse-server clickhouse-client

启动后,用客户端连上去试试:

clickhouse-client --host 127.0.0.1 --port 9000

# 查看版本
SELECT version();

# 建个表试试
CREATE TABLE test.event (
    event_date Date,
    event_type String,
    user_id UInt64,
    amount Float64
) ENGINE = MergeTree()
ORDER BY (event_date, event_type);

# 插入几条数据
INSERT INTO test.event VALUES 
('2024-01-01', 'click', 1001, 12.5),
('2024-01-01', 'view', 1002, 0),
('2024-01-02', 'click', 1003, 8.0);

# 查询
SELECT event_date, count() AS cnt, sum(amount) AS total
FROM test.event
GROUP BY event_date;

小提示:第一次建表时,ORDER BY 子句特别重要。它决定了数据的物理排序,直接影响查询性能。我一般把最常用的过滤字段放在前面。

1.6 环境搭建:集群模式

单机玩熟了,就该上集群了。集群模式需要配置 ZooKeeper 来协调节点。

我直接给一个三节点集群的 docker-compose 配置:

version: '3'
services:
  zookeeper:
    image: zookeeper:3.7
    ports:
      - "2181:2181"

  clickhouse-1:
    image: clickhouse/clickhouse-server:latest
    ports:
      - "8123:8123"
      - "9000:9000"
    volumes:
      - ./configs/node1/config.xml:/etc/clickhouse-server/config.xml
      - ./configs/node1/metrika.xml:/etc/clickhouse-server/metrika.xml

  clickhouse-2:
    image: clickhouse/clickhouse-server:latest
    ports:
      - "8124:8123"
      - "9001:9000"
    volumes:
      - ./configs/node2/config.xml:/etc/clickhouse-server/config.xml
      - ./configs/node2/metrika.xml:/etc/clickhouse-server/metrika.xml

  clickhouse-3:
    image: clickhouse/clickhouse-server:latest
    ports:
      - "8125:8123"
      - "9002:9000"
    volumes:
      - ./configs/node3/config.xml:/etc/clickhouse-server/config.xml
      - ./configs/node3/metrika.xml:/etc/clickhouse-server/metrika.xml

关键配置在 metrika.xml 里,需要定义分片和副本:

<yandex>
  <clickhouse_remote_servers>
    <my_cluster>
      <shard>
        <internal_replication>true</internal_replication>
        <replica>
          <host>clickhouse-1</host>
          <port>9000</port>
        </replica>
        <replica>
          <host>clickhouse-2</host>
          <port>9000</port>
        </replica>
      </shard>
      <shard>
        <internal_replication>true</internal_replication>
        <replica>
          <host>clickhouse-3</host>
          <port>9000</port>
        </replica>
      </shard>
    </my_cluster>
  </clickhouse_remote_servers>
</yandex>

注意:生产环境中,ZooKeeper 一定要单独部署,不要和 ClickHouse 混布。我曾经因为图省事把 ZK 和 ClickHouse 放一起,结果 ZK 磁盘写满导致整个集群挂了。血的教训。

1.7 知识体系总览

为了让你对本章内容有个整体印象,我画了张图:

ClickHouse 入门知识体系 ClickHouse 发展史 核心特性 OLAP vs OLTP 环境搭建 Yandex 内部项目 2016 年开源 列式存储 向量化执行 数据压缩 OLTP: 行式存储 OLAP: 列式存储 核心要点 • 列式存储 + 向量化 = 极速聚合 • 适合 OLAP,不适合 OLTP

这张图把本章的核心知识点串起来了。你可以看到,ClickHouse 的底层设计(列式存储、向量化执行)决定了它的性能上限,而 OLAP vs OLTP 的对比则帮你判断「什么时候该用它」。环境搭建是动手的第一步,后面的章节我们会一步步深入。