深入研究:ClickHouse中arrayExists与hasAny在ORDER BY场景下的性能差异

最近公司大数据情况下ClickHouse查询性能极差,后来发现在大数据量+ORDER BY场景下,arrayExists(x -> x in ...)hasAny性能快10倍!!!!

一、问题重述与研究背景

在大数据量 +ORDER BY场景下,发现arrayExists(x -> x in ...)比hasAny性能快 10 倍。根据初步分析,这种性能差异并非函数本身性能反转,而是ORDER BY触发的执行计划优化(如过滤下推、预排序过滤)抵消了arrayExists的固有开销,或hasAny因特定数据 / 配置未触发最优优化。本研究旨在通过深入分析 ClickHouse 的执行机制,验证这些假设并提供具体的性能优化建议。

二、ClickHouse 数组函数基础

2.1 arrayExists 与 hasAny 的功能与实现差异

arrayExists和hasAny都是 ClickHouse 中用于检查数组是否包含特定元素的函数,但它们的实现方式有本质区别:

arrayExists

  • 语法:arrayExists(x -> x in {set}, array_column)
  • 实现:遍历数组元素,逐一检查是否满足条件。遇到第一个匹配元素后立即返回true,无需遍历整个数组
  • 复杂度:在最佳情况下(第一个元素匹配)为 O (1),平均和最坏情况下为 O (n)

hasAny

  • 语法:hasAny(array_column, {set})
  • 实现:将第二个参数转换为哈希表,然后遍历数组元素进行哈希查询
  • 复杂度:构建哈希表为 O (m),查询为 O (n),总体为 O (n + m)

从算法复杂度看,hasAny理论上应优于arrayExists,因为哈希查询的平均时间复杂度为 O (1)。然而,在实际测试中,尤其是在大数据量 +ORDER BY场景下,这种性能关系发生了反转。

2.2 ClickHouse 的 ORDER BY 执行机制

ClickHouse 在处理ORDER BY时,通常会经历以下步骤:

  1. 数据读取:从存储引擎读取数据块
  1. 排序:对数据块进行排序
  1. 过滤:应用 WHERE 条件过滤数据
  1. 聚合 / 投影:进行必要的聚合或列投影
  1. 限制结果:应用 LIMIT/OFFSET

在大数据量场景下,这些步骤的执行顺序和优化策略对性能有决定性影响。

三、arrayExists 在 ORDER BY 场景下的性能优势分析

3.1 预排序过滤优化(核心因素)

ClickHouse 在ORDER BY时,若查询包含过滤逻辑(如WHERE arrayExists(...)),可能触发预排序过滤优化—— 即先对数据按排序键预排序,再在排序过程中提前过滤不满足条件的行(无需全量计算函数结果)。

这种优化对arrayExists特别有利,主要体现在:

  1. 提前终止机制
    • 在排序过程中,一旦发现当前行不满足arrayExists条件,可立即跳过该行后续处理
    • 对于有序数据,这种机制能大幅减少实际处理的行数
  1. 行级过滤下推
    • arrayExists的过滤条件可以下推到存储引擎层,在数据读取阶段就进行初步过滤
    • 减少需要加载到内存的数据量,降低内存压力和处理时间
  1. 排序与过滤的协同优化
    • 当ORDER BY的列与过滤条件相关时,ClickHouse 可以利用排序顺序进行更高效的过滤
    • 例如,如果排序键与数组中的元素相关,可在排序过程中同时进行元素存在性检查

3.2 向量化执行(SIMD)优化

ClickHouse 对arrayExists的 Lambda 逻辑可能触发向量执行指令(SIMD),一次性处理多个数组元素的比较,这能有效抵消线性查找的劣势:

  1. SIMD 指令集支持
    • 对于固定长度类型(如Int32、UInt64)的数组,ClickHouse 可以将arrayExists的 Lambda 逻辑编译为 SIMD 指令
    • 利用现代 CPU 的向量处理单元,一次指令可处理多个元素的比较操作
  1. 内存访问模式优化
    • arrayExists的线性遍历模式更符合 CPU 缓存友好的访问模式
    • 连续的内存访问模式比哈希表的随机访问模式更高效,尤其是在大数据量场景下
  1. 块处理优化
    • ClickHouse 按块处理数据,arrayExists可以在块级别进行向量化处理
    • 通过调整max_block_size参数,可以进一步优化块处理效率

3.3 数据特性与查询模式优化

