ClickHouse查询执行与优化

SQL语法扩展与执行计划分析

特殊函数与子句

  • WITH子句 :定义临时表达式(CTE),复用中间结果。
    • WITH tmp AS (SELECT ...) SELECT * FROM tmp
  • ANY修饰符 :在JOIN时仅保留第一个匹配的行(避免笛卡尔积爆炸)。
    • SELECT ... FROM table1 ANY LEFT JOIN table2 USING (key)
  • GLOBAL IN/GLOBAL JOIN :在分布式查询中,将右表广播到所有节点执行。
    • SELECT ... WHERE id GLOBAL IN (SELECT id FROM remote_table)
  • LIMIT n BY expr :按expr分组后,每组取前n条数据(类似窗口函数ROW_NUMBER)。
    • SELECT * FROM table LIMIT 3 BY date ORDER BY time DESC
  • SAMPLE子句 :快速采样数据(基于哈希或分块)。
    • SELECT * FROM table LIMIT 3 BY date ORDER BY time DESC
  • ARRAY JOIN :展开数组或嵌套结构为多行。
    • SELECT * FROM table ARRAY JOIN tags

聚合函数扩展

  • uniqCombined :近似计算UV(误差率<1%),内存占用低。
    • SELECT uniqCombined(user_id) FROM logs
  • quantileTDigest :近似计算分位数(如P99延迟)。
    • SELECT quantileTDigest(0.99)(latency) FROM requests
  • sumMap/minMap :对Map类型(Key-Value)的聚合计算。
    • SELECT sumMap(keys, values) FROM metrics
  • anyLast :返回最后一次出现的非NULL值(适合状态跟踪)。
    • SELECT anyLast(status) FROM events GROUP BY user_id

执行计划优化策略

  • 主键索引 :确保WHERE条件命中ORDER BY键的前缀。

  • 跳数索引 :对高基数列使用SETMINMAX索引加速过滤。

  • 避免全表扫描 :使用分区裁剪(PARTITION BY)和索引过滤;避免在WHERE中对非索引列进行复杂计算。

  • 聚合优化

    • 预聚合 :使用SummingMergeTreeAggregatingMergeTree减少实时计算。
    • Combiner优化:在分布式查询中,优先在本地节点预聚合。
  • JOIN优化

    • ANY JOIN:右表存在重复键,只需保留第一个匹配值。
    • GLOBAL JOIN:右表数据量小,广播到所有节点避免Shuffle。
    • DISTRIBUTED JOIN:右表数据量大,按Join键分布数据。
  • 资源控制 :设置内存限制max_memory_usage;调整线程数max_threadsbackground_pool_size

索引原理

索引类型与适用场景

  • 主键索引 :基于排序键(ORDER BY)的粗粒度索引,定位数据块(Granule)范围,如时间范围过滤。
  • 跳数索引:基于列值统计的细粒度索引,跳过不满足条件的数据块,如高基数列的等值或范围过滤。

主键索引(Primary Key)

  • 数据排序 :MergeTree表的数据按ORDER BY指定的列严格排序后存储在磁盘,形成​​有序数据块(Granule)​ ​默认包含8192行数据。

  • 索引结构 :主键索引记录每个Granule中​​第一个行的排序键值​ ​,并持久化为primary.idx文件。索引条目与Granule一一对应。

  • 工作流程

    • 查询条件解析 :优化器提取WHERE条件中涉及主键的过滤表达式(如date >= '2023-01-01')。
    • 索引匹配:通过二分查找定位到满足条件的Granule范围。
    • 数据读取 :仅加载相关Granule的数据文件(.bin),跳过无关数据块。
  • 优化策略

    • 前缀匹配原则 :主键索引仅对ORDER BY键的​前缀列​有效。若查询条件不包含前缀列,索引将失效。
    • 合理选择排序键 :将高频过滤条件(如时间列)放在ORDER BY最左侧。

跳数索引(Data Skipping Indexes)

  • 数据块级统计 :跳数索引为每个Granule生成统计信息(如最大/小值),记录在skp_idx_[index_name].idx.mrk文件中。
  • 跳过机制:查询时根据索引统计信息,跳过不满足条件的Granule,减少数据读取量。
  • 索引类型
    • minmax:记录Granule中列的最小值/最大值,适用于范围查询(如数值列、日期列)。
    • set(max_rows) :记录Granule中列的取值集合(最多max_rows个),适用于低基数枚举列(如状态码)。
    • bloom_filter:基于布隆过滤器判断列值是否存在,适用于高基数列的等值查询(如UserID)。
    • ngrambf_v1 :支持文本子串的布隆过滤器,适用于模糊查询(如LIKE '%error%')。
    • tokenbf_v1:基于分词后的布隆过滤器,适用于分词后的文本搜索(如日志关键词)。

索引的存储与维护

  • 存储结构

    • 主键索引 :每个MergeTree表对应一个primary.idx文件,按Granule顺序存储排序键的起始值。

    • 跳数索引

      1..idx:存储索引数据(如minmax值、布隆过滤器位数组)。

      2..mrk:记录索引条目与数据文件的偏移量映射。

  • 数据写入与合并

    • 写入阶段:数据插入时,每个新生成的Part(数据部分)独立维护其索引文件。
    • 合并阶段(Merge):多个Parts合并时,重新生成合并后的索引文件,确保索引的连续性。

