跳到主要内容
版本:0.4.x

复杂查询调优

复杂查询执行模型

分析型查询可以分为简单查询和复杂查询,简单查询通常是单表检索聚合、大表与小表 Join 查询,查询响应快;复杂查询指的是有大表、多表关联和复杂的逻辑处理,通常查询响应慢消耗资源多。ByConity 在复杂查询上进行了优化设计。 简单的查询可以采用两阶段执行模型,计算节点上面执行的 partial 阶段,服务节点上面执行的 final 阶段,一旦我们需要执行一个复杂的多个聚合或者 join 的查询,两阶段的执行模型灵活性非常低,也让查询的优化变得棘手。为了更好的支持分布式查询,方便执行优化器产生的执行计划,我们引入了支持多轮分布式执行模式的复杂查询。

复杂查询执行模型图

复杂查询的执行流程如下:

  • Query SQL String 通过 parser 解析为 AST
  • 对 AST 做改写和优化,产生执行计划
  • 启用优化器的时候,通过优化器产生执行计划。
  • 将执行计划切分为多个 PlanSegment
  • PlanSegment 即分布式执行过程中的一个执行片段,它包含
  • 执行需要的 AST 片段,或者一个由 PlanNode 构成的逻辑执行计划片段
  • PlanSegment 执行的节点信息
  • PlanSegment 的上下游信息,这些信息包括上游的输入流,下游需要的输入流
  • 引擎的调度器会将这些 PlanSegment 构成一个 DAG,然后按拓扑排序下发给集群中的所有节点
  • 每个节点收到 PlanSegment 之后,开始驱动 PlanSegment 执行
  • 包含数据源的 PlanSegment 开始读取数据,将数据按照一定的 shuffle 规则分发到下游的各个节点上
  • 包含 exchange 输入的 PlanSegment 等待上游的数据,如果需要继续做 shuffle 则会继续将数据下发给各个节点
  • 多轮 stage 完成之后,结果会返回到服务端

如何开启查询级别参数

开启优化器会自动走复杂查询执行模型。通过配置 enable_optimizer=1,或者 dialect_type='ANSI' 可以开启。

  • 确定统计信息存在

没有统计信息,生成的查询计划不是最优。show stats [<db_name>.]<table_name>

  • 分析计划每一个 Step 的耗时

通过 explain analyze sql 可以显示每一个 Step 的耗时

  • 参数调优

ByteHouse 支持查询级别的参数调优,参数可以针对不同的查询分别进行设置。与 Exchange 算子相关的参数也可以通过这种方式进行设置,通过关键字SETTINGS 为查询指定参数值,如下所示:

SELECT 1 SETTINGS TEST_KNOB=1;

当执行此 SQL 语句时,参数 TEST_KNOB 将被强制设置为 1。以这种方式设置参数不会影响其他查询的参数值。

接下来,我们展示了一些对性能有重要影响的查询级别参数。

  1. Exchange 算子负责 PlanSegment 之间的数据传输。

exchange_source_pipeline_threads 影响 pipeline 执行的总线程数目,尤其是非 source 的 pipeline(指直接从存储读取数据的 pipeline),目前默认设置为 CPU 核数,目前并没有一个理想推荐值,可以考虑/2 或者*2 观察结果。如果查询内存占用较大,可以调小。

exchange_timeout_ms Exhange 超时时间(ms),默认为 100s,如果出现 Exchange 相关超时报错可以适当调大。

exchange_unordered_output_parallel_size 影响 Exchange 输出数据的并发能力,默认为 8,一般不需要调整。如果 exchange_source_pipeline_threads 调整比较大,可以适当调大 exchange_unordered_output_parallel_size,增加上游输出能力。

exchange_enable_block_compress 是否开启 Exchange 压缩,默认开启,如果 CPU 瓶颈比网络明显,可以尝试关闭。

exchange_parallel_size 决定了单分区数据向下游 shuffle 的分区数量,默认值为 1。通常情况下,无需调整此值。只有在未来需要对 Exchange 进行排序的场景中,才需要通过调整 exchange_parallel_size 来增加分区数量,以提高下游算子的并发处理能力。

exchange_local_receiver_queue_size 表示本地 exchange receiver 的队列大小。它通过算子的异步和算子状态的同步来实现软流控制。它向 Sender 发送两个信号,空闲和积压。Sender 会降低积压流的传输优先级,并提高空闲流的优先级。

exchange_remote_receiver_queue_size 表示远程 exchange receiver 的队列大小。

exchange_buffer_send_threshold_in_bytes 限制了 Exchange 刷新发送缓冲区时的最小字节数,它决定了 receiver 的积压阈值大小。

exchange_buffer_send_threshold_in_row 限制了 Exchange 刷新发送缓冲区时的最小行数,它决定了 receiver 的积压阈值大小。

  1. 有一些参数既可以对整个数据库生效,也可以对单个查询生效。

max_threads 限制了执行请求的最大线程数量。默认情况下,它是自动确定的。

max_block_size 限制了读取的最大块大小。默认情况下,它是 65536。

  1. 路由相关的参数通常用于确定分布式系统中数据的存储和访问路径。

group_by_two_level_threshold 表示从多少个键开始进行两级聚合。

group_by_two_level_threshold_bytes 表示从聚合状态达到多少字节时,开始使用两级聚合。

  1. runtime filter 旨在为某些连接查询在运行时动态生成过滤条件,以减少扫描的数据量,避免不必要的 I/O 和网络传输,从而加速查询。以下是一些与 runtime filter 相关的参数。

runtime_filter_min_filter_rows 限制了启用 runtime filter 的最小行数。

runtime_filter_bloom_build_threshold 限制了构建 bloom filter 的右表的阈值。