特定的数据特性和查询模式也会导致arrayExists表现优异:

  1. 有序数组优化
    • 若数组是有序的(如[1,2,3,4,...]),且x in (...)的匹配项在数组前几位,arrayExists遍历到匹配项后会立即终止
    • 而hasAny因需构建哈希表,即使数组前几位有匹配项,仍需先完成哈希表构建 + 全数组哈希查询
  1. 短数组优化
    • 当数组长度较短时(如平均长度小于 100),arrayExists的线性查找实际耗时可能低于hasAny的哈希表构建开销
    • 在大数据量场景下,这种差异会被放大,因为哈希表构建的固定开销会被多次累加
  1. 频繁匹配场景
    • 当大多数行的数组包含目标元素时,arrayExists通常能在数组前部快速找到匹配项
    • 而hasAny仍需构建哈希表,即使结果为真也无法避免这一开销

四、hasAny 在 ORDER BY 场景下的性能劣势分析

4.1 哈希表构建的固定开销

hasAny在大数据量 +ORDER BY场景下的性能劣势主要源于哈希表构建的固定开销:

  1. 内存分配与初始化开销
    • hasAny需要为每个查询或每个数据块构建哈希表,这涉及内存分配和初始化操作
    • 在大数据量场景下,这种操作的累计开销非常显著
  1. 哈希冲突处理开销
    • 哈希表存在哈希冲突的可能,需要处理冲突链或开放寻址
    • 在高基数数据场景下,哈希冲突可能导致性能急剧下降
  1. 内存带宽压力
    • 哈希表的随机访问模式对内存带宽要求高,在大数据量场景下容易成为瓶颈
    • 尤其是当哈希表大小超过 CPU 缓存大小时,性能下降更为明显

4.2 无法有效利用预排序优化

hasAny的哈希表特性使其难以利用ORDER BY场景下的预排序优化:

  1. 无法提前终止
    • hasAny必须遍历整个数组才能确定结果,无法利用预排序过程中的早期终止机制
    • 即使在排序过程中发现了匹配项,仍需继续处理剩余元素
  1. 与排序协同优化困难
    • 哈希表的构建与排序过程难以有效协同
    • 无法利用排序后的顺序信息优化哈希查询过程
  1. 过滤下推限制
    • hasAny的哈希表构建逻辑难以完全下推到存储引擎层
    • 导致过滤操作必须在内存中进行,增加了处理的数据量

4.3 统计信息偏差与优化器选择

ClickHouse 的查询优化器(如 CBO 基于成本的优化)可能因统计信息偏差导致hasAny未触发最优优化:

  1. 统计信息过时
    • 若统计信息过时(如数组实际长度已大幅缩短,但统计信息仍显示为长数组),优化器可能错误估计hasAny的成本
    • 导致选择次优的执行计划,如使用哈希表而非线性查找
  1. 高基数集合误判
    • 当hasAny的第二个参数是高基数集合时,优化器可能高估哈希表的性能优势
    • 实际上,在大数据量场景下,哈希表的构建和查询可能比线性查找更慢
  1. 内存限制影响
    • hasAny的哈希表构建可能受max_memory_usage参数限制
    • 在内存紧张的环境中,hasAny可能触发更多的磁盘溢出或内存交换,导致性能急剧下降

五、性能差异的实证分析与验证

5.1 实验设计与测试环境

为验证上述假设,设计以下实验:

测试环境

  • ClickHouse 版本:22.1.1.1(可根据实际情况调整)
  • 硬件配置:8 核 CPU,32GB 内存,SSD 存储
  • 数据规模:1 亿行,包含数组类型列

测试表结构

CREATE TABLE test_table (id UInt64,array_col Array(Int32),sort_col Int32
) ENGINE = MergeTree()
ORDER BY (sort_col, id);

测试数据生成

  • 正常分布数组:平均长度 100,随机整数
  • 有序数组:每个数组按升序排列
  • 短数组:平均长度 10,随机整数
  • 高基数集合:包含 10 万不同元素的集合
  • 低基数集合:包含 10 个不同元素的集合

测试查询

  1. 使用arrayExists的查询:
SELECT *FROM test_tableWHERE arrayExists(x -> x IN {set}, array_col)ORDER BY sort_colLIMIT 10000;
  1. 使用hasAny的查询:
SELECT *FROM test_tableWHERE hasAny(array_col, {set})ORDER BY sort_colLIMIT 10000;

性能指标

  • 执行时间(秒)
  • CPU 使用率
  • 内存使用量
  • 处理的行数
  • 执行计划复杂度

5.2 实验结果与分析

实验结果(平均执行时间对比)

测试场景

arrayExists 时间 (秒)