索引优化策略

  • 主键设计原则

    • 高频过滤列前置ORDER BY的第一列应为最常用的范围过滤条件(如时间列)
    • 避免过长主键:主键列过多会增加索引文件大小,降低查询效率。
  • 跳数索引选择

    • 低基数列setminmax索引。

    • 高基数等值查询bloom_filter

    • 文本搜索ngrambf_v1tokenbf_v1

  • 参数调优

    • index_granularity :减小此值(如4096)可提升索引精度,但会增加索引文件大小。
    • GRANULARITY :跳数索引的粒度需平衡精度与存储开销。较小的粒度(如2)过滤更精确,但索引条目更多。

查询优化的核心策略

执行计划分析与优化

  • 执行计划分析

    • ReadFromMergeTree :检查是否命中主键索引或跳数索引(如显示Index ... used)。

    • Aggregating :判断是否启用并行聚合(ParallelAggregating)。

    • Sorting :是否使用外部排序(ExternalSort)或内存排序(InMemorySort)。

  • 执行优化

    • 并行化 :设置parallel_aggregation_min_rows(默认100000)触发多线程聚合。

    • 索引覆盖 :确保WHERE条件匹配主键前缀或跳数索引。

查询优化

  • 过滤条件下推:将过滤条件尽可能提前,减少中间结果集。

  • ClickHouse语法扩展:LIMIT BY替代窗口函数、ANY JOIN减少Shuffle。

  • 分布式查询优化

    • GLOBAL修饰符 :小表右连接时使用GLOBAL INGLOBAL JOIN,避免重复计算。
    • 本地预聚合 :分布式聚合前启用distributed_aggregation_memory_efficient
  • 数据预聚合与存储优化

    • 物化视图加速:预计算高频指标;直接查询物化视图,避免全量扫描
    • 冷热数据分层:按时间自动迁移冷数据到廉价存储(TTL策略)。

分布式场景优化

  • 分片键选择
    • 均匀分布 :选择高基数列(如user_id)作为分片键,避免数据倾斜。
    • 本地性优化 :按业务属性分片(如region),减少跨节点查询。
  • 副本与一致性
    • 异步复制 :使用ReplicatedMergeTree实现副本同步,设置max_replicated_queries限制并发。
    • 最终一致性 :查询时添加SET allow_experimental_consistency=1容忍副本延迟。

资源管理与并发控制

资源管理:核心参数与策略

  • 内存管理

    • max_memory_usage:单个查询最大内存使用量(默认10GB,推荐根据节点内存调整)。
    • max_bytes_before_external_sort:触发外部排序的阈值,超过后使用磁盘暂存数据(默认1GB,推荐设为总内存的50%)。
    • max_memory_usage_for_all_queries:所有查询的总内存限制(默认0,表示无限制,推荐设为物理内存的80%)。
    • memory_overcommit_ratio:允许内存超卖的比例(默认0,不允许超卖)。
  • CPU管理

    • max_threads:单查询最大线程数(默认物理CPU核数,推荐设为逻辑CPU核数的75%)。
    • background_pool_size:后台任务(合并、删除)的线程池大小,推荐生产环境设为16
    • load_balancing:分布式查询的分片调度策略(随机/就近优先)。
  • 磁盘与网络

    • max_concurrent_queries:最大默认并发查询数(默认100),推荐根据磁盘IOPS进行调整。
    • max_bytes_to_read:单查询最大读取数据量(默认0,无限制),需要限制大查询。
    • network_bandwidth:网络带宽限制(默认0,无限制),推荐网络带宽限制(默认0,无限制)。

并发控制:队列与优先级s

  • 查询队列机制

    • max_running_queries:同时运行的查询数(默认100),超出后新查询排队。

    • max_waiting_queries:最大等待查询数(默认1000),超出后拒绝新查询。

  • 优先级调度 :通过**priority**参数设置查询优先级(范围1-10,默认0)。

  • 资源组(Resource Groups):配置资源组,实现物理资源隔离。

相关推荐
小蒜学长5 小时前
springboot多功能智能手机阅读APP设计与实现(代码+数据库+LW)
java·spring boot·后端·智能手机
追逐时光者6 小时前
精选 4 款开源免费、美观实用的 MAUI UI 组件库,助力轻松构建美观且功能丰富的应用程序!
后端·.net
你的人类朋友7 小时前
【Docker】说说卷挂载与绑定挂载
后端·docker·容器
间彧7 小时前
在高并发场景下,如何平衡QPS和TPS的监控资源消耗?
后端
间彧7 小时前
QPS和TPS的区别,在实际项目中,如何准确测量和监控QPS和TPS?
后端
间彧7 小时前
消息队列(RocketMQ、RabbitMQ、Kafka、ActiveMQ)对比与选型指南
后端·消息队列
brzhang8 小时前
AI Agent 干不好活,不是它笨,告诉你一个残忍的现实,是你给他的工具太难用了
前端·后端·架构
brzhang8 小时前
一文说明白为什么现在 AI Agent 都把重点放在上下文工程(context engineering)上?
前端·后端·架构
Roye_ack9 小时前
【项目实战 Day9】springboot + vue 苍穹外卖系统(用户端订单模块 + 商家端订单管理模块 完结)
java·vue.js·spring boot·后端·mybatis
AAA修煤气灶刘哥10 小时前
面试必问的CAS和ConcurrentHashMap,你搞懂了吗?
后端·面试