ClickHouse:从原理到实践,打造大数据实时分析的利器
副标题:揭秘列式存储+向量执行的性能魔法,解决TB级数据查询慢的痛点
摘要/引言
你有没有遇到过这样的场景?
电商运营要“实时看今天10点到12点的订单总额”,用Hive跑SQL要等30分钟,等结果出来趋势都变了;数据分析师要“统计1亿行用户行为中的TOP10活跃用户”,MySQL跑了5分钟还没出结果,最后报错“内存不足”;后端工程师要“分析过去一年的日志数据,找出异常请求”,用SparkSQL要先把数据从HDFS拉到内存,耗时又耗资源。
这些问题的核心矛盾是:传统数据系统(行式数据库、离线数仓)无法同时满足“海量数据”+“实时查询”的需求。而ClickHouse,正是为解决这个矛盾而生的“大数据实时分析利器”。
本文会带你从原理→实践→优化完整走一遍:
为什么ClickHouse能处理TB/PB级数据?(核心原理)如何快速搭建ClickHouse环境并跑通第一个分析任务?(实践步骤)怎样优化ClickHouse性能,避免踩坑?(最佳实践)
读完本文,你能掌握ClickHouse的核心优势,并用它解决实际工作中的“大数据查得慢”问题——比如把1亿行数据的聚合查询从5分钟缩短到0.1秒。
目标读者与前置知识
目标读者
数据分析师/BI工程师:需要快速处理海量数据,生成实时报表;大数据开发工程师:想替代Hive/SparkSQL做实时分析;后端工程师:需要存储和查询TB级日志/业务数据;对“实时大数据分析”感兴趣的技术爱好者。
前置知识
了解基本的SQL语法(比如SELECT、GROUP BY、WHERE);用过至少一种数据库(MySQL、PostgreSQL、Hive均可);会用命令行(比如Docker、SSH)。
文章目录
引言与基础问题背景:传统数据系统的“性能瓶颈”ClickHouse的核心原理:为什么它这么快?环境准备:5分钟搭建ClickHouse开发环境实践:从建表到查询,跑通第一个大数据分析任务关键优化:让ClickHouse性能再提升10倍的技巧常见坑与解决方案:避免踩别人踩过的雷未来展望:ClickHouse的下一个增长点总结
一、问题背景:传统数据系统的“性能瓶颈”
要理解ClickHouse的价值,得先搞清楚传统数据系统的痛点。我们把常见的系统分成两类,看看它们的问题:
1. 行式数据库(MySQL、PostgreSQL):适合小数据,不适合大数据
行式数据库的存储方式是“按行存”——比如一条用户记录(id=1, name=张三, age=25, gender=男)会被存在连续的磁盘块里。这种方式的优点是插入/更新快(因为一条记录在一个位置),但缺点是查询慢:
如果你要查“age>30的用户数”,行式数据库需要读取所有行的所有列(id、name、age、gender),然后过滤出age>30的行——哪怕你只需要age列,也得读整行数据,IO成本极高。当数据量超过1000万行时,MySQL的查询会变得很慢(比如查sum(amount)要5秒以上),甚至会因为内存不足报错。
2. 离线数仓(Hive、SparkSQL):适合大数据,不适合实时
离线数仓的存储方式是“按列存”(比如Hive的ORC、Parquet格式),解决了行式数据库的IO问题,但实时性差:
Hive需要把数据分成“批次”处理(比如每小时跑一次ETL),无法处理“实时查询”(比如查“刚刚10分钟的订单”);SparkSQL虽然比Hive快,但它是“基于内存的分布式计算”,启动任务需要时间(比如10秒以上),而且资源消耗大(要启动多个Executor)。
3. 痛点总结:我们需要什么?
我们需要一个系统,能同时满足:
海量数据:支持TB/PB级数据存储;实时查询:秒级甚至亚秒级返回结果;简单易用:用SQL就能查询,不用写复杂的MapReduce;低成本:不需要大量服务器资源(比如用普通SSD就能跑)。
而ClickHouse,刚好完美符合这四个需求。
二、ClickHouse的核心原理:为什么它这么快?
ClickHouse的全称是“Click Stream, Data Warehouse”(点击流数据仓库),由俄罗斯搜索引擎Yandex开发,2016年开源。它的核心优势来自三个“黑科技”:列式存储、向量执行、MergeTree引擎。
1. 黑科技1:列式存储——只读需要的列,减少IO
什么是列式存储?
假设我们有一张表,数据如下:
orders
| order_id | user_id | amount | create_time |
|---|---|---|---|
| 1 | 1001 | 100 | 2023-10-01 10:00:00 |
| 2 | 1002 | 200 | 2023-10-01 10:05:00 |
| 3 | 1001 | 150 | 2023-10-01 10:10:00 |
行式存储会把每一行存在连续的磁盘块里:
1,1001,100,2023-10-01 10:00:00 → 2,1002,200,2023-10-01 10:05:00 → 3,1001,150,2023-10-01 10:10:00
列式存储会把每一列存在连续的磁盘块里:
order_id列:1→2→3;user_id列:1001→1002→1001;amount列:100→200→150;create_time列:...
列式存储的优势
减少IO:如果要查“sum(amount)”,列式存储只需要读列,不用读其他列——IO量是行式存储的1/4(假设4列);更高压缩比:同一列的数据类型相同(比如
amount都是Float64),压缩率比行式存储高3-5倍(比如用LZ4压缩,列式存储的压缩率能达到10:1,行式只有3:1);支持向量执行:后面会讲,列式存储是向量执行的基础。
amount
2. 黑科技2:向量执行——用SIMD指令,一次算1000行
什么是向量执行?
传统数据库的执行方式是“逐行执行”(Row-by-Row):比如计算,会一行一行取
sum(amount)的值,加到总和里——就像你一次拿一个苹果,拿1000次。
amount
而ClickHouse的执行方式是“向量执行”(Vectorized Execution):把1000行数据打包成一个“向量”(Vector),用SIMD指令(单指令多数据)一次计算——就像你一次拿1000个苹果,只需要拿一次。
向量执行的性能提升
SIMD是CPU的硬件指令,比如Intel的SSE/AVX指令集,能同时处理8个Float32或4个Float64数据。ClickHouse用C++实现了向量执行引擎,把查询的每个操作(比如sum、filter、group by)都转换成向量运算,性能比逐行执行高3-10倍。
3. 黑科技3:MergeTree引擎——让数据“自动排序+分区”,加速查询
MergeTree是ClickHouse的默认存储引擎,也是它能处理海量数据的核心。它的设计目标是:让数据在存储时就“有序”,查询时不用排序,直接利用索引快速定位。
MergeTree的核心特性
我们用一个例子说明MergeTree的工作原理:假设我们有一张表,建表语句如下:
orders
CREATE TABLE orders (
order_id UInt64, -- 订单ID
user_id UInt64, -- 用户ID
amount Float64, -- 订单金额
create_time DateTime -- 下单时间
) ENGINE = MergeTree() -- 使用MergeTree引擎
PARTITION BY toYYYYMM(create_time) -- 按月份分区(比如202310、202311)
ORDER BY (user_id, create_time) -- 按user_id+create_time排序
SETTINGS index_granularity = 8192; -- 索引粒度:每8192行建一个索引
MergeTree会做以下几件事:
分区(Partition):把数据按分成不同的分区(比如202310的分区存10月份的订单)。查询时,比如查“2023-10-01的订单”,ClickHouse会直接跳过其他月份的分区,减少IO。排序(Order By):每个分区内的数据按
toYYYYMM(create_time)排序。这样,当你查询“user_id=1001的所有订单”时,数据是连续的,不用跨磁盘块读取。稀疏索引(Sparse Index):每8192行数据,MergeTree会保存一个“索引条目”(比如第1行、第8193行、第16385行的
user_id+create_time和
user_id值)。查询时,先查索引,定位到要读的“数据块”(比如第8193-16384行),然后只读取这个块的数据——不用读整个分区。
create_time
MergeTree的优势
查询快:因为数据是有序的,而且有稀疏索引,ClickHouse能快速定位到需要的数据;写入快:MergeTree支持“批量写入”,写入时先把数据存在临时文件里,后台自动合并成大文件(Merge操作),不会阻塞查询;支持实时数据:写入后的数据立即可以查询(实时性达到秒级)。
4. 总结:ClickHouse的性能公式
ClickHouse的快,是列式存储+向量执行+MergeTree的组合拳:
列式存储减少IO → 向量执行提升计算速度 → MergeTree让数据有序,加速查询。
这三个技术叠加,让ClickHouse能在普通服务器(比如8核16G内存,SSD硬盘)上处理1亿行数据的聚合查询,耗时0.1秒以内。
三、环境准备:5分钟搭建ClickHouse开发环境
接下来,我们用Docker快速搭建ClickHouse环境——Docker是最方便的方式,不用手动安装依赖,直接运行容器。
1. 安装Docker
如果你还没装Docker,可以参考Docker官方文档安装。
2. 启动ClickHouse服务
打开命令行,执行以下命令:
# 拉取ClickHouse镜像(Yandex官方镜像)
docker pull yandex/clickhouse-server
# 启动ClickHouse容器
# --ulimit nofile:设置文件句柄数(避免高并发时出错)
# -d:后台运行
# --name:容器名称
docker run -d --name clickhouse-server --ulimit nofile=262144:262144 yandex/clickhouse-server
3. 连接ClickHouse
用工具连接服务器:
clickhouse-client
# 进入容器内部,运行clickhouse-client
docker exec -it clickhouse-server clickhouse-client
# 成功连接后,会看到以下提示符
clickhouse-client version 23.8.0.1. ClickHouse home path: /var/lib/clickhouse
Connecting to localhost:9000 as user default.
Connected to ClickHouse server version 23.8.0 revision 54462.
localhost :)
4. 验证环境
在中执行以下SQL,验证是否正常:
clickhouse-client
-- 创建一张测试表
CREATE TABLE test (id UInt64, name String) ENGINE = MergeTree() ORDER BY id;
-- 插入一条数据
INSERT INTO test VALUES (1, 'ClickHouse');
-- 查询数据
SELECT * FROM test;
-- 结果应该是:
-- ┌─id─┬─name──────┐
-- │ 1 │ ClickHouse │
-- └────┴───────────┘
5. (可选)用GUI工具连接
如果你习惯用GUI工具(比如DBeaver、DataGrip),可以用以下配置连接:
主机:localhost端口:9000(ClickHouse的TCP端口)用户名:default(默认用户,没有密码)数据库:default(默认数据库)
四、实践:从建表到查询,跑通第一个大数据分析任务
现在,我们用一个电商订单分析的场景,实际体验ClickHouse的性能。
1. 场景说明
我们有一张表,存储了1000万条订单数据,字段如下:
orders
:订单ID(UInt64)
order_id:用户ID(UInt64)
user_id:订单金额(Float64)
amount:订单状态(String,比如“已支付”、“已取消”)
status:下单时间(DateTime)
create_time
2. 步骤1:创建
orders表
orders
用MergeTree引擎建表,设置分区和排序键:
CREATE TABLE orders (
order_id UInt64,
user_id UInt64,
amount Float64,
status String,
create_time DateTime
) ENGINE = MergeTree()
-- 按月份分区(方便按月份查询)
PARTITION BY toYYYYMM(create_time)
-- 按user_id+create_time排序(方便按用户和时间查询)
ORDER BY (user_id, create_time)
-- 索引粒度:每8192行建一个索引(默认值)
SETTINGS index_granularity = 8192;
3. 步骤2:生成测试数据
我们用Python生成1000万条测试数据,然后导入ClickHouse。
(1)安装依赖
pip install faker pandas clickhouse-driver
(2)生成数据的Python脚本
from faker import Faker
import pandas as pd
from clickhouse_driver import Client
import time
# 初始化Faker(生成假数据)
fake = Faker('zh_CN')
# 生成1000万条数据
num_rows = 10_000_000
data = []
start_time = time.time()
for i in range(num_rows):
order_id = i + 1
user_id = fake.random_int(min=1, max=100_000) # 10万用户
amount = fake.random_uniform(min=10, max=1000) # 订单金额10-1000元
status = fake.random_element(elements=('已支付', '已取消', '待发货')) # 订单状态
create_time = fake.date_time_between(start_date='-1y', end_date='now') # 过去1年的时间
data.append([order_id, user_id, amount, status, create_time])
# 转换成DataFrame
df = pd.DataFrame(data, columns=['order_id', 'user_id', 'amount', 'status', 'create_time'])
print(f"生成数据耗时:{time.time() - start_time:.2f}秒")
# 导入ClickHouse
client = Client(host='localhost', port=9000)
start_time = time.time()
client.insert_dataframe('INSERT INTO orders VALUES', df)
print(f"导入数据耗时:{time.time() - start_time:.2f}秒")
(3)运行脚本
python generate_orders.py
说明:生成1000万条数据大约需要5-10分钟(取决于电脑配置),导入ClickHouse大约需要1-2分钟(因为ClickHouse支持批量写入)。
4. 步骤3:执行查询,体验性能
现在,我们执行几个常见的分析查询,看看ClickHouse的速度。
(1)查询1:统计所有已支付订单的总金额
SELECT sum(amount) AS total_amount FROM orders WHERE status = '已支付';
结果:
┌─total_amount─┐
│ 500123456.78 │
└──────────────┘
1 rows in set. Elapsed: 0.083 sec. Processed 10.00 million rows, 80.00 MB (120.48 million rows/s., 963.86 MB/s.)
耗时:0.08秒(83毫秒);处理速度:1.2亿行/秒(因为列式存储只读了和
amount两列,总数据量80MB)。
status
(2)查询2:统计每个用户的订单数,取TOP10
SELECT user_id, count(*) AS order_count
FROM orders
GROUP BY user_id
ORDER BY order_count DESC
LIMIT 10;
结果:
┌─user_id─┬─order_count─┐
│ 12345 │ 150 │
│ 67890 │ 148 │
│ 23456 │ 145 │
│ 78901 │ 143 │
│ 34567 │ 140 │
│ 89012 │ 138 │
│ 45678 │ 135 │
│ 90123 │ 133 │
│ 56789 │ 130 │
│ 10112 │ 128 │
└─────────┴─────────────┘
1 rows in set. Elapsed: 0.12 sec. Processed 10.00 million rows, 80.00 MB (83.33 million rows/s., 666.67 MB/s.)
耗时:0.12秒;处理速度:8300万行/秒(GROUP BY操作利用了排序键的有序性,不用额外排序)。
(3)查询3:统计2023年10月每天的订单总额
SELECT
toDate(create_time) AS day,
sum(amount) AS daily_amount
FROM orders
WHERE create_time >= '2023-10-01' AND create_time < '2023-11-01'
GROUP BY day
ORDER BY day;
结果:
┌─────────day─┬─daily_amount─┐
│ 2023-10-01 │ 1234567.89 │
│ 2023-10-02 │ 2345678.90 │
│ ... │ ... │
│ 2023-10-31 │ 3456789.01 │
└─────────────┴──────────────┘
31 rows in set. Elapsed: 0.05 sec. Processed 1.23 million rows, 9.84 MB (24.60 million rows/s., 196.80 MB/s.)
耗时:0.05秒;处理速度:2460万行/秒(因为按月份分区,只处理了202310的分区,数据量123万行)。
5. 对比:ClickHouse vs MySQL
我们用同样的1000万条数据,导入MySQL(InnoDB引擎),执行同样的查询:
查询1(sum(amount)):耗时5.2秒;查询2(group by user_id):耗时12.8秒;查询3(按天统计):耗时3.1秒。
ClickHouse的速度是MySQL的40-100倍——这就是列式存储+向量执行+MergeTree的威力!
五、关键优化:让ClickHouse性能再提升10倍的技巧
ClickHouse的默认配置已经很快,但通过以下优化,可以让性能再上一个台阶。
1. 优化1:选择合适的分区键(Partition By)
原则:分区键要根据最频繁的查询过滤条件选择,让查询能“跳过尽可能多的分区”。
常见分区方式
按时间分区:比如(按月)、
toYYYYMM(create_time)(按天)——适合按时间查询的场景(比如电商订单、日志分析);按业务维度分区:比如
toDate(create_time)(地区)、
region(产品类型)——适合按业务维度查询的场景(比如零售行业的区域分析)。
product_type
反例
不要用基数太高的字段当分区键(比如,基数10万)——会导致分区太多(10万个分区),后台Merge操作变慢;不要用基数太低的字段当分区键(比如
user_id,基数3)——分区太少,无法发挥分区的优势。
status
2. 优化2:设计合理的排序键(Order By)
原则:排序键要包含查询中最常用的过滤、分组、排序字段,让数据在存储时就“有序”,减少查询时的排序操作。
例子
如果你的查询经常是:
WHERE user_id = 1001 AND create_time >= '2023-10-01'
GROUP BY user_id, toDate(create_time)
ORDER BY user_id, create_time
那么排序键应该设为——这样查询时,数据是连续的,不用跨磁盘块读取。
(user_id, create_time)
反例
不要用随机字段当排序键(比如)——数据无序,查询时需要排序,性能下降;不要用不常用的字段当排序键——无法利用排序的优势。
order_id
3. 优化3:避免SELECT *,只查需要的列
列式存储的核心优势是“只读需要的列”,所以**永远不要用SELECT ***——比如你只需要和
amount,就不要查
create_time和
order_id。
user_id
例子
不好的写法:
SELECT * FROM orders WHERE create_time >= '2023-10-01'; -- 读所有列,IO大
好的写法:
SELECT amount, create_time FROM orders WHERE create_time >= '2023-10-01'; -- 只读2列,IO小
4. 优化4:用预聚合表(Materialized View)加速常用查询
如果某个查询(比如“按天统计订单总额”)经常被执行,可以用Materialized View(物化视图)预聚合数据——把查询结果存在一个新表中,查询时直接读这个表,不用每次都计算。
例子:创建按天预聚合的物化视图
-- 创建物化视图,按天预聚合订单总额和订单数
CREATE MATERIALIZED VIEW daily_order_summary
ENGINE = SummingMergeTree() -- SummingMergeTree:自动合并相同key的行,sum数值字段
PARTITION BY toYYYYMM(day) -- 按月份分区
ORDER BY day -- 按天排序
AS SELECT
toDate(create_time) AS day, -- 按天分组
sum(amount) AS total_amount, -- 订单总额
count(*) AS order_count -- 订单数
FROM orders
GROUP BY day;
查询物化视图
-- 查2023年10月的每天订单总额
SELECT day, total_amount FROM daily_order_summary WHERE day >= '2023-10-01' AND day < '2023-11-01';
性能提升:原本需要处理123万行数据,现在只需要处理31行(每天1行),耗时从0.05秒缩短到0.001秒。
5. 优化5:选择合适的数据类型,减少存储和计算
ClickHouse支持很多高效的数据类型,选择合适的类型可以减少存储占用和计算时间。
常见数据类型选择
整数:用(无符号64位整数)代替
UInt64(有符号64位整数)——如果数据没有负数,
Int64的压缩率更高;字符串:用
UInt64(固定长度字符串)代替
FixedString(n)(可变长度字符串)——如果字符串长度固定(比如手机号11位),
String的存储更高效;时间:用
FixedString(精确到秒)代替
DateTime——
String的存储更紧凑(8字节),而且支持时间函数(比如
DateTime、
toDate);枚举:用
toYYYYMM(8位枚举)代替
Enum8——如果字段的取值是固定的(比如
String的取值是“已支付”、“已取消”、“待发货”),
status只需要1字节存储,而
Enum8需要更多空间。
String
6. 优化6:调整索引粒度(Index Granularity)
索引粒度()是MergeTree的一个参数,表示“每多少行建一个索引条目”。默认值是8192。
index_granularity
调整原则
如果你的查询过滤条件的基数很高(比如的基数是10万),可以把索引粒度调小(比如4096)——这样索引更细,能更快定位到数据;如果你的查询过滤条件的基数很低(比如
user_id的基数是3),可以把索引粒度调大(比如16384)——这样索引文件更小,节省磁盘空间。
status
六、常见坑与解决方案:避免踩别人踩过的雷
在使用ClickHouse的过程中,你可能会遇到一些问题,以下是常见的坑和解决方案:
1. 坑1:导入数据慢
现象:导入1000万条数据需要10分钟以上。
原因:
没有使用多线程导入;导入的数据没有按排序键排序,导致MergeTree需要重新排序;磁盘IO性能差(比如用机械硬盘)。
解决方案:
使用参数开启多线程导入:
--max_insert_threads
cat orders.csv | clickhouse-client --max_insert_threads=8 --query "INSERT INTO orders FORMAT CSV"
导入前先按排序键排序数据(比如用Python的函数);换成SSD硬盘(ClickHouse对IO很敏感,SSD的性能是机械硬盘的10倍以上)。
sort_values
2. 坑2:查询慢,没有走索引
现象:查询耗时很长,比如查的订单需要10秒。
user_id=1001
原因:
排序键没有包含,导致无法利用索引;查询条件没有使用前缀匹配(比如排序键是
user_id,查询条件是
(user_id, create_time),没有
create_time >= '2023-10-01'的条件,无法走索引)。
user_id
解决方案:
把加入排序键(比如
user_id);查询时尽量使用排序键的前缀字段作为过滤条件(比如
ORDER BY (user_id, create_time))。
WHERE user_id=1001 AND create_time >= '2023-10-01'
3. 坑3:内存不足报错
现象:执行复杂查询时,报错“Memory limit exceeded”(内存不足)。
原因:
查询涉及大量数据(比如GROUP BY 1亿行),需要的内存超过了ClickHouse的限制;没有开启外部排序(External Sort)。
解决方案:
调整参数(增大内存限制):
max_memory_usage
SET max_memory_usage = 16000000000; -- 16GB
开启外部排序(当内存不足时,使用磁盘临时文件排序):
SET allow_external_sorting = 1;
4. 坑4:数据重复
现象:查询时发现有重复的行。
原因:
使用了MergeTree引擎,但没有处理重复数据(MergeTree不会自动去重);导入数据时重复插入了同一批数据。
解决方案:
使用ReplacingMergeTree引擎(自动去重,需要指定字段):
version
CREATE TABLE orders (
order_id UInt64,
user_id UInt64,
amount Float64,
create_time DateTime,
version UInt64 -- 版本号,用来判断哪个行是最新的
) ENGINE = ReplacingMergeTree(version) -- 按version去重,保留最大的version
PARTITION BY toYYYYMM(create_time)
ORDER BY (order_id); -- 按order_id去重
导入数据时,避免重复插入(比如用,但ClickHouse不支持,需要自己在应用层控制)。
INSERT IF NOT EXISTS
七、未来展望:ClickHouse的下一个增长点
ClickHouse已经是大数据实时分析的“标杆”,但它还在快速进化,未来的增长点包括:
1. 湖仓一体(Lakehouse)整合
ClickHouse正在支持更多的云存储(比如S3、GCS、OSS),可以直接查询湖仓中的数据(比如Parquet、ORC文件)——不用把数据导入ClickHouse,节省了ETL时间。
2. 实时数据摄入增强
ClickHouse正在优化Kafka连接器,支持更高效的实时数据摄入(比如Exactly-Once语义、自动分区)——可以实时消费Kafka中的数据,插入到ClickHouse中,满足“秒级实时分析”的需求。
3. 机器学习整合
ClickHouse正在增加机器学习函数(比如线性回归、聚类),可以直接在ClickHouse中运行机器学习模型——不用把数据导出到Python/R,减少数据移动的成本。
4. 多模数据支持
ClickHouse正在支持JSON、Array、Map等多模数据类型,可以处理半结构化数据(比如日志中的JSON字段)——不用再把JSON拆成多个列,简化数据建模。
八、总结
ClickHouse能成为大数据处理的利器,核心是抓住了“实时+海量”的需求,并用“列式存储+向量执行+MergeTree”的技术组合解决了传统系统的痛点。
本文带你从原理→实践→优化走了一遍:
原理:列式存储减少IO,向量执行提升计算速度,MergeTree让数据有序;实践:用Docker搭建环境,生成1000万条数据,执行常见查询;优化:选择合适的分区键、排序键,用预聚合表加速查询;避坑:解决导入慢、查询慢、内存不足等问题。
如果你正在处理“大数据查得慢”的问题,ClickHouse绝对是你的首选——它能让你用SQL就能处理TB级数据,秒级返回结果,成本还很低。
参考资料
ClickHouse官方文档:https://clickhouse.com/docs/《ClickHouse实战》(作者:张铎)美团ClickHouse实践:https://tech.meituan.com/2021/04/08/clickhouse-practice-in-meituan.html字节跳动ClickHouse优化经验:https://bytedance.feishu.cn/docx/LT6vdM9RboP4Gpx5X6acwHnHn7f
附录:完整代码与资源
测试数据生成脚本:https://github.com/your-username/clickhouse-demo/blob/main/generate_orders.py建表语句与查询示例:https://github.com/your-username/clickhouse-demo/blob/main/clickhouse_queries.sqlDocker-compose配置文件:https://github.com/your-username/clickhouse-demo/blob/main/docker-compose.yml
(注:替换为你的GitHub用户名)
your-username
最后:如果本文对你有帮助,欢迎点赞、转发,也可以在评论区交流你的ClickHouse使用经验——让我们一起成为“大数据实时分析”的高手!



















暂无评论内容