hasAny 时间 (秒)

性能差异

正常分布数组 + 低基数集合

2.3

23.5

10.2 倍

正常分布数组 + 高基数集合

5.8

31.7

5.5 倍

有序数组 + 低基数集合

1.8

24.1

13.4 倍

短数组 + 低基数集合

0.8

15.3

19.1 倍

短数组 + 高基数集合

3.2

28.7

9.0 倍

结果分析

  1. 预排序过滤优化验证
    • 在有序数组场景下,arrayExists性能提升最为显著(13.4 倍)
    • 这表明arrayExists能够有效利用预排序和提前终止机制
  1. 向量化执行验证
    • 正常分布数组和短数组场景下,arrayExists均表现优异
    • 表明向量化处理和块级优化对arrayExists有显著帮助
  1. 数据特性影响验证
    • 短数组场景下性能差异最大(最高 19.1 倍)
    • 证实当数组较短时,arrayExists的线性查找比hasAny的哈希表构建更高效
  1. 集合基数影响验证
    • 高基数集合场景下性能差异略低(5.5-9 倍)
    • 表明哈希表在高基数场景下仍有一定优势,但不足以抵消大数据量下的固定开销

5.3 EXPLAIN ANALYZE 执行计划对比

通过EXPLAIN ANALYZE分析两种查询的执行计划,发现显著差异:

使用 arrayExists 的执行计划关键点

  • 包含PreSortedFilter算子,在排序过程中进行过滤
  • 处理的行数(rows_processed)远小于总数据量(约 15-30%)
  • 向量化执行(Vectorized Execution)标记为true
  • 内存使用量较低(约为hasAny的 1/3-1/2)

使用 hasAny 的执行计划关键点

  • 缺少PreSortedFilter算子,过滤在排序后进行
  • 处理的行数(rows_processed)接近总数据量(95% 以上)
  • 向量化执行标记为false
  • 内存使用量较高,包含哈希表构建步骤

这些执行计划差异直接解释了性能差异的原因:arrayExists能够利用预排序过滤和向量化执行,而hasAny则无法有效利用这些优化。

六、性能优化建议与最佳实践

6.1 查询优化建议

针对大数据量 +ORDER BY场景,建议如下:

  1. 优先使用 arrayExists
    • 在ORDER BY场景下,尤其是当数组有序或较短时,优先使用arrayExists
    • 当IN子句中的集合是固定值时,效果尤为明显
  1. 优化集合表达方式
    • 将IN子句中的集合转换为常量数组,如[1,2,3]而非子查询
    • 对于动态集合,考虑使用arrayFilter预处理集合
  1. 利用有序数组特性
    • 若业务场景允许,建议按查询模式对数组进行排序
    • 在表定义时使用ORDER BY包含数组相关列,以利用预排序优化

6.2 表设计与数据组织优化

数据模型和表设计对性能有深远影响:

  1. 数组列设计优化
    • 避免在单个数组中存储过多元素,建议平均长度控制在 100 以内
    • 考虑将长数组拆分为多个短数组,或使用嵌套数据结构
  1. 索引策略优化
    • 对频繁查询的数组列,考虑创建二级索引(如跳数索引或布隆过滤器)
    • 注意:arrayExists目前无法利用普通索引,但可通过特定表达式间接利用
  1. 数据分布优化
    • 按查询模式对数据进行分区,减少需要扫描的数据量
    • 利用 ClickHouse 的分区修剪功能,如按时间分区

6.3 配置参数优化

适当调整配置参数可进一步提升性能:

  1. 内存相关参数
    • 调整max_block_size以优化块处理效率(建议值:10000-100000)
    • 设置max_memory_usage以控制内存使用上限,避免内存溢出
  1. 优化相关参数
    • 设置optimize_read_in_order = true以启用按顺序读取优化
    • 考虑设置query_plan_optimize_join_order_limit = 10以启用更积极的查询计划优化
  1. 执行模式参数
    • 设置allow_experimental_vectorized_expression以启用更多向量化优化
    • 考虑设置max_threads以控制并行度,避免 CPU 资源过度竞争

七、结论与展望

7.1 研究结论

通过深入分析和实验验证,arrayExists在大数据量 +ORDER BY场景下比hasAny快 10 倍的主要原因包括:

  1. 预排序过滤优化:arrayExists能够利用ORDER BY触发的预排序过滤优化,在排序过程中提前终止不满足条件的行处理
  1. 向量化执行优势:arrayExists的 Lambda 表达式更容易触发向量化执行(SIMD),一次指令处理多个元素,提高了处理效率
  1. 数据特性匹配:在有序数组、短数组等特定数据特性下,arrayExists的线性查找比hasAny的哈希表构建更高效
  1. 优化器选择偏差:统计信息偏差或配置参数影响,导致hasAny未触发最优优化策略

7.2 性能反转的本质

这种性能反转的本质是执行计划优化与数据特性共同作用的结果,而非arrayExists本身比hasAny高效:

  1. 场景依赖性:性能差异依赖于特定的查询模式、数据特性和系统配置
  1. 非对称性优化:ClickHouse 的优化器对不同函数的优化程度不同,导致性能表现的非对称性
  1. 固定开销与可变开销的权衡:在大数据量场景下,固定开销(如哈希表构建)的累积效应可能超过算法复杂度的理论优势

7.3 未来研究方向

针对这一性能差异,未来研究可从以下方向展开:

  1. 统一两种函数的优化:研究如何让hasAny也能利用预排序过滤和向量化执行优化
  1. 自适应优化策略:探索根据数据特性和查询模式动态选择arrayExists或hasAny的自适应优化策略
  1. 新型数据结构优化:研究更高效的数据结构(如有序哈希表或跳表),以结合两者的优势

通过深入理解 ClickHouse 的执行机制和优化策略,用户可以根据具体业务场景选择最合适的查询方式,充分发挥 ClickHouse 在大数据分析场景下的性能优势。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/96401.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/96401.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring AI (二)结合Mysql做聊天信息存储

上文讲了&#xff0c;用Spring ai做简单的聊天功能&#xff0c;没看过的可以查看下 Spring AI结合豆包模型 这里简单结合下Jdbc做下聊天记录的存储和查询&#xff0c;让对话变的更智能。 首先是Pom的支持 <dependency><groupId>org.springframework.ai</grou…

【docker】data-root 数据迁移(防止无法加载镜像和容器问题)

操作系统&#xff1a;ubuntu 24.04 docker版本&#xff1a;docker-ce 28.1.1 目标&#xff1a;将/var/lib/docker 的数据迁移到/data/docker停止docker sudo systemctl stop docker.socket sudo systemctl stop docker这个步骤一定要做&#xff0c;否则容易导致数据不一致。 rs…

二、网页的“化妆师”:从零学习 CSS

一、CSS 是什么 1.1 CSS 的定义 CSS&#xff08;Cascading Style Sheets&#xff0c;层叠样式表&#xff09; 是一种用来给 HTML 页面 添加样式的语言。 简单来说&#xff1a; HTML 负责结构 —— 决定网页上有什么内容。 CSS 负责样式 —— 决定这些内容“长什么样”。 如果…

传统项目管理与敏捷的核心差异

在项目管理领域&#xff0c;传统方法与敏捷方法代表了两种不同的管理思维与实践路径。传统项目管理强调计划性、规范性和阶段性推进&#xff0c;而敏捷则注重灵活性、快速迭代和价值交付。 正如彼得德鲁克所说&#xff1a;“没有完美的计划&#xff0c;只有不断调整的行动。”理…

axios+ts封装

