基础知识+原理
极致压缩率 极速查询性能
列式数据库管理 ,读请求多 大批次更新或无更新 读很多但用很少 大量的列 列的值小数值/短字符串 一致性要求低
DBMS:动态创建/修改/删除库 表 视图,动态查/增/修/删,用户粒度设库/表权限,分布式,备份恢复
向量化执行引擎:优化循环,SIMD CPU寄存器实现数据并行;
数据分片 分布式查询:1分片对应1服务节点
本地表=数据分片,分布式表不存数据,本地表访问代理
列式存储:减少查询所需要扫描的数据量
数据压缩LZ4:步长匹配扫描数据,编码转换重复部分 ;降低IO和存储的压力
多线程:
大小写敏感
多样化表引擎:合并树 内存 文件 接口
多主架构:对等角色 all节点功能相同 规避单点故障
总结:高效列式存储引擎,实现了数据有序存储、主键索引、稀疏索引、数据Sharding、数据Partitioning、TTL、主备复制等丰富功能
存储层
列式存储:只需要读取参与计算的列,减少io 加速查询
数据有序存储:按某列sort by,相同sort key数据联系存储 有序摆放,少量block 利用page cache
主键索引:每列按index granularity(8192)划分,第一行mark行 二分查找 index granularity 加速查
数据增改删:alter变更,异步执行时立即返回,实际排队等待执行
mutation:找到分区 重建 新换旧,不可撤 顺序执行 ,推荐一次性更新大量数据
模块
- column列对象 field是基础映射单元,不同类型不同实现 columnString columnArray,单个值field聚合13中数据类型和相应处理逻辑 uint64 null string array
- dataType序列化反序列化,具体实现=》对应数据类型的实例,数据读取从column field获取
- block 表的子集,数据 类型 列名组成,ColumnWithTypeAndName聚合column读取数据,dataType序列化
- block流,IBlockInputStream(60种)读取 关系运算,IBlockOutputStream输出到下一环节 表引擎相关
- table IStorage接口,子类实现 DDL read write,查询交给Interpreter解释器处理
实践
Java Language Client Options for ClickHouse | ClickHouse Docs
实践这个就先不写了,直接上一个完整的博客例子
springboot整合mybatis-plus、clickhouse、mysql多数据源_springboot 配置mysql 和clickhouse双数据源-CSDN博客
SpringBoot2 集成 ClickHouse 实现高性能数据分析_springboot如何集成clickhouse-CSDN博客
问题
活动及用户+匹配模块 收集 埋点数据 异步kafka 活动等业务消费 MybatisPlus 写入clickhouse
消费等TPS大几千, too many parts
clickhouse最小操作单元block,写入借助zk的唯一自增的blockId,按照PartitionId_blockId_blockId_0生成data parts,后台merge线程 单次耗时5min,分钟内 多个小文件合并成PartitionId_MinBlockNum_MaxBlockBum_Level,32个merge线程池,频繁插入 merge压力大 处理不了,默认clickhouse一次合并超300个文件就报错了
解决:少批多条 调小写入的并发数,调大批处理数据size间隔 减少文件个数,1s写入一次
每次单条写入
MybatisPlus的savebatch 最大sql 4M,最大单次1000条,再多的话便单条写入
改为其他方式写入,原生jdbc/flink,自定义sink 一定批次/执行checkpoint写入一次
写入性能很差
clickhouse底层使用httpclient,单条频繁写入效率很低
解决
定时任务,有数据 放到队列1 当数据过大|10-15s后 队列1放入队列2 队列2批量写入clickhouse 清空队列1
优化
25亿数据 耗时比较长
大部分按照时间来查询,执行计划分析sql
//打印执行日志:执行日志级别trace
clickhouse-client -u xxxx --password xxxxxx --send_logs_level=trace <<< 'your query sql' > /dev/null
没有用到主键索引
空值使用无意义的字符串填充,clickhouse底层将空值单独文件存储,影响性能
明确select查询字段
多线程查询
7天数据放入一个分区,分区查询
直接使用count()查询数据量
使用高级函数,multilf argMax
聚合外推