
OLTP与OLAP
什么是 OLTP
- 全称: Online Transaction Processing(联机事务处理系统)
- 特点:
- 专注于事务处理
- 进行数据的增删改查操作
- 典型代表: MySQL、Oracle
- 用于网站和系统应用的后端数据库
- 存储业务数据(如下单、支付、注册信息)
- 支持事务操作,响应时间要求高
- 数据量相对较少
OLTP 的问题
- 当数据量达到 TB 或 PB 级别时,传统的 MySQL 性能不足
- 数据分析场景需要全盘扫描统计,不适合 OLTP
什么是 OLAP
- 全称: Online Analytical Processing(联机分析处理)
- 特点:
- 专注于复杂分析操作
- 更侧重于决策支持
- 典型代表: ClickHouse、Doris、StarRocks
- 数据量通常为 TB 级别
- 不擅长修改操作,数据一致性要求低
ClickHouse 数据库介绍
- 简介:
- 是俄罗斯的Yandex于2016年开源的列式存储数据库
- 使用c++语言编写
- 类型: OLAP 数据库
- 特点:
- 实时数据仓库,在线分析处理查询
- 支持标准 SQL 语句(如 INSERT、SELECT),实时生成分析数据报告
- 提供高级查询功能(复合聚合函数、窗口函数、跨表查询)
- 支持列式存储、数据分区和线程并行
列式存储 vs 行式存储
- 行式存储:
- 将整行数据存储为一个整体
- 列式存储:
- 将每一列作为一个数据块存储
- 优势: 更高效的统计查询、数据压缩效果好
数据分区和线程并行
- 数据分区:
- 按业务逻辑将数据分类,便于查询管理
- 示例: 按日期分区
- 线程并行:
- 多个 CPU 核心并行查询不同分区
- 优势: 降低查询延迟,利用 CPU 资源
支持丰富的表引擎
- 表引擎:
- 决定数据存储方式和位置
- 支持查询类型和并发访问
- 常用表引擎:
- MergeTree(支持分区和 TTL)
- 日志引擎(快速写入小数据)
- 集成引擎(实时接入其他数据源)
ClickHouse 的缺点
- 由于单条查询使用多核cpu,不支持高并发请求
- 对 UPDATE 和 DELETE 操作支持较差
- 单个插入性能较低,建议批量插入
- 吃硬件,一般物理机安装,单独安装不和业务混部
ClickHouse 的适用场景
- 适用于数据量大且需要分析的场景
- 适合存储已经处理过的大宽表,进行分析,读取大量列中的少量列
- 不适合高并发请求、频繁更新和删除的场景
ClickHouse安装
安装准备
- 防火墙设置:关闭防火墙以避免影响安装
- 实际工作中需根据需要配置IP和端口限制
- Linux系统文件限制,取消文件限制:否则影响ch性能
- 使用ulimit -a命令查看系统限制
- 关注的主要参数:
- 打开的文件数
- 用户最大进程数
- 修改配置文件/etc/security/limits.conf
没修改前:
修改:
修改后: - 解释配置项:
- 用户与用户组的设置
- 软限制(soft)与硬限制(hard)的区别
- 打开文件数(number of open files)与进程数(number of processes)
- 安装依赖
linux安装由运维协助,这里不展开记录,下面直接通过docker安装(生产环境不要使用docker,这里只为了学习原理和使用,并非安装所以简化安装)
参考
https://clickhouse.com/docs/install#from-docker-image
安装
linux安装由运维协助,这里不展开记录,下面直接通过docker安装(生产环境不要使用docker,这里只为了学习原理和使用,并非安装所以简化安装)
1 | docker pull yandex/clickhouse-server:21.7.3.14 |
核心目录:
- 数据在 data目录
- 表结构信息在 metadata目录
Clickhouse数据类型
整形数值
- Int8, Int16, Int32, Int64:
- Int8: 8位(-128 到 127)
- Int16: 16位(-32768 到 32767)
- Int32: 32位(-2147483648 到 2147483647)
- Int64: 64位(-9223372036854775808 到 9223372036854775807)
- 与Java类型类比
- int8对应byte
- int16对应short
- int32对应int
- int64对应long
- 无符号整型:
- UInt8: 0 到 255
- UInt16: 0 到 65535
- UInt32: 0 到 4294967295
- UInt64: 0 到 18446744073709551615
浮点型
浮点型的分类
- Float32: 32位(4字节)
- Float64: 64位(8字节)
精度问题
- 浮点型存储可能会导致精度丢失,尤其在处理货币时应避免使用。
布尔类型
- 使用uint8表示布尔值
- 0表示false
- 1表示true
Decimal类型
- Decimal(p, s):
- p: 总位数
- s: 小数位数
- 类型示例
- decimal(32, 5) 整数小数一共9位, 小数后5位
- decimal(64, 5), 整数小数一共18位, 小数后5位
- decimal(128, 5) 整数小数一共38位, 小数后5位
- Decimal(p, s):
字符串类型
- String: 可变长度字符串。
- FixedString(n): 固定长度字符串,不足部分用空字节填充。
枚举类型
- 定义和使用枚举, Enum8,Enum16
- 插入和查询枚举值的示例
1
2
3
4
5
6
7
8
9
10CREATE TABLE t_enum (
x Enum8('hello' = 1, 'world' = 2)
) ENGINE = TinyLog;
INSERT INTO t_enum VALUES ('hello'),('world'),('hello');
SELECT * FROM t_enum;
# 转换查询
SELECT CAST(x, 'Int8') FROM t_enum; - 插入未知类型会报错
时间类型
- Date: 仅包含年月日。
- DateTime: 包含年月日时分秒。
- DateTime64: 包含亚秒(毫秒)。
数组类型
- mergeTree表中不能存储多维数组
- 使用 Array 表示,支持单层数组。
- 例子:Array(UInt8) 表示存储无符号8位整型的数组。
1
2
3
4
5
6
7
8
9
10
11
12SELECT array(1,2) AS x, toTypeName(x);
2d9991e86323 :) SELECT array(1,2) AS x, toTypeName(x);
SELECT
[1, 2] AS x,
toTypeName(x)
┌─x─────┬─toTypeName([1, 2])─┐
│ [1,2] │ Array(UInt8) │
└───────┴────────────────────┘
1 rows in set. Elapsed: 0.013 sec.
其他数据类型
介绍其他常用数据类型
可查阅ClickHouse官网获取更多信息
https://clickhouse.com/docs/zh/sql-reference/data-types空值处理
ClickHouse中的空值存储方式
建议使用无意义的数字或字符表示空值, 避免使用NULL
比如可以用-1,无意义得值,字符串可以搞空串或者null等
Clickhouse表引擎
概述
- 表引擎的定义:类似于MySQL中的InnoDB和MyISAM,不同引擎具有不同的功能和作用。
- 表引擎的作用:
- 决定数据的存储方式和位置。
- 指定如何读写数据。
ClickHouse表引擎的特点
- 存储方式和位置
- 数据一般存储在本地磁盘上。
- ClickHouse不依赖于Hadoop或HDFS计算资源。
- 默认配置路径:/var/lib/clickhouse/data。
- 支持的查询及语法
- 不同引擎支持不同的查询语法。
- 例如,某些引擎不支持多维数组存储。
- 并发数据访问
- ClickHouse支持多线程并发查询,但并非所有引擎都支持。
- 索引支持
- 不同引擎对索引的支持程度不同,索引的目的是提高查询效率。
- 数据复制
- 某些引擎支持数据复制,而其他引擎则不支持。
- 引擎名称是大小写敏感的,采用大驼峰命名法。
表引擎TinyLog
- 属于日志家族,适合小数据量(小于100万行)。
- 特点:
- 存储在磁盘上,不支持索引。
- 无并发控制,适合简单测试。
- 做一些简单测试,生产环境肯定不会用
1
CREATE table t_tinylog(id String,name String) engine=TinyLog;
表引擎Memory
- 基于内存,性能(超过10G/s)快但数据易丢失。
- 特点:
- 不支持索引,适合小数据量测试。
- 做一些简单测试,生产环境肯定不会用
MergeTree系列
- ClickHouse的核心引擎,适合生产环境。相当于innodb之于mysql
- 包含多个变种,如ReplacingMergeTree和SummingMergeTree,具有不同功能。
参考官方文档:
https://clickhouse.com/docs/zh/engines/table-engines/mergetree-family
集成引擎
集成外部系统,如MySQL和Kafka。
外部集成的意义:无需将数据导入ClickHouse,直接查询外部数据源。
https://clickhouse.com/docs/zh/engines/table-engines/integrations
MergeTree引擎
mergeTree家族的第一个引擎——MergeTree。
MergeTree引擎本身是一个表引擎。
介绍建表语句和嵌表语句。
参考:
https://clickhouse.com/docs/zh/engines/table-engines/mergetree-family/mergetree
建表语句
1 | CREATE TABLE t_order_mt( |
primary key是可以重复的,这和mysql不一样
order by 是必须的, primary key 不是必须的
测试数据(多执行几次,多插入一些):
1 | INSERT INTO t_order_mt (id, sku_id, total_amount, create_time) VALUES (1, 'SKU001', 100.50, '2023-01-01 10:00:00'), (1, 'SKU002', 200.75, '2023-01-01 15:30:00'), (3, 'SKU003', 50.20, '2023-03-20 09:15:00'), (4, 'SKU004', 150.00, '2023-05-10 14:45:00'), (5, 'SKU005', 300.99, '2023-05-10 11:20:00'), (6, 'SKU006', 75.55, '2023-06-30 16:50:00'); |
查询结果,分区了O(∩_∩)O
连接配置
连接ClickHouse的步骤:
使用主机名和端口(默认端口为8123)。
默认用户名为default,密码为空。
如果驱动未自动下载,需要手动添加驱动文件。
partition by 分区(可选)
不是必须的建表语句
分区的作用
主要目的是避免全表扫描。
查询语句中加入分区字段的取值,可以优化查询速度。
分区的实现方式类似于物理分隔,像是将数据分放在不同的房间。ClickHouse的分区机制
ClickHouse的分区是基于本地磁盘,而hive的分区是在HDFS上建立分区目录。
如果不写分区,默认会用一个分区,名为“all”,所有数据都在里面。并行查询
单个查询可以多线程同时执行。
每个线程处理一个分区的数据。
通常按天分区,因为这样可以避免不必要的麻烦。分区的存储
数据存储稀疏索引
索引文件采用的稀疏索引,和kafka的partition一样分区目录原理
- 分区目录如上图所示, 第一位分区键,第二位最小区块编号,第三位,最大区块编号,第四位合并层级
- 日期类型分区键,最好存日期类型,不像其他数仓,hive存字符串,这样效率高
- 其他类型分区目录,如string,float类型分区建,目录名字取hash
数据写入与分区合并
- 每次数据写入会产生一个临时分区,之后需要执行合并操作。
- 合并是定期执行的,可以手动触发。
- 手动触发指令
1
OPTIMIZE TABLE t_order_mt FINAL
- 可以加一个分区参数 合并之后,分区目录目录会变,产生合并后的目录,过期数据会自动清理,删除。
1
OPTIMIZE TABLE t_order_mt PARTITION '20230101' FINAL
primary key主键(可选)
- 提供了数据的一级索引,但不是唯一约束
主键的设定主要依据查询中的where条件。(一般加载where条件中)
index granularity
直接翻译就是索引粒度,指在稀疏索引中两个相邻索引对应的间隔,Clickhouse中mergeTree默认是 8192,官方不建议修改这个值,除非该列存在大量重复值,比如在一个分区中几万行才有一个不同数据。稀疏索引
索引文件采用的稀疏索引,和kafka的partition一样
要求主键必须有序所以必须要有order bt
优点:数据量小,查询效率高
order by(必选)
- 分区内排序
- 必填 甚至比主键还重要, 因为要建立稀疏索引,且后面去重和汇总也要用到order by
- 可以多个字段,但必须是左前坠, 比如(id,sku_id) 要么是id ,要么是id,sku_id,不能是sku_id,id 和sku_id
二级索引
版本问题
20.1.2.4之前,官方标注是实验性的,之后是默认开启的(20年1月份版本)
之前版本可以用以下命令设置,之后版本会报错1
set allow_expreimental_data_skipping_indices=1
二级索引使用在大量重复数据的场景下
二级索引不必有序
二级索引也叫跳数索引如何使用
1
2
3
4
5
6
7
8
9
10CREATE TABLE t_order_mt2(
id UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time DateTime,
INDEX a total_amount TYPE minmax GRANULARITY 5
) engine=MergeTree
partition by toYYYYMM(create_time)
primary key (id)
order by (id,sku_id);二级索引的类型是 minmax
minmax 是最小最大值索引,粒度是5, 也就是5个数据算一个区间, 然后取最小值和最大值。
1 | INSERT INTO t_order_mt2 (id, sku_id, total_amount, create_time) VALUES (1, 'SKU001', 100.50, '2023-01-01 10:00:00'), (1, 'SKU002', 200.75, '2023-01-01 15:30:00'), (3, 'SKU003', 50.20, '2023-03-20 09:15:00'), (4, 'SKU004', 150.00, '2023-05-10 14:45:00'), (5, 'SKU005', 300.99, '2023-05-10 11:20:00'), (6, 'SKU006', 75.55, '2023-06-30 16:50:00'); |
1 | clickhouse-client --send_logs_level=trace <<< 'select * from t_order_mt2 where total_amount > toDecimal32(900,2)' |
使用了二级索引
数据TTL
time to alive 数据存活时间
- 对某一列设置ttl, 数据过期之后,会自动删除
1
2
3
4
5
6
7
8
9
10CREATE TABLE t_order_mt3(
id UInt32,
sku_id String,
total_amount Decimal(16,2) TTL create_time + INTERVAL 10 SECOND,
create_time DateTime,
INDEX a total_amount TYPE minmax GRANULARITY 5
) engine=MergeTree
partition by toYYYYMM(create_time)
primary key (id)
order by (id,sku_id);
时间到期,这一列就会被删除(相当于时间到了,会启动一个合并任务,处理删除)
- 表级TTL会根据每行的create_time 字段进行过期删除
1
ALTER table t_order_mt3 MODIFY TTL create_time + INTERVAL 10 SECOND;
TTL不能使用到主键字段
参考:
https://clickhouse.com/docs/zh/engines/table-engines/mergetree-family/mergetree
ReplacingMergeTree 引擎
替换合并树
他的存储特性完全继承MergeTree, 多了一个去重功能
不是根据主键去重,而是根据order by的字段去重
去重时机
不是实时去重,只会在同一批插入(新版本)或合并的过程去重。
最终一致性,不实时一致去重范围
分区内去重,不能跨分区去重1
2
3
4
5
6
7
8
9CREATE TABLE t_order_rmt(
id UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time DateTime
) engine=ReplacingMergeTree(create_time)
partition by toYYYYMM(create_time)
parimary key (id)
order by (id,sku_id);
ReplacingMergeTree(create_time) 中create_time 表示按照order by的去重之后,保留crate_time最大的数据。
不指定,默认按照插入顺序来,保留最后插入的。
SummingMergeTree 引擎
预聚合求和合并树
分区内聚合
按照order by
1 | CREATE TABLE t_order_smt( |
以上求和total_amount,是按照gruop by id,sku_id来聚合求和的, 重要的是order_by,如果不指定total_amaount,那么会按照order by求和所有的数字列。
1 | insert into t_order_smt values (1,'sku001',100.50,'2023-01-01 10:00:00'),(1,'sku001',200.75,'2023-01-01 15:30:00'),(3,'sku003',50.20,'2023-03-20 09:15:00'),(4,'sku004',150.00,'2023-05-10 14:45:00'),(5,'sku005',300.99,'2023-05-10 11:20:00'),(6,'sku006',75.55,'2023-06-30 16:50:00'); |
结果,两条被聚合了:
且crate_time 是最前面的一条(第一行),而不是最后一条(最大)
只有在同一批次插入时(新版本),或分片合并时才会聚合
设计聚合表时,最好设置聚合的列,否则序号之类的都会被聚合,没有意义
查询的时候,还是需要在sql里面写sum(),因为可能还没来得及聚合,体会下预聚合
Clickhouse SQL操作
insert
和mysql一样
从表到表,支持insert into xxx select
update和delete
OLAP的,不叫更新,叫做可变查询, 是ALTER的一种
不支持事务
- 物理删除
1
alter table t_order_mt delete where id = 1;
- 逻辑删除
1
alter table t_order_mt update status = 1 where id = 1;
- 恢复
1
alter table t_order_mt update status = 0 where id = 1;
这种删除操作比较重,分了两步,新增分区并发旧分区打上逻辑失效标记,直到合并的时候才会释放磁盘空间,一般这种功能不给用户,由管理员完成
实践中, 可以加_sign标记,_version版本号机制以新增的方式替代删除,缺点就是数据膨胀
查询操作
支持子查询
支持CTE Common Table Expression 公用表表达式with子句
语法支持join,但是实际上避免join,json操作无法使用缓存,所以即使是join ch也会查询两次,且占用内存,且分布式的话内存爆炸
窗口函数,目前官网表示正在试验阶段
各种函数查看官网
多维分析函数
不支持自定义函数
GROUP BY 增加了with rollup上卷 , with cube多维分析, with totals总计 来计算小计和总计
比如按照a,b维度分析,
上卷:
分析 group by a,b
分析 group by a
分析 group by ()
多维分析:
分析 group by a,b
分析 group by a
分析 group by b
分析 group by ()
总计分析:
分析 group by a,b
分析 group by ()
1 | alter table t_order_mt delete where 1=1; |
alter操作
和mysql基本一致
新增字段
1 | alter table tableName add column newcolname String after col1; |
修改字段类型
1 | alter table tableName modify column colname String; |
删除字段
1 | alter table tableName drop column colname; |
导出数据
1 | clickhouse-client --query "select * from t_order_mt where create_time='2024-01-01 12:00:00'" --output_format_csv_separator="\t" --output_format_csv_with_names --output_format_csv_with_names_and_types > /tmp/t_order_mt.csv |
更多格式
参考官网
副本
互为副本, 平权
注意点:副本的表引擎都要加Replicated
例如:
- ReplicatedMergeTree
- ReplicatedReplacingMergeTree
- ReplicatedSummingMergeTree
其他可参考官方文档
分片集群
clickhouse支持数据分片到不同机器上存储
- 集群写入流程(3分片2副本共6个节点)
s1 s2 s3 三个分片,
r1 r2 每个分片两个副本
参数 internal_replication 内部副本参数
false得话,非内部同步,如图黄色
true得话,内部同步(生产一般要打开true),如图绿色
- 集群读取流程
- 优先选择errors_count小得副本
- errors_count相同,有随机,顺序,随机(优先第一顺位)host名称近似等四种选择方式
- 集群建表
创建本地表:参数含义:1
2
3
4
5
6
7
8
9CREATE TABLE st_order_mt on cluster gmall_cluster (
id UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time DateTime
)engine=RepilicatedMergeTree('/clickhouse/tables/{shard}/st_order_1109', '{replica}')
patition by toYYYYMMDD(create_time)
primary key(id)
order by(id,sku_id);
集群名, 集群配置里面得宏定义
本地表得意思是集群每个节点都创建一个表
创建分布式表:
1 | CREATE TABLE st_order_mt_all on cluster gmall_cluster ( |
参数含义:
集群名称,库名,本地表名,分片键
统领本地表,有点视图得意思
- 插入数据
插得是分布式表,实际上存在每一张表1
INSERT INTO st_order_mt_all values()
Clickhouse执行计划
官方推荐独立部署,128G硬盘 100G内存,32线程内核
参考官文档:
https://clickhouse.com/docs/zh/sql-reference/statements/explain
语法
1 | EXPLAIN [AST | SYNTAX | QUERY TREE | PLAN | PIPELINE | ESTIMATE | TABLE OVERRIDE] [setting = value, ...] |
EXPLAIN 类型:
- AST — 抽象语法树。
- SYNTAX — 经 AST 级别优化后的查询文本。
- QUERY TREE — 经查询树级别优化后的查询树。
- PLAN — 查询执行计划。 (默认)
- PIPELINE — 查询执行流水线。
以上实例说明该查询走了预处理文件,也就是直接读的文件资源,速度比较快。
以上示例,对查询做了优化
以上对三元嵌套查询做了优化
以上对函数查询做了优化
提升查询性能总结
选择合适的表引擎, mergeTree是ch常见的引擎,但不意味着mergeTree适合所有的场景。
建表时不要使用Nullable,因为官方已经指出了,且这种类型几乎总是拖累性能,所以建议使用一个特殊字符来表空,比如-1, 字符串null等等
合适的划分分区和索引,分区是必须的,官方建议按天分区,建议自己分区的话单分区不超过100w行数据。
数据变更不要太频繁,避免产生过多的临时分区,加大合并的负担,如果要修改建议少次数,大批量的更新修改。官方建议1秒左右发起一起写入操作,单词操作写入数据量保持2w-5w ,具体根据服务器性能定
使用prewhere代替where,因为where是获取整行然后再过滤,而prewhere直接过滤列
指定列和分区,避免select *, 减少内存消耗
避免构建虚拟列,减少内存消耗
比如:select income,age income/age as incrate from xxx
正例:避免虚拟列,直接写select income,age from xxx用in代替join
join会加载到内存,再计算消耗内存如果非要join,尽量把大表写在前面,小表写后面,这跟mysql 建议小表驱动大表相反。
生产常见问题
- 数据一致性问题
ch强调最终一致性,事务控制天生比较弱,主要源自于合并的机制,新分区合并到旧分区
分布式表尽量避免使用,要用就用副本表, 分布式表网络重试问题丢消息,副本同步机制,再次启动还会同步,更稳定可靠
数据合并时间不确定,不一致问题要注意, 可以定期夜间optimize final, 如果非要一致性,就用_sign _version 来标记自己用乐观锁机制保持一致性(sign标记是否删除,version标记新增)
多副本表,尽量固定写入节点。因为ch 副本是同权的, 如果随机写,一个挂了依然写成功了,但是数据不对齐了,固定写入一个节点,这样挂了立马可以知道
zookeeper数据丢失导致副本无法启动。
- 本文标题:Clickhouse总结
- 本文作者:形而上
- 创建时间:2024-01-10 21:01:00
- 本文链接:https://deepter.gitee.io/2024_01_10_clickhouse/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!