MergeTree原理之一级索引

news2025/1/11 22:54:37

一级索引

MergeTree的主键使用PRIMARY KEY定义,待主键定义之后,MergeTree会依据index_granularity间隔(默认8192行),为数据表生成一级索引并保存至primary.idx文件内,索引数据按照PRIMARY KEY排序。相比使用PRIMARY KEY定义,更为常见的简化形式是通过ORDER BY指代主键。在此种情形下,PRIMARY KEY与ORDER BY定义相同,所以索引(primary.idx)和数据(.bin)会按照完全相同的规则排序。

稀疏索引

primary.idx文件内的一级索引采用稀疏索引实现。稀疏索引和稠密索引的区别如图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3kCnyu8j-1672134635864)(/Users/lidongmeng/Library/Application Support/typora-user-images/image-20221216165533745.png)]

在稠密索引中每一行索引标记都会对应到一行具体的数据记录。而在稀疏索引中,每一行索引标记对应的是一段数据,而不是一行。用一个形象的例子来说明:如果把MergeTree比作一本书,那么稀疏索引就好比是这本书的一级章节目录。一级章节目录不会具体对应到每个字的位置,只会记录每个章节的起始页码。

稀疏索引的优势是显而易见的,它仅需使用少量的索引标记就能够记录大量数据的区间位置信息,且数据量越大优势越为明显。以默认的索引粒度(8192)为例,MergeTree只需要12208行索引标记就能为1亿行数据记录提供索引。由于稀疏索引占用空间小,所以primary.idx内的索引数据常驻内存,取用速度自然极快。

索引粒度

索引粒度(index_granularity)对MergeTree而言是一个非常重要的概念,它就如同标尺一般,会丈量整个数据的长度,并依照刻度对数据进行标注,最终将数据标记成多个间隔的小段:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nfK6NPdH-1672134635866)(/Users/lidongmeng/Library/Application Support/typora-user-images/image-20221216165308399.png)]

数据以index_granularity的粒度(默认8192)被标记成多个小的区间,其中每个区间最多8192行数据。MergeTree使用MarkRange表示一个具体的区间,并通过start和end表示其具体的范围。index_granularity的命名虽然取了索引二字,但它不单只作用于一级索引(.idx),同时也会影响数据标记(.mrk)和数据文件(.bin)。因为仅有一级索引自身是无法完成查询工作的,它需要借助数据标记才能定位数据,所以一级索引和数据标记的间隔粒度相同(同为index_granularity行),彼此对齐。而数据文件也会依照index_granularity的间隔粒度生成压缩数据块。

索引数据生成过程

假设我们定义表hits_v1如下,按照月份进行分区,按照CounterID设置主键,我们来看下索引数据是如何生成的。

CREATE TABLE hits_v1 (
    CounterID Int64,
    EventDate Date
) ENGINE = MergeTree()
PRIMARY KEY CounterID  -- 也可以不写,默认和排序键保持一致
ORDER BY CounterID
PARTITION BY toYYYYMM(EventDate)

由于是稀疏索引,所以需要间隔index_granularity行数据才会生成一条索引,索引值会根据声明的主键字段获取。hits_v1使用年月分区(PARTITION BYtoYYYYMM(EventDate)),所以2014年3月份的数据最终会被划分到同一个分区目录内。如果使用CounterID作为主键(ORDER BY CounterID),则每间隔8192行数据就会取一次CounterID的值作为索引值,索引数据最终会被写入primary.idx文件进行保存。例如第0(8192 * 0)行CounterID取值57,第8192(8192 * 1)行CounterID取值1635,而第16384(8192 * 2)行CounterID取值3266,最终索引数据将会是5716353266,可以看出MergeTree对于稀疏索引的存储是非常紧凑的,索引值前后相连,按照主键字段顺序紧密地排列在一起,如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AxtPiurV-1672134635866)(/Users/lidongmeng/Library/Application Support/typora-user-images/image-20221217164802950.png)]

如果使用多个主键,例如ORDER BY (CounterID, EventDate),则每间隔8192行可以同时取CounterID与EventDate两列的值作为索引值,具体如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YpjQwsJi-1672134635867)(/Users/lidongmeng/Library/Application Support/typora-user-images/image-20221217164819357.png)]

索引查询过程

