如何合理选择ClickHouse表主键

news2025/2/27 12:22:00

ClickHouse提供索引和数据存储的复杂机制,能够实现在高负载下仍有优异的读写性能。当创建MergeTree表时需要选择主键,主键影响大多数查询性能。本文介绍主键的工作原理,让我们知道如何选择合适的主键。

设置主键

MergeTree表可以设置主键,必须在创建表时指定,示例如下:

CREATE TABLE test
(
    `dt` DateTime,
    `event` String,
    `user_id` UInt64,
    `context` String
)
ENGINE = MergeTree
PRIMARY KEY (event, user_id, dt)
ORDER BY (event, user_id, dt)

上面在三个列上按一定顺序创建了主键:event, user_id, dt。注意,主键应该与排序键相同,或作为排序键的前缀。

排序键定义在磁盘上的排列顺序,主键定义查询使用的数据结构。通常主键与顺序键相同,如何相同可以忽略主键的定义,ClickHouse会自动采用排序键的字段。

数据存储粒度

ClickHouse把表记录划分为多个组,组称为粒度:

在这里插入图片描述

粒度大小基于表的设置(创建表时设置),缺省为8192,粒度数量可以通过下面公式进行计算:

在这里插入图片描述

单个粒度可以理解为虚拟小表(包括较少记录的子集,缺省为8192)。每个粒度按顺序存储行(order by 指定的键顺序):

在这里插入图片描述

主键标记和索引存储

主键仅存储每个粒度第一行,而不是每一行:

在这里插入图片描述

这就是ClickHouse查询快的原因。不保存所有值,技能保存部分使得主键特别小。Clickhouse不是查找单个行,而是先找到某个粒度,然后只对找到的粒度执行完整扫描(这是非常高效的,因为每个颗粒的尺寸都很小):

在这里插入图片描述

查询性能

这里填充5千万测试记录验证查询性能:

insert into test 
select * FROM generateRandom(
    'dt datetime, event Text, user_id UInt64, context Text',1, 20
) LIMIT 50000;

前节表定义了主键包括三个字段:

在这里插入图片描述

如果ClickHouse查询条件使用主键则能够利用主键提升性能:


SELECT *
FROM test
WHERE event = 'YJ9'

返回结果:

Query id: d237e4d9-5b6e-453f-befb-57e5ae84fd28

┌──────────────────dt─┬─event─┬──────────────user_id─┬─context─┐
│ 2079-09-01 19:10:41 │ YJ9   │ 16936621875208636777 │         │
└─────────────────────┴───────┴──────────────────────┴─────────┘

1 rows in set. Elapsed: 0.002 sec. Processed 8.19 thousand rows, 414.23 KB (3.78 million rows/s., 190.99 MB/s.)

我们看到搜索特定event值,仅扫描了单个粒度。这里YJ9只有一条,可以通过下面语句进行确认:

SELECT
    event,
    count(event != '') AS cnt
FROM test
GROUP BY event
HAVING cnt = 1
LIMIT 10

Query id: c5dc21e7-929a-4e0d-9ae9-0213caffee41

┌─event────┬─cnt─┐
│ Po$h\VLc │   1 │
│ YJ9      │   1 │
│ PS6>;.f  │   1 │
│ ov       │   1 │
│ |FYQ~1 │
│ yZ$~cP   │   1 │
│ kUAfps@  │   1 │
│ kX{]/:g(1 │
│ ]R,gw,vA │   1 │
│ qu       │   1 │
└──────────┴─────┘

下面通过explain查看执行计划进行确认:

explain indexes = 1 
SELECT *
FROM test
WHERE event = 'YJ9'

返回结果:

EXPLAIN json = 1, indexes = 1
SELECT *
FROM test
WHERE event = 'YJ9'

Query id: 6954664a-6e9c-4417-adba-d6f04270734b

┌─explain─────────────────────────────────────────────────────────────────────┐
│ Expression ((Projection + Before ORDER BY))                                 │
│   Filter (WHERE)                                                            │
│     SettingQuotaAndLimits (Set limits and quota after reading from storage) │
│       ReadFromMergeTree                                                     │
│       Indexes:                                                              │
│         PrimaryKey                                                          │
│           Keys:                                                             │
│             event                                                           │
│           Condition: (event in ['YJ9', 'YJ9'])                              │
│           Parts: 1/1                                                        │
│           Granules: 1/6                                                     │
└─────────────────────────────────────────────────────────────────────────────┘

可以看到仅使用了1/6,即扫描一个粒度。这说明不扫描全表,ClickHouse使用主键索引首先定义相应的粒度,然后在该粒度中扫描过滤,我们也可以在查询中使用多个主键列:

SELECT *
FROM test
WHERE event = 'YJ9' and user_id = '16936621875208636777'

返回结果:


SELECT *
FROM test
WHERE (event = 'YJ9') AND (user_id = '16936621875208636777')

Query id: 8deeb18f-8e85-4cb6-b8e4-fe6e11f2715c

