聊聊ClickHouse MergeTree引擎的固定/自适应索引粒度

前言

我们在刚开始学习ClickHouse的MergeTree引擎时,就会发现建表语句的末尾总会有SETTINGS index_granularity = 8192这句话(其实不写也可以),表示索引粒度为8192。在每个data part中,索引粒度参数的含义有二:

  • 每隔index_granularity行对主键组的数据进行采样,形成稀疏索引,并存储在primary.idx文件中;

  • 每隔index_granularity行对每一列的压缩数据([column].bin)进行采样,形成数据标记,并存储在[column].mrk文件中。

index_granularity、primary.idx、[column].bin/mrk之间的关系可以用ClickHouse之父Alexey Milovidov展示过的一幅简图来表示。

image.png

但是早在ClickHouse 19.11.8版本,社区就引入了自适应(adaptive)索引粒度的特性,并且在之后的版本中都是默认开启的。也就是说,主键索引和数据标记生成的间隔可以不再固定,更加灵活。下面通过简单实例来讲解固定索引粒度和自适应索引粒度之间的不同之处。

固定索引粒度

利用Yandex.Metrica提供的hits_v1测试数据集,创建如下的表。

CREATE TABLE datasets.hits_v1_fixed
(`WatchID` UInt64,`JavaEnable` UInt8,`Title` String,-- A lot more columns...
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID)
SETTINGS index_granularity = 8192, index_granularity_bytes = 0;  -- Disable adaptive index granularity

注意使用SETTINGS index_granularity_bytes = 0取消自适应索引粒度。将测试数据导入之后,执行OPTIMIZE TABLE语句触发merge,以方便观察索引和标记数据。

来到merge完成后的数据part目录中——笔者这里是201403_1_32_3,并利用od(octal dump)命令观察primary.idx中的内容。注意索引列一共有3列,Counter和intHash32(UserID)都是32位整形,EventDate是16位整形(Date类型存储的是距离1970-01-01的天数)。

[root@ck-test001 201403_1_32_3]# od -An -i -j 0 -N 4 primary.idx 57  # Counter[0]
[root@ck-test001 201403_1_32_3]# od -An -d -j 4 -N 2 primary.idx 16146        # EventDate[0]
[root@ck-test001 201403_1_32_3]# od -An -i -j 6 -N 4 primary.idx 78076527  # intHash32(UserID)[0]
[root@ck-test001 201403_1_32_3]# od -An -i -j 10 -N 4 primary.idx 1635  # Counter[1]
[root@ck-test001 201403_1_32_3]# od -An -d -j 14 -N 2 primary.idx 16149        # EventDate[1]
[root@ck-test001 201403_1_32_3]# od -An -i -j 16 -N 4 primary.idx 1562260480  # intHash32(UserID)[1]
[root@ck-test001 201403_1_32_3]# od -An -i -j 20 -N 4 primary.idx 3266  # Counter[2]
[root@ck-test001 201403_1_32_3]# od -An -d -j 24 -N 2 primary.idx 16148        # EventDate[2]
[root@ck-test001 201403_1_32_3]# od -An -i -j 26 -N 4 primary.idx 490736209  # intHash32(UserID)[2]

能够看出ORDER BY的第一关键字Counter确实是递增的,但是不足以体现出index_granularity的影响。因此再观察一下标记文件的内容,以8位整形的Age列为例,比较简单。

[root@ck-test001 201403_1_32_3]# od -An -l -j 0 -N 320 Age.mrk0                    00                 81920                163840                245760                327680                409600                491520                5734419423                    019423                 819219423                1638419423                2457619423                3276819423                4096019423                4915219423                5734445658                    045658                 819245658                1638445658                24576

上面打印出了两列数据,表示被选为标记的行的两个属性:第一个属性为该行所处的压缩数据块在对应bin文件中的起始偏移量,第二个属性为该行在数据块解压后,在块内部所处的偏移量,单位均为字节。由于一条Age数据在解压的情况下正好占用1字节,所以能够证明数据标记是按照固定index_granularity的规则生成的。

自适应索引粒度

创建同样结构的表,写入相同的测试数据,但是将index_granularity_bytes设为1MB(为了方便看出差异而已,默认值是10MB),以启用自适应索引粒度。

CREATE TABLE datasets.hits_v1_adaptive
(`WatchID` UInt64,`JavaEnable` UInt8,`Title` String,-- A lot more columns...
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID)
SETTINGS index_granularity = 8192, index_granularity_bytes = 1048576;  -- Enable adaptive index granularity

index_granularity_bytes表示每隔表中数据的大小来生成索引和标记,且与index_granularity共同作用,只要满足两个条件之一即生成。

触发merge之后,观察primary.idx的数据。

[root@ck-test001 201403_1_32_3]# od -An -i -j 0 -N 4 primary.idx 57  # Counter[0]
[root@ck-test001 201403_1_32_3]# od -An -d -j 4 -N 2 primary.idx 16146        # EventDate[0]
[root@ck-test001 201403_1_32_3]# od -An -i -j 6 -N 4 primary.idx 78076527  # intHash32(UserID)[0]
[root@ck-test001 201403_1_32_3]# od -An -i -j 10 -N 4 primary.idx61  # Counter[1]
[root@ck-test001 201403_1_32_3]# od -An -d -j 14 -N 2 primary.idx16151        # EventDate[1]
[root@ck-test001 201403_1_32_3]# od -An -i -j 16 -N 4 primary.idx1579769176  # intHash32(UserID)[1]
[root@ck-test001 201403_1_32_3]# od -An -i -j 20 -N 4 primary.idx63  # Counter[2]
[root@ck-test001 201403_1_32_3]# od -An -d -j 24 -N 2 primary.idx16148        # EventDate[2]
[root@ck-test001 201403_1_32_3]# od -An -i -j 26 -N 4 primary.idx2037061113  # intHash32(UserID)[2]

通过Counter列的数据可见,主键索引明显地变密集了,说明index_granularity_bytes的设定生效了。接下来仍然以Age列为例观察标记文件,注意文件扩展名变成了mrk2,说明启用了自适应索引粒度。

[root@ck-test001 201403_1_32_3]# od -An -l -j 0 -N 2048 --width=24 Age.mrk20                    0                 11200                 1120                 11200                 2240                 11200                 3360                 11200                 4480                 11200                 5600                 11200                 6720                 11200                 7840                  3520                 8192                 11110                 9303                 11110                10414                 11110                11525                 11110                12636                 11110                13747                 11110                14858                 11110                15969                  4150                16384                 1096
# 略去一些17694                    0                 110217694                 1102                 110217694                 2204                 110217694                 3306                 110217694                 4408                 110217694                 5510                 110217694                 6612                  95617694                 7568                 1104
# ......

mrk2文件被格式化成了3列,前两列的含义与mrk文件相同,而第三列的含义则是两个标记之间相隔的行数。可以观察到,每隔1100行左右就会生成一个标记(同时也说明该表内1MB的数据大约包含1100行)。同时,在偏移量计数达到8192、16384等8192的倍数时(即经过了index_granularity的倍数行),同样也会生成标记,证明两个参数是协同生效的。

最后一个问题:ClickHouse为什么要设计自适应索引粒度呢?

当一行的数据量比较大时(比如达到了1kB甚至数kB),单纯按照固定索引粒度会造成每个“颗粒”(granule)的数据量膨胀,拖累读写性能。有了自适应索引粒度之后,每个granule的数据量可以被控制在合理的范围内,官方给定的默认值10MB在大多数情况下都不需要更改。

作者:京东物流 康琪

来源:京东云开发者社区 自猿其说 Tech 转载请注明来源

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

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

相关文章

Qt多语言翻译

Qt多语言翻译概述 Qt提供了非常简单易用的多语言翻译机制,其核心类为QTranslator.概括来说就是利用Qt的lupdate工具将项目中所有tr函数包裹的字符串提取到.ts文件中,然后使用Qt Linguist由专门的翻译人员对提取的.ts文件进行逐个单词短语的翻译工作. 翻译…

Node.js Express 框架 2024版 笔记

1.0 操作命令 Node.js express 框架 https://www.expressjs.com.cn/ npm install -g express-generator expressexpress --pug --git // --pug 添加对 pug 模板引擎的支持 // --git 添加 .gitignore 代码仓库排除 //无法直接安装新版pug模板 npm i npm …

CentOS7中安装ElasticSearch

文章目录 检测是否安装了Elasticsearch安装JDK下载java配置 下载Elasticsearch解压安装Elasticsearch修改配置文件启动Elasticsearch常见问题 ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasti…

C++ Qt开发:SqlTableModel映射组件应用

Qt 是一个跨平台C图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍SqlTableModule组件的常用方法及灵活运用。 …

Java 数据结构 二叉树(一)二叉查询树

目录 树的种类 二叉树 二叉查找树 满二叉树 ​编辑 完全二叉树 二叉树的数据存储 链式存储 数组存储 寻址方式: 二叉树的遍历(了解即可) ​编辑 二叉查询树缺点 前言-与正文无关 生活远不止眼前的苦劳与奔波,它还充满…

如何在Windows系统使用Plex部署影音服务与公网访问本地资源【内网穿透】

文章目录 1.前言2. Plex网站搭建2.1 Plex下载和安装2.2 Plex网页测试2.3 cpolar的安装和注册 3. 本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 正文开始前给大家推荐个网站,前些天发现了一个巨牛的 人工智能学习网站, 通…

java设计模式:观察者模式

在平常的开发工作中,经常会使用到设计模式。合理的使用设计模式,可以提高开发效率、提高代码质量、提高代码的可拓展性和维护性。今天来聊聊观察者模式。 观察者模式是一种行为型设计模式,用于对象之间一对多的依赖关系,当被观察对…

【SAR成像】基于RD、CS和ωk算法的合成孔径雷达成像算法原理与实现

基于RD、CS和ωk算法的合成孔径雷达成像算法实现 前言SAR基本概念雷达获取数据的几何关系低斜视角下的回波信号模型 RADARSAT-1主要参数数据预处理数据读取与再封装数据补零 成像算法坐标轴的产生RD算法距离压缩距离徙动矫正方位压缩 CS算法第一次相位相乘 变标后的信号第二次相…

命令注入漏洞原理以及修复方法

漏洞名称 :命令注入 漏洞描述:Command Injection,即命令注入攻击,是指由于Web应用程序对用户提交的数据过滤 不严格,导致黑客可以通过构造特殊命令字符串的方式,将数据提交至Web应用程序中,并利…

Windows错误“ 0xc0000005”解决与分析全流程

Windows错误“ 0xc0000005”解决与分析全流程 问题的描述 Windows错误“ 0xc0000005”原因分析内存条的选择实操流程展示 问题的描述 Windows错误“ 0xc0000005” 问题发生的最开始是,电脑的系统一直运行的时候一直蓝屏报错,越来越频繁(在电…

【C/Python】Gtk部件ListStore的使用

一、C语言 在GTK中,Gtk.ListStore是一个实现了Gtk.TreeModel接口的存储模型,用于在如Gtk.TreeView这样的控件中存储数据。以下是一个简单的使用Gtk.ListStore的C语言示例,该示例创建了一个列表,并在图形界面中显示: …

基于Springboot的校园失物招领网站(有报告)。Javaee项目,springboot项目。

演示视频: 基于Springboot的校园失物招领网站(有报告)。Javaee项目,springboot项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结构…

优秀学习网站推荐-第一辑

原文地址:https://jaune162.blog/2024/02/15/study-website-recommend Developer Roadmaps(开发者路线图) 官网地址:https://roadmap.sh/ 该网站包含了各个方向、各个语言的开发人员从零开始学习的路线图。 下图为Java方向的学…

elk之基本crud

写在前面 本文看下工作中用的最多的CRUD。让我们一起来做一个帅帅的CRUD BOY吧!!! 1:基本操作 Create 格式1(指定ID):PUT 索引名称/_create/文档ID {文档json} 格式2(不指定ID):POST 索引名称…

18.通过telepresence调试部署在Kubernetes上的微服务

Telepresence简介 在微服务架构中,本地开发和调试往往是一项具有挑战性的任务。Telepresence 是一种强大的工具,使得开发者本地机器上开发微服务时能够与运行在 Kubernetes 集群中的其他服务无缝交互。本文将深入探讨 Telepresence 的架构、运行原理,并通过实际的案例演示其…

导出pdf 加密、加水印、加页脚

1.依赖 <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.10</version> </dependency> <dependency> …

从0开始搭建若依微服务项目 RuoYi-Cloud(保姆式教程完结)

文章接上一章&#xff1a; 从0开始搭建若依微服务项目 RuoYi-Cloud&#xff08;保姆式教程 一&#xff09;-CSDN博客 四. 项目配置与启动 当上面环境全部准备好之后&#xff0c;接下来就是项目配置。需要将项目相关配置修改成当前相关环境。 数据库配置 新建数据库&#xff…

VitePress-08-文档中代码组的使用

什么是代码组 代码组 : 就是代码块的集合。一个代码组中可以包含多个代码块。 效果 &#xff1a; 用页签的形式将不同的代码块分开展示。 代码组的语法格式 代码组的语法格式较为固定&#xff0c;如下 &#xff1a; ::: code-group代码块1的类型 [代码块1展示的页签名称]代码块…

Redis——SpringBoot整合Redis实战

1、基本配置 1.1、引入依赖 首先&#xff0c;建立Maven项目&#xff0c;在Maven项目中引入pom.xml文件&#xff1a; <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> &l…

计算机设计大赛 深度学习 python opencv 火焰检测识别

文章目录 0 前言1 基于YOLO的火焰检测与识别2 课题背景3 卷积神经网络3.1 卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV54.1 网络架构图4.2 输入端4.3 基准网络4.4 Neck网络4.5 Head输出层 5 数据集准备5.1 数…