应用场景
- 绝大多数读请求
- 数据需要以大批次(大于1000行)进行更新,而不是单行更新,或者没有更新操作
- 数据只添加到数据库中,没有必要修改
- 读数据时,会从数据库中提取出大量的行,但只用到少量的列
- 表很“宽”,即表中包含大量的列
- 查询频率相对较低(每台服务器的QPS小于100)
- 简单查询,允许大约50ms的延迟
- 列的值是比较小的数值和短字符串
- 处理单个查询需要高吞吐量(每台服务器每秒高达数十亿行)
- 不需要事务
- 数据一致性要求低
- 每次查询中只会查询一个大表,除了一个大表,其余都是小表
- 查询结果显著小于数据源。即数据有过滤或聚合,返回结果不超过单个服务器内存大小
缺点
- 不支持真正的删除/更新,不支持事务
- 不支持二级索引
- 有限的SQL支持
- 不支持窗口功能
- 元数据维护需要人工干预维护
列式存储
- 列式存储可以满足快速读取特定列的需求,在线分析处理往往需要在上百列的宽表中读取指定列分析;
- 列式存储就近存储同一列的数据,使用压缩算法可以得到更高的压缩率,减少存储占用的磁盘空间;
单条查询就能利用整机的CPU处理(不适合高qps的查询业务)
不适合关联查询
引擎:
- TinyLog:列文件,保存在磁盘,不支持索引,没有并发控制
- Memory:保存在内存中,不支持索引,读写不会阻塞
- MergeTree:
- 主键不唯一,可重复,没有唯一约束
- 分区时按文件来区分
- 分区内排序
- 建表时 order by 必填【必填原因:稀疏索引,必须要有序,有序效率高】(主键必须是order by的前缀)
- 以列文件+索引文件+表定义文件
- 分区并行
- TTL:设置表或者列的生命周期
- ReplacingMergeTree:
- 去重功能
- (去重只会在同批插入或合并的过程中才生效)最终一致性
- 分区内去重
- 合并时间不确定
- optimize 会手动发起合并,但是不应该太频繁,会引发对数据的大量读写
- 根据order_by字段去重
- 重复数据保留版本字段最大的,插入先后顺序
文件目录
- columns.txt :列信息
- primary.idx:稀疏索引(数据量小,二分查找,跳表)【index granularity,索引粒度】
- data.bin :数据文件
- data.mrk:标记文件,标记偏移量
关键参数
- background_pool_size:后台线程池大小,merge线程就是在该线程池中执行(cpu2倍)
- background_schedule_pool_size:执行后台任务的线程数(cpu2倍)
- background_distributed_schedule_pool_size:分布式发送执行后台任务的线程数(cpu2倍)
- max_concurrent_queries:最大并发处理请求数
- max_threads:单个查询所能使用的最大cpu个数(cpu核数)
- max_memory_usage:单次查询占用内存最大值
- max_bytes_before_external_group_by:当group使用内存超过阈值后会刷新到磁盘进行
- max_bytes_before_external_sort:当order by使用内存超过阈值后会刷新到磁盘进行
clickhouse不支持多数据目录,为了提升数据io性能,可以挂载虚拟券组
查询优化
- 使用count时,尽量可以不指定具体字段,直接去count.xml中获取条数
- 去除查询中的重复字段
- 使用prewhere代替where(prewhere会读取指定的列数据,来判断数据过滤,等待数据过滤之后再读取select声明的列字段来补全其余属性)
- 使用主键字段
- select 查询的列字段和where字段相同
- order by要搭配where和limit使用
- 避免产生虚拟列(select 表中没有的字段a+b)
- 去重根据业务可以选择 uniqCombined函数,性能提升10倍以上,存在误差,每次查询结果不同
- 查询熔断(设置单个查询的超时时间问题)
- 关闭虚拟内存(物理内存和虚拟内存交换数据,导致查询变慢)
- 表中不配置null值(单独存文件,不能够被索引,效率低)
- 批量写入时先排序(无需的数据或者涉及的分区太多,导致clickhouse无法及时对新导入的数据进行合并,从而影响查询性能
- join时小表在右(会把join的表加载到内存)