┌──────────────────dt─┬─event─┬──────────────user_id─┬─context─┐
│ 2079-09-01 19:10:41 │ YJ9   │ 16936621875208636777 │         │
└─────────────────────┴───────┴──────────────────────┴─────────┘

1 rows in set. Elapsed: 0.003 sec. Processed 8.19 thousand rows, 414.23 KB (2.65 million rows/s., 133.93 MB/s.)

仍然仅扫描了单个粒度。相反如果使用条件列不在主键中,ClickHouse则需要全表扫描:

SELECT count(*)
FROM test where context = '{'

返回结果:


SELECT count(*)
FROM test
WHERE context = '{'

Query id: 0c6f01a1-7192-4d46-ad1b-89e465f051fd

┌─count()─┐
│      24 │
└─────────┘

1 rows in set. Elapsed: 0.002 sec. Processed 50.00 thousand rows, 951.29 KB (21.15 million rows/s., 402.31 MB/s.)

可以看到ClickHouse执行了全表扫描。另外,如果使用条件列跳过主键的前缀,ClickHouse也不能完全利用主键索引:

SELECT *
FROM test
WHERE user_id = '16936621875208636777'

返回结果:


SELECT *
FROM test
WHERE user_id = '16936621875208636777'

Query id: d9d2675d-b231-4458-9bc1-6854cf4cf865

┌──────────────────dt─┬─event─┬──────────────user_id─┬─context─┐
│ 2079-09-01 19:10:41 │ YJ9   │ 16936621875208636777 │         │
└─────────────────────┴───────┴──────────────────────┴─────────┘

1 rows in set. Elapsed: 0.003 sec. Processed 50.00 thousand rows, 748.69 KB (17.25 million rows/s., 258.23 MB/s.)

主键索引利用

下面总结ClickHouse 利用主键索引的场景:

  1. 查询where或order子句包含主键的第一列
  2. 查询where包含主键的前x列,order子句包含主键的前x列
  3. 查询where包含主键的所有列
  4. 其他情况,ClickHouse需要扫描全表获取请求数据。

在这里插入图片描述

总结

基于ClickHouse优化结构和排序数据,正确利用主键索引能节约资源,极大提升查询性能。总之选择主键需遵循下面简单规则:

  1. 选择计划在大多数查询中使用的列
  2. 选择大部分查询需要的列,如主键包含3列,查询包括1列或2列
  3. 如果查询不确定,首先使用低基数列,然后再使用高基数列,从而获得更好的压缩和提高磁盘利用率
    参考资料:https://medium.com/datadenys/how-clickhouse-primary-key-works-and-how-to-choose-it-4aaf3bf4a8b9

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

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

相关文章

香橙派5使用RK3588S内置NPU加速yolov5推理,实时识别数字达到50fps

前言: 香橙派5采用了RK3588S,内置的NPU达到了6Tops的算力,博主这里记录一下自己的踩坑过程,好不容易做出来的不能以后忘记了(手动狗头)。这里博主还在B站上发布了效果视频,大家感兴趣的话可以看…

TensorFlow 和 Keras 应用开发入门:1~4 全

原文:Beginning Application Development with TensorFlow and Keras 协议:CC BY-NC-SA 4.0 译者:飞龙 本文来自【ApacheCN 深度学习 译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。 不要担心自己的形…

Java 中的 非并发容器

1.四大类容器 java中容器主要有四大类,如下图所示 2.非并发容器 1) List 类 List 类 不支持并发的有 ArrayList 与 LinkedList ArrayList 底层实现 ArrayList 底层为 数组,由于数组的特性,非常适合用于 查询多,增删改的业务…

【数据结构学习1】数据结构

目录数据结构定义数据结构的构成逻辑结构逻辑结构的类型存储结构数据运算数据类型和抽象数据类型算法定义分析基础时间复杂度分析事前分析估算法 -> 分析算法的执行时间时间复杂度时间复杂度类型简化的算法时间复杂度分析空间复杂度分析数据结构 定义 数据:所有…

工作流调度系统 Azkaban介绍与安装(一)

文章目录前言1、为什么要用工作流调度系统2、常见的工作流调度系统1 集群规划2 配置 MySQL3 配置 Executor Server3.1 修改 azkaban.properties3.2 启动3.3 激活4 配置 Web Server4.1 修改 azkaban.properties4.2 修改azkaban-users.xml文件,添加 atguigu 用户4.3 启…

VM 虚拟机没有网络,无法Ping通