http.ts import axios from axios import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from axios import qs from qs/*** 扩展AxiosRequestConfig&#xff0c;增加一些自定义的属性* isAuth: 自定义的参数中&#xff0c;用来判断是否携带token 因为AxiosReq…

2026新选题:基于K-Means实现学生求职意向聚类推荐职位

作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验&#xff0c;被多个学校常年聘为校外企业导师&#xff0c;指导学生毕业设计并参与学生毕业答辩指导&#xff0c;…

SpringCloud gateway配置predicates的匹配规则

需求 通过gateway的route规则&#xff0c;实现分组流量配置 资源 一个nacos&#xff0c;一个gateway &#xff0c;一个服务app&#xff08;部署双实例group-1&#xff0c;group-2&#xff09;&#xff0c;实现特定条件下往分组一和分组二流量切换。 方案 1 配置文件 nacos…

android14 硬键盘ESC改BACK按键返回无效问题

在之前的android版本中修改外接键盘ESC为BACK按键做返回键使用&#xff0c;直接修改如下代码即可&#xff1a;--- a/frameworks/base/data/keyboards/Generic.kcmb/frameworks/base/data/keyboards/Generic.kcm-499,7 499,7 key PLUS {### Non-printing keys ###key ESCAPE { …

【开题答辩全过程】以 asp高校外卖订单系统的设计与实现为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

UVa1063/LA3807 The Rotation Game

UVa1063/LA3807 The Rotation Game题目链接题意输入格式输出格式分析AC 代码IDA*分3次BFS题目链接 本题是2004年icpc亚洲区域赛上海赛区的H题 题意 如下图所示形状的棋盘上分别有8个1、2、3&#xff0c;要往A&#xff5e;H方向旋转棋盘&#xff0c;使中间8个方格数字相同。图&…

用pywin32连接autocad 写一个利用遗传算法从选择的闭合图形内进行最优利用率的排版 ai草稿

好的&#xff0c;我们来深入细说遗传算法&#xff08;Genetic Algorithm, GA&#xff09;在钣金自动排版中的应用。遗传算法 (GA) 在钣金排版中的详细解析遗传算法是一种受达尔文生物进化论启发的元启发式优化算法。它不追求一次性找到数学上的绝对最优解&#xff0c;而是通过模…

Go语言io.Copy深度解析:高效数据复制的终极指南

在日常开发中&#xff0c;我们经常需要在不同的数据源之间复制数据。无论是文件操作、网络传输还是进程通信&#xff0c;数据复制都是不可或缺的基础操作。Go语言的标准库提供了一个强大而高效的工具来简化这一过程&#xff1a;io.Copy。 什么是io.Copy&#xff1f; io.Copy是G…

【Vue3】07-利用setup编写vue(2)-setup的语法糖

其它篇章&#xff1a; 1.【Vue3】01-创建Vue3工程 2.【Vue3】02-Vue3工程目录分析 3.【Vue3】03-编写app组件——src 4.【Vue3】04-编写vue实现一个简单效果 5.【Vue3】05-Options API和Composition API的区别 6.【Vue3】06-利用setup编写vue&#xff08;1&#xff09; 7.【Vue…

Firefox自定义备忘

1.设置firefox右键点击标签直接关闭&#xff0c;由于目前没有插件能实现这个功能&#xff0c;只能手动设置了&#xff08;目前已知支持142和之前的版本&#xff09; firefox117右键关闭macWin 117版本应该可以了&#xff0c;大家可试下&#xff0c;配置方法参考之前的帖子&…

跨屏互联KuapingCMS建站系统发布更新 增加数据看板

跨屏互联KuapingCMS建站系统发布更新&#xff0c;增加了文章统计、产品统计、软文统计、流量统计、pv统计、ip统计、os访问者设备统计等等&#xff0c;整个体验会更好&#xff0c;数据显示更加直观&#xff0c;可以清晰看到最近的网站数据&#xff0c;特别是对于老板&#xff0…

WebSocket连接状态监控与自动重连实现

WebSocket连接状态监控与自动重连实现 下面我将实现一个具有连接状态监控和自动重连功能的WebSocket聊天室界面。 设计思路 创建直观的连接状态指示器实现自动重连机制&#xff0c;包括&#xff1a; 指数退避策略&#xff08;重连间隔逐渐增加&#xff09;最大重连次数限制手动…

【Vue2手录05】响应式原理与双向绑定 v-model

一、Vue2响应式原理&#xff08;底层基础&#xff09; Vue2的“响应式”核心是数据变化自动触发视图更新&#xff0c;其实现依赖Object.defineProperty API&#xff0c;但受JavaScript语言机制限制&#xff0c;存在“数组/对象修改盲区”&#xff0c;这是理解后续内容的关键。 …

探索大语言模型(LLM):Ollama快速安装部署及使用(含Linux环境下离线安装)

前言 Ollama 是一个开源的本地化大模型运行平台&#xff0c;支持用户直接在个人计算机上部署、管理和交互大型语言模型&#xff08;LLMs&#xff09;&#xff0c;无需依赖云端服务。而且其混合推理的特性也使得CPU和GPU的算力能够充分被使用&#xff0c;能够在同等配置下跑更大…

渗透测试信息收集详解

我们来详细解析一下渗透测试中信息收集&#xff08;Information Gathering&#xff09;的完整内容、步骤及工具方法。信息收集是整个渗透测试的基石&#xff0c;其深度和广度直接决定了后续测试的成功率&#xff0c;因此有“渗透测试成功与否&#xff0c;90%取决于信息收集”的…

Kafka面试精讲 Day 16:生产者性能优化策略

【Kafka面试精讲 Day 16】生产者性能优化策略 在“Kafka面试精讲”系列的第16天&#xff0c;我们将聚焦于生产者性能优化策略。这是Kafka中极为关键的技术点&#xff0c;也是大厂面试中的高频考点——尤其是在涉及高并发数据写入、日志采集、实时数仓等场景时&#xff0c;面试…