经过上面的讲述,我们知道了一级索引的生成过程,那么我们在查询时候是如何配合一级索引来精确定位数据的呢?首先我们需要了解什么是 MarkRange,MarkRange 在 ClickHouse 中是用于定义标记区间的对象。MergeTree 按照 index_granularity 的间隔粒度,将一段完整的数据划分成了多个小的间隔数据段,一个具体的数据段就是一个 MarkRange,并与索引编号对应,使用start 和 end 两个属性表示其范围。通过 start 和 end 对应的索引编号的取值,即可得到它所对应的数值区间,而数值区别表示了此 MarkRange 的数据范围。

假如现在有一份测试数据,共192行记录。其中,主键ID为String类型,ID的取值从A000开始,后面依次为A001、A002……直至A192为止,假设MergeTree的索引粒度index_granularity=3,那么我们生成的索引文件如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RyAhfATF-1672134635867)(/Users/lidongmeng/Library/Application Support/typora-user-images/image-20221226145700376.png)]

根据索引数据,MergeTree会将此数据片段划分成192/3=64个小的MarkRange,两个相邻MarkRange相距的步长为1。其中,所有MarkRange(整个数据片段)的最大数值区间为[A000,+inf),其完整的示意如图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cCxenVSa-1672134635868)(/Users/lidongmeng/Library/Application Support/typora-user-images/image-20221226145757782.png)]

索引查询其实就是两个区间的交集判断。其中一个区间是由基于主键的查询条件转换而来的条件区间;另一个区间就是上面说的与 MarkRange 对应的数值区间。我们来看下整个查询过程:

1. 生成查询区间:首先将查询条件转换为区间,即使是单个值也会转换为区间的形式,举个栗子:

WHERE ID = 'A003' -> ['A003', 'A003']
WHERE ID > 'A012' -> ('A012', +inf]
WHERE ID < 'A185' -> [-inf, 'A185')
WHERE ID LIKE 'A006%' -> ['A006', 'A007')

2. 递归交集判断:以递归的形式,依次对 MarkRange 的数值区间与条件区间做交集判断,从最大的区间 [A000, +inf) 开始:

  • 如果不存在交集,则直接通过剪枝算法优化此整段 MarkRange;
  • 如果存在交集,且 MarkRange 步长大于等于 8(end - start),则将此区间进一步拆分成 8 个子区间(由 merge_tree_coarse_index_granularity 指定,默认值为 8),然后重复此过程,继续做递归交集判断;
  • 如果存在交集,且 MarkRange 不可再分解(步长小于 8),则记录 MarkRange 并返回。

3)合并 MarkRange 区间:将最终匹配的 MarkRange 聚在一起,合并它们的范围。

我们通过下面这张图,来展示一下上面的几个步骤,以上面的测试数据为例,查询条件为 ID = ‘A003’,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-595MdN6w-1672134635868)(/Users/lidongmeng/Library/Application Support/typora-user-images/image-20221226150629332.png)]

MergeTree通过递归的形式持续向下拆分区间,最终将 MarkRange 定位到最细的粒度,以便在后续读取数据的时候,能够最小化数据的扫描范围。以上图为例,当查询条件为 ID = ‘A003’ 的时候,最终只需要读取 [A000, A003] 和 [A003, A006] 两个区间的数据,它们对应 MarkRange(start:0, end:2) 范围,而其它无用区间都被裁剪掉了。由于 MarkRange 转换的数值区间是闭区间,所以会额外匹配到临近的一个区间。

一级索引设计

在建表时,我们需要指定【排序字段】,这个就是主索引(建表sql中的order by部分)。索引的本质是通过排序的方式,跳过无用的数据扫描,加速查询。所有查询条件中的【where】都是数据过滤部分,该部分的条件,Clickhouse内部会尝试提高到索引过滤。我们的索引都是有序的,并且是前缀匹配的,比如索引是(A,B,C),那么查询(A), (A,B), (A, B, C),均可能会有不错的过滤效果,而查询(B), ©, (B, C)等,基本没有什么过滤效果。一般来说,我们可以按照下面方式进行排序键选取:

  1. 列出该表常用的 SELECT 语句。
  2. 对 WHERE 条件后的列,按使用比重选取出 [1,5] 个作为备选。
  3. 根据备选列的基数从小到大排序,得出最终排序键的顺序。
  4. 排序键数量保持在 [1,5] 个。不设置和超出数量都不好。