场景: 虚拟机用过,之前一切正常,使用NAT模式联网,配置了静态IP换了路由器,推测是主机IP网段变了无法使用ssh工具连接虚拟机,且相互都ping不通(后来经历了主机可以ping通虚拟机,虚拟…

PWM寄存器初始化

本模块主要实现输出频率占空比可调的 PWM 波形功能和输入捕获功能,同时也可作为计数器使用。一、主要特性 1. 16位向上或向下计数器; 2. 支持最多6路PWM通道; 3. 每个通道支持输出比较或边缘对齐PWM模式波形输出,支持设置、清除、…

关于 CSDN-AI 机器人 programmer_ada —— 阿达·洛夫莱斯(Ada Lovelace)

收到早期文章的一条新评论: 文笔和内容稍稍透漏着机器人的风格,打开主页果不其然 看到个人介绍中的巴贝奇的分析机,突然觉得头像很是眼熟。 最近刚读了《人工智能简史》,第4章——从汇编语言到TensorFlow,人工智能的…

使用layui组件库制作进度条

使用layui组件库制作进度条 html代码 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>Example</title><!-- 引入 layui 的 CSS 文件 --><link rel"stylesheet" href"https://cdn.staticfil…

Day948.组件化成熟度评估,你的目的地在哪里呢 -系统重构实战

组件化成熟度评估&#xff0c;你的目的地在哪里呢 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于组件化成熟度评估&#xff0c;你的目的地在哪里呢的内容。 一、组件化成熟度模型 组件化成熟度模型可以帮助咱全局去思考当前的现状&#xff0c;并制定更有针对性的…

ChatGPT带火的百万年薪职业究竟是什么?

对话有ChatGPT、画图有Midjourney&#xff0c;哪怕被封号了&#xff0c;国内的文心一言、通义千问也不遑多让。 ChatGPT等生成式AI工具涌现程度堪比“乱花渐欲迷人眼”。 拟一份演讲稿&#xff0c;画一张海报&#xff0c;做一份PPT大纲&#xff0c;生成个图表&#xff0c;敲一…

【数据结构】- 线性表+顺序表(上)

文章目录前言一、线性表二、顺序表2.1概念及结构2.2接口实现2.3具体实现总结前言 所有的失败都是上帝在考验你是否真的热爱 本章是关于数据结构中的顺序表和链表 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、线性表 线性表&#xff08;linear …

Xilinx CDC Constraints(ug903: Chapter6 )

&#xff08;1&#xff09;About CDC Constraints 跨时钟域约束适用于具有不同启动和捕获时钟的定时路径。根据启动和捕获时钟的关系以及在CDC路径上设置的时序异常&#xff0c;有同步CDC和异步CDC。例如&#xff0c;同步时钟之间但被错误路径约束覆盖的CDC路径不被定时…

传统图像处理——颜色迁移

转自知乎&#xff1a;https://zhuanlan.zhihu.com/p/267832794,仅供学习。 利用一张图片的颜色去修改另一张图片的颜色风格。 原理是利用颜色空间的正交化&#xff0c;即更改某个颜色&#xff0c;不会影响到其它属性。这里的色彩迁移的论文则是使用了LAB空间&#xff08;RGB颜…

C语言初阶--连用scanf(以%c读取时)遇到的问题

目录前言总结前言 在我们写程序的过程中&#xff0c;会频繁使用scanf函数&#xff0c;当在一个程序中scanf用多了&#xff0c;会出现输入不了的问题&#xff01;大家有没有想过是什么原因导致的该问题呢&#xff1f;下面我们一起探讨一下吧&#xff01; 遇到问题的例子&#…

Linux环境下安装JDK1.8

目录 一、下载jdk 二、安装准备 三、解压缩包到指定安装目录 四、配置环境变量 五、验证安装结果 一、下载jdk 这部分依然是从Oracle官网下载&#xff0c;下载速度还是很快的。 下载完成后&#xff0c;将该压缩包放到Linux环境下&#xff0c;准备解压安装。 二、安装准备…

Redis性能调优详解

文章目录前言确认是否是Redis真的变慢了&#xff1f;什么是基准性能&#xff1f;具体如何做&#xff1f;使用复杂度过高的命令哪些属于复杂度过高命令--聚合类命令、 大值数据针对这种情况如何解决呢&#xff1f;操作bigkeybigkey耗时原因如何定位出bigKey--bigkeys这里我需要提…

34岁本科男,做了5年功能测试想转行,除了进厂还能干什么?

我的建议是不要给自己设限。任何一个行业只要做到顶尖都是很有作为的&#xff0c;何况是IT行业&#xff0c;本身就比别的行业有优势&#xff0c;如果你现在是功能测试&#xff0c;应该想的是进阶自动化测试或者测试开发 如何在半年时间由功能测试成长为年薪30W的测试开发&#…

【Hydro】常用地下水与溶质运移模拟软件

饱和地下水流和溶质运移常见的模拟软件 常用的求解地下水水流和溶质运移方程的数学方法有两种&#xff1a;有限差分法和有限元法。两者主要的差别在于离散模型区的方法不同。基于不同的数学方法&#xff0c;当前市场上有一些地下水模拟图形用户界面&#xff0c;它们在基本功能…

【Python基础入门学习】Python工具Pycharm的安装与使用

一、关于Python 1.1 下载Python 在下载与安装pycharm工具前&#xff0c;一定要先安装python 打开Python官网&#xff1a;python下载打开上述网站&#xff0c;选择 Downloads -> 系统 我是Windows系统&#xff0c;点击进入后&#xff0c;找到自己要安装的安装包以及想安装的…