对于多个排序字段的顺序,可以遵循两个原则:

  • WHERE 子句中出现频次高的字段放到频率低字段的前面,增加查询命中索引的概率;
  • 维度基数大的字段放到维度基数小字段的后面,降低查询扫描范围。

参考

  1. https://mp.weixin.qq.com/s/Aa7BbutLoCK1Yn5vC5EuEA/
  2. https://mp.weixin.qq.com/s/VTTYMdY5A2SZNQdkZoXuhw
  3. https://clickhouse.com/docs/zh/guides/improving-query-performance/sparse-primary-indexes/
  4. https://www.cnblogs.com/MrYang-11-GetKnow/p/16017995.html
  5. Clickhouse原理解析与应用实践 朱凯

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

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

相关文章

【PotPlayer】采集Switch图像及录制

【PotPlayer】采集Switch图像及录制下载potplayer使用方法连接设备录制视频无边框设置阳&#xff0c;休&#xff0c;懂&#xff1f;QAQ。阳之前买了个Switch&#xff0c;正好有好玩的想录下来&#xff0c;然后就…自行某宝&#xff0c;某东去买个采集卡。本文只管连软件&#x…

【运维有小邓】ADSelfService Plus身份管理

一、身份管理挑战&#xff1a; 由于企业需要越来越高的安全性&#xff0c;以保护用户帐户免遭入侵者的任何恶意攻击&#xff0c;因此身份管理正日益变得重要。在所有密码相关的身份挑战中&#xff0c;帮助台工作单量成为重中之重&#xff0c;它们在组织的年同比财务预算中不堪…

(二)ElasticSearch使用

一、ES的基本使用 1.创建索引 创建一个test索引http://localhost:9200/test 2.删除索引 http://localhost:9200/test 3.查看索引 http://localhost:9200/_all 4.向索引中新增数据 http://localhost:9200/person/_doc/ 5.搜索数据 http://localhost:9200/person/_doc/_sear…

全志Tina Linux SPINAND UBI 离线烧录 开发指南 支持百问网T113 D1-H哪吒 DongshanPI-D1s V853-Pro等开发板

1 概述 编写目的&#xff1a; 介绍Sunxi SPINand 烧写时的数据布局 2 名词解释 词义UBIunsorted block imagePEBphysical erase blockLEBlogical erase block PEB 和logical block 关系 1 PEB 1 logical block 1 logical block 2 physical blocks3 总体数据布局 ubi 方案…

必读,一文普及MES系统知识

MES系统的基本概念制造执行系统&#xff08;MES&#xff09;是一套面向制造企业的信息管理系统。MES系统可以为企业提供管理模块&#xff0c;包括制造数据管理、计划与调度管理、生产调度管理、库存管理、质量管理、生产过程控制、底层数据集成分析、上层数据集成与分解&#x…

LVGL学习笔记5 - Display, Screen和Layer

目录 1. Display 2. Screen 2.1 创建Screen 2.2 加载Screen 2.3 获取活动的Screen 2.4 实例 2.4.1 定义2个Screen全局变量 2.4.2 初始化Screen 2.4.3 循环更替 3. Layer图层 3.1 切换顺序 3.2 顶层和系统层 3.3 实例 3.3.1 创建全局变量 3.3.2 初始化 3.3.3 …

【OpenFOAM】-olaFlow-算例5- oppositeSolitariesFlume

算例路径&#xff1a; olaFlow\tutorials\oppositeSolitariesFlume 算例描述&#xff1a; 两列反向的孤立波相互作用 学习目标&#xff1a; 熟练掌握olaFlow的造波设置&#xff0c;波浪方向与消波方向设置 算例快照&#xff1a; 图1 两列反向孤立波相互作用文件结构&#xff1…

Linux Kernel 远程代码执行漏洞(CVE-2022-47939)

Linux Kernel 远程代码执行漏洞&#xff08;CVE-2022-47939&#xff09; CVE-2022-47939 据Security Affairs消息&#xff0c;近期披露的一个严重 Linux 内核漏洞会影响 SMB 服务器&#xff0c;可能导致远程代码执行。 Linux Kernel SMB2_TREE_DISCONNECT 命令处理中存在远程…

C进阶:征服指针之指针与数组强化笔试题练习(2)

&#x1f432;&#x1f996; 本篇文章是接上篇文章的&#xff0c;上篇文章链接&#xff1a;http://t.csdn.cn/RogqL 目录 &#x1f407;&#x1f431;一.关于 strlen 函数与数组、指针的综合笔试题 &#x1f638;T1. &#x1f406; T2. &#x1f405;T3. &#x1f432;一.关…

网络和通信安全中的SSL/TLS国密改造

2021年3月&#xff0c;国家市场监管总局、国家标准化管理委员会发布公告&#xff0c;国家密码应用与安全性评估的关键标准GB/T39786—2021《信息安全技术信息系统密码应用基本要求》&#xff08;以下简称“GB/T39786”&#xff09;正式发布&#xff0c;GB/T 39786是贯彻落实《中…

CSS浮动与CSS定位装饰 Day3

结构伪类选择器 结构伪类 公式 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>伪类</title><style>li:nth-child(4n){background-color: red;}</style> </head> <body>…

【记录贴】项目经理的进阶日常:靠年终总结获得了核心项目的机会

进入项目经理这个岗位已经三年了&#xff0c;之前决定转行做项目经理是因为它涉及的知识面广&#xff0c;对个人的成长非常有帮助&#xff1b;也期望未来能积累一些大型且复杂的项目经验、获得更好的升职空间。但现在做了这么久&#xff0c;好像遇到了职业瓶颈&#xff0c;仿佛…

ESP8266 WIFI模块的使用

ESP8266 wiFI 可以用作连接周边的无线设备&#xff0c;也可以作为发送器供其他设备连接通常在产品中&#xff0c;我们经常用作无线的接收使用&#xff0c;也可能会用作在线升级使用等。 说点题外话&#xff1a;虽然在线升级已经较为成熟&#xff0c;但我不推荐在一些重要的产品…

Shell程序编写猜数字的小游戏

文章目录 目录 文章目录 前言 一、设计思路 二、代码编写 三、效果图 总结 前言 在学习Linux课程中学习了一点简单的shell语法&#xff0c;实现了一个猜数字功能的程序。感兴趣的可以看完后自己手动编写玩玩~这个小游戏的编写也是把基础的shell语法基本上都用到了&#…

在QQ音乐巅峰榜年度榜单中,听懂国人2022年的音乐记忆!

拐左为夏&#xff0c;拐右为冬&#xff0c;时至年末&#xff0c;各类形式的年终报告层出不穷。2022年&#xff0c;是特殊的一年&#xff0c;音乐作为不可或缺的一部分&#xff0c;也陪伴广大用户走过了日日夜夜。一首首歌曲拨动着心弦、同时也承载着回忆&#xff0c;熟悉的旋律…

【软件测试】不好,事搞大了,APP测试的血泪教训总结......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 听说&#xff0c;人…

C语言链表-记录学生信息

题目要求&#xff1a; 创建一个 单向链表 来记录学生信息&#xff0c;人数3–5人&#xff1b;链表结点为结构变量&#xff0c;结构的要求如下&#xff1a; struct stu_info{char stu_num[10]; //学号char stu_name[8]; //姓名char stu_sex[2]; //性别int stu_score /…

Flowable学习笔记(二):flowable实战

1.定义流程模板 (1)Call Activity&#xff08;调用活动&#xff09; 在这个流程图中&#xff0c;定义了一个开始节点、调用活动节点和结束节点&#xff08;bpmn.xml文件在文章最后附上&#xff09;。 开始节点&#xff1a;定义了一个执行监听器&#xff08;commonStartExecut…

【MyBatis】进一步理解choose、when、otherwise标签

choose、when、otherwise choose里面包含when、otherwise两个标签&#xff0c;choose是父标签&#xff0c;when和otherwise必须都要写在它里面 当 when 中有条件满足的时候&#xff0c;就会跳出 choose&#xff0c;即所有的 when 和 otherwise 条件中&#xff0c;只有一个会输…

半导体设备中制冷系统的压力和温度准确控制解决方案

摘要&#xff1a;针对半导体低温工艺中制冷系统在高压防护和温度控制中存在的问题&#xff0c;本文将提出一种更简便有效的解决方案。解决方案的核心是在晶片托盘上并联一个流量可调旁路&#xff0c;使制冷剂在流入晶片托盘之前进行部分短路。即通过旁路流量的变化调节流出晶片…