MergeTree原理之存储

news2024/10/7 18:31:31

我们都知道在MergeTree中数据是按列存储的,但是具体到存储的细节、以及如何工作的,都存在很多疑问。数据存储,就好比一本书中的文字,在排版时,绝不会密密麻麻地把文字堆满,这样会导致难以阅读。更为优雅的做法是,将文字按段落的形式精心组织,使其错落有致。本节将介绍MergeTree在数据存储方面的细节,尤其是其中关于压缩数据块的概念。

列式存储

对于 OLAP 技术来说,一般都是这对大量行少量列做聚合分析,所以列式存储技术基本可以说是 OLAP 必用的技术方案。列式存储相比于行式存储,列式存储在分析场景下有着许多优良的特性。

  • 分析场景中往往需要读大量行但是少数几个列。在行存模式下,数据按行连续存储,所有列的数据都存储在一个block中,不参与计算的列在IO时也 要全部读出,读取操作被严重放大。而列存模式下,只需要读取参与计算的列即可,极大的减低了IO cost,加速了查询;
  • 同一列中的数据属于同一类型,压缩效果显著,压缩比高。列存往往有着高达十倍甚至更高的压缩比,节省了大量的存储空间,降低了存储成本;
  • 更高的压缩比意味着更小的data size,从磁盘中读取相应数据耗时更短;
  • 自由的压缩算法选择。不同列的数据具有不同的数据类型,适用的压缩算法也就不尽相同。可以针对不同列类型,选择最合适的压缩算法;
  • 高压缩比,意味着同等大小的内存能够存放更多数据,系统cache效果更好。

在MergeTree中,数据按列存储,而具体到每个列字段,数据也是独立存储的,每个列字段都拥有一个与之对应的.bin数据文件。也正是这些.bin文件,最终承载着数据的物理存储。数据文件以分区目录的形式被组织存放,所以在.bin文件中只会保存当前分区片段内的这一部分数据。而对应到存储的具体实现方面,MergeTree也并不是一股脑地将数据直接写入.bin文件,而是经过了一番精心设计:首先,数据是经过压缩的,目前支持LZ4、ZSTD、Multiple和Delta几种算法,默认使用LZ4算法;其次,数据会事先依照ORDER BY的声明排序;最后,数据是以压缩数据块的形式被组织并写入.bin文件中的。

存储块

一个压缩数据块由头信息和压缩数据两部分组成,头信息固定使用 9 位字节表示,具体由 1 个 UInt8(1 字节)和 2 个 UInt32(4 字节)组成,分别代表使用的压缩算法类型、压缩后的数据大小、压缩前的数据大小。所以虽然存储的是压缩后的数据,但是在头信息中将压缩前的数据大小也记录了下来,如下图所示:

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

这是我们就有疑问了,压缩数据块的大小是怎么计算出来的呢?首先压缩数据块的最终大小是和索引粒度(index_granularity)相关的,MergeTree 在具体的数据写入过程中,会依照索引粒度按批次获取数据并写入(由于索引粒度默认是 8192,所以每批次会获取 8192 行)。如果把一批未压缩的数据的大小设为 size,则整个数据的写入过程遵循如下规则:

  1. 单个批次数据 size < 64KB:如果单个批次数据小于 64KB,则继续获取下一批数据,直至累积到size >= 64KB时,生成下一个压缩数据块。如果平均每条 记录小于8byte,多个数据批次压缩成一个数据块;
  2. 单个批次数据 64KB <= size <=1MB:如果单个批次数据大小恰好在 64KB 与 1MB 之间,则直接生成下一个压缩数据块;
  3. 单个批次数据 size > 1MB:如果单个批次数据直接超过 1MB,则首先按照 1MB 大小截断并生成下一个压缩数据块。剩余数据继续依照上述规则执行。此时, 会出现一个批次数据生成多个压缩数据块的情况。如果平均每条记录的大小超过 128byte,则会把当前这一个批次的数据压缩成多个数据块。

也就是说MergeTree在获取数据的时候会依照索引粒度按批次获取数据,所以默认情况下就是每批获取 8192 行,然后一批一批获取,而如果设当前批次的数据大小为 size,那么会根据 size 的不同,走上面三个分支中的一个,整个过程逻辑如图所示:

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

数据物理存储

数据文件(.bin)

经过上面分析,我们可以得出来一个column.bin数据文件是由1至多个压缩数据块组成的,每个压缩块大小在64KB~1MB之间,多个压缩数据块之间,按照写入顺序首尾相接,紧密地排列在一起。

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

.bin文件中引入压缩数据块的目的至少有以下两个:

其一,虽然数据被压缩后能够有效减少数据大小,降低存储空间并加速数据传输效率,但数据的压缩和解压动作,其本身也会带来额外的性能损耗。所以需要控制被压缩数据的大小,以求在性能损耗和压缩率之间寻求一种平衡;

其二,在具体读取某一列数据时(.bin文件),首先需要将压缩数据加载到内存并解压,这样才能进行后续的数据处理。通过压缩数据块,可以在不读取整个.bin文件的情况下将读取粒度降低到压缩数据块级别,从而进一步缩小数据读取的范围。

数据标记(.mrk)

通过前面的分析,我们了解了分区,一级索引,二级索引,知道了数据文件是由多个压缩块组成的,那么如何根据索引定位查询的数据在哪个压缩块以及在压缩块中的数据精确位置,这时候我们就需要数据标记文件。数据标记是衔接一级索引和数据的桥梁,像极了书签,而且书本中每一个章节目录都有各自的书签,它记录了与一级章节目录对应的页码信息,以及一段文字在某一页中的起始位置信息。

数据标记生成规则

数据标记是衔接一级索引和数据的桥梁,它们之间的关系如图所示:

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

从图中可以看到,数据标记的生成满足如下规则:

  1. 数据标记和索引区间是对齐的,均按照index_granularity的粒度间隔,如此一来,只需简单通过索引区间的下标编号就可以直接找到对应的数据标记;
  2. 为了能够与数据衔接,数据标记文件也与.bin文件一一对应,即每一个列字段[Column].bin文件都有一个与之对应的[Column].mrk数据标记文件,用于记录数据在.bin文件中的偏移量信息。

一行标记数据使用一个元组表示,元组内包含两个整型数值的偏移量信息。它们分别表示在此段数据区间内,在对应的.bin压缩文件中,压缩数据块的起始偏移量;以及将该数据压缩块解压后,其未压缩数据的起始偏移量,下图是.mrk文件内标记数据示意:

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

数据标记生成实例

假设我们有表hits_v1,Age字段是UInt8,占用一个字节,我们来用该字段进行简单分析一批数据写入后的.bin和.mrk文件内容。

CREATE TABLE hits_v1 (
    Age UInt8,
    ID String,
    EventDate Date
) ENGINE = MergeTree()
ORDER BY Age
PARTITION BY toYYYYMM(EventDate)

我们先回顾下存储块的生成规则,

  1. 单个批次数据 size < 64KB:如果单个批次数据小于 64KB,则继续获取下一批数据,直至累积到size >= 64KB时,生成下一个压缩数据块。如果平均每条 记录小于8byte,多个数据批次压缩成一个数据块;
  2. 单个批次数据 64KB <= size <=1MB:如果单个批次数据大小恰好在 64KB 与 1MB 之间,则直接生成下一个压缩数据块;
  3. 单个批次数据 size > 1MB:如果单个批次数据直接超过 1MB,则首先按照 1MB 大小截断并生成下一个压缩数据块。剩余数据继续依照上述规则执行。此时, 会出现一个批次数据生成多个压缩数据块的情况。如果平均每条记录的大小超过 128byte,则会把当前这一个批次的数据压缩成多个数据块。

假设我们每次按批读取8192行,因为一个UInt 8一字节,所以每次读取 8192 个字节,在读取8批之后(符合生成规则1)会进行压缩得到一个压缩数据块。生成的第一个压缩数据块为 276 个字节(压缩后大小),然后未压缩数据还剩下 34464 字节,小于 64KB,于是直接生成 155 字节的第二个压缩数据块。因此在 Age.bin 中两个偏移量的对应关系如下:

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

每一行标记数据都标记了一个片段的数据(默认 8192 行)在 .bin 压缩文件中的读取位置信息,因为 Age 占 1 字节,所以每次读取 8912 行相当于每次读取 8192 个字节,因此 “未压缩数据的起始偏移量” 就是 0、8192、16384、24576、…。但是需要注意图中的 57344,它表示第 8 批未压缩数据的起始偏移量,因为此时已经达到了 64KB,所以会生成一个压缩数据块,于是接下来读取第 9 批未压缩数据的时候就会对应新的压缩数据块,因此起始偏移量会重置为0,而不是 65536。我们这里是 Age 字段为例,至于其它列也是同理,然后是 “压缩数据块的起始偏移量”,因为读了 8 批才生成了第一个压缩数据块,因此前 8 行都是 0。然后由于第一个压缩数据块的大小是 276,因此第 9 行、即索引为 8 的位置,存储的值就是 276,表示第二个压缩数据块的起始偏移量。

以上就是标记文件的存储原理,但是标记文件和一级索引不同,它不能常驻内存,而是使用 LRU(最近最少使用)缓存淘汰策略加快其取用速度。

数据标记与压缩块关系

由于压缩数据块的划分,与一个间隔(index_granularity)内的数据大小相关,每个压缩数据块的体积都被严格控制在64KB~1MB。而一个间隔(index_granularity)的数据,又只会产生一行数据标记。那么根据一个间隔内数据的实际字节大小,数据标记和压缩数据块之间会产生三种不同的对应关系。

  1. 多对一: 多个数据标记对应一个压缩数据块,当一个间隔(index_granularity)内的数据未压缩大小size小于64KB时,会出现这种对应关系;
  2. 一对一: 一个数据标记对应一个压缩数据块,当一个间隔(index_granularity)内的数据未压缩大小size大于等于64KB且小于等于1MB时,会出现这种对应关系;
  3. 一对多: 一个数据标记对应多个压缩数据块,当一个间隔(index_granularity)内的数据未压缩大小size直接大于1MB时,会出现这种对应关系。

数据标记工作方式

MergeTree在读取数据时,必须通过标记数据的位置信息才能够找到所需要的数据。整个查找过程大致可以分为读取压缩数据块和读取数据两个步骤,我们使用上面实例中的数据进行解释:

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

  1. 读取压缩数据块:在查询某一列数据时,MergeTree无须一次性加载整个.bin文件,而是可以根据需要,只加载特定的压缩数据块,而这项特性需要借助标记文件中所保存的压缩文件中的偏移量。从图中可以看到,上下相邻的两个压缩数据块的起始偏移量,构成了与当前标记对应的压缩数据块的偏移量区间,说人话就是通过第 n 个压缩数据块的起始偏移量和第 n + 1 个压缩数据块的起始偏移量,可以获取第 n 个压缩数据块。具体做法就是从当前偏移量开始向下寻找(当前块的起始位置 start),直到找到不同的偏移量位置(当前块的下一个块的起始位置 next_start),此时 start 到 next_start 便是当前块对应的偏移量区间,比如图中的 0 到 276。通过偏移量区间,即可获得当前的压缩块;

  2. 读取数据:在读取解压后的数据时,MergeTree并不需要一次性扫描整段解压数据,它可以根据需要,以index_granularity的粒度加载特定的一小段。为了实现这项特性,需要借助标记文件中保存的解压数据块中的偏移量。通过偏移量,ClickHouse 可以按需读取数据,比如通过 [0, 8192] 即可读取压缩数据块 0 中第一个数据片段对应的解压数据。

我们最后梳理一下整体的定位数据的思路,先通过索引查找到相应的MarRange,然后根据索引和数据标记之间的对应关系,找到相应的数据标记对应的数据压缩块,然后借助数据标记中的数据偏移量定位到具体压缩块的某段数据,从而实现精确地定位数据的效果。

参考

  1. https://mp.weixin.qq.com/s/VTTYMdY5A2SZNQdkZoXuhw
  2. https://www.cnblogs.com/traditional/p/15218743.html
  3. Clickhouse原理解析与应用实践 朱凯

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

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

相关文章

Git学习:各阶段回退方法

文章目录一、问题背景二、解决方式1. 回退情况1&#xff1a;文件在工作区进行回退2. 回退情况2&#xff1a;文件在暂存区进行回退3. 回退情况3&#xff1a;文件在本地仓库进行回退4. 回退情况4&#xff1a;文件已经在远程仓库中一、问题背景 Git 是分布式的软件管理系统。在把…

代码随想录拓展day4 205. 同构字符串;1002. 查找常用字符;925.长按键入;844.比较含退格的字符串

代码随想录拓展day4 205. 同构字符串&#xff1b;1002. 查找常用字符&#xff1b;925.长按键入&#xff1b;844.比较含退格的字符串 哈希表和字符串的一些应用&#xff0c;放到一起了。 同构字符串 https://leetcode.cn/problems/isomorphic-strings/description/ 刚开始以…

spring boot:集成支付宝(沙箱环境)(spring boot 2.4.0 / wap/h5方式 )

一&#xff0c;配置支付宝沙箱环境: 1,沙箱的地址: 登录 - 支付宝 也可以登录后&#xff0c;从控制台点击 研发服务 进入 2&#xff0c;下载开发助手:并生成密钥 从这个页面&#xff0c;按自己所在的平台下载&#xff0c; 当前支持 windows,macos 开发助手简介 &#xf…

自定义Tango Control设备服务在Ubuntu中的测试

文章目录环境create a device classcompile the device classregister the devicestart the deviceexplore the device故障问题解决参考继续上一篇&#xff1a;https://blog.csdn.net/woshigaowei5146/article/details/128443892?spm1001.2014.3001.5501 环境 虚拟机&#xf…

布隆过滤器 以及缓存穿透问题

1. 前言 今天从客观的角度来分析下&#xff0c;什么是缓存穿透&#xff0c;什么是布隆过滤器&#xff0c;布隆过滤器是如何解决缓存穿透的。 2. 适合人群 对布隆过滤器不熟悉的人对Redis 缓存穿透不熟悉的人 3. 开始 3.1 什么是缓存穿透 其实通过上图我们可以知道&#xff0…

(七)Filter

Filter 表示过滤器&#xff0c;是JavaWeb 三大组件(Servlet、Filter、Listener)之一。过滤器可以把对资源的请求拦截下来&#xff0c;从而实现一些特殊的功能。 过滤器一般完成一些通用的操作&#xff0c;比如&#xff1a;权限控制、统一编码处理、敏感字符处理等等..一、Filte…

彻底理解Redis持久化

前言 大家都知道Redis一个内存数据库,它支持2种持久化方式&#xff1a;RDB(Snapshot 内存快照) &#xff0c;AOF(append only file)。持久化功能将内存中的数据同步到磁盘来避免Redis发生异常导致数据丢失的情况。当Redis实例重启时&#xff0c;即可利用之前持久化的文件实现数…

软件项目管理教程

软件项目管理 1. 软件项目管理 1.1 概述 概念 项目是为了创造一个唯一的产品或提供一个唯一的服务而进行的临时性的努力 软件项目特征 目标性 相关性 周期性 独特性 没有完全一样的项目”&#xff0c;项目的这种独特性对实际项目管理有非常重要的指导意义&#xff0c;因此…

跨境电商日本市场再创纪录,亚马逊失去流量第一位置

从跨境电商服务行业市场分析人员拿到的近期的各平台的销售数据和相关工作总结来看&#xff0c;美国市场微热&#xff0c;而反观欧洲市场则有些遇冷。 此外&#xff0c;近几年的疫情及各国相关政策并未影响到跨境电商市场的整体大好趋势&#xff0c;而伴随着疫情逐渐被控制&…

认真学习MySQL中的MVCC机制

什么是MVCC&#xff1f;MVCC&#xff08;Multiversion Concurrency Control&#xff09;&#xff0c;多版本并发控制。顾名思义&#xff0c;MVCC是通过数据行的多个版本管理来实现数据库的并发控制。这项技术使得在InnoDB的事务隔离级别下执行一致性操作有了保证。换言之&#…

Aspose.PDF for Java系列1-使用前说明

一、关于pdf文件说明 什么是pdf文件&#xff1f; PDF是Portable Document Format的缩写&#xff0c;用来以电子形式显示文档&#xff0c;不受软件、硬件或者操作系统的影响。 最初是由Adobe开发&#xff0c;是一种基于PostScript格式的通用兼容文件格式&#xff0c;现在已成为…

当云原生网关遇上图数据库,NebulaGraph 的 APISIX 最佳实践

本文介绍了利用开源 API 网关 APISIX 加速 NebulaGraph 多个场景的落地最佳实践&#xff1a;负载均衡、暴露接口结构与 TLS Termination。 API 网关介绍 什么是 API 网关 API 网关是位于客户端和服务器之间的“中间人”&#xff0c;用于管理、监控和保护 API。它可以在 API 之…

GAMES101 现代计算机图形学入门

Lecture1 Overview of CG 计算机图形学概述Lecture2 Review of Linear Algebra 向量与线性代数Lecture3 2D Transformation 基础变换&#xff08;二维&#xff09;3.1 线性变换 (寻找变换前后直角坐标坐标关系)3.2齐次坐标 Homogeneous Coordinate3.3 仿射变换Affine Transform…

SpringBoot2核心技术(核心功能)- 05、Web开发【5.4 数据响应与内容协商】

4、数据响应与内容协商 1、响应JSON 1.1、jackson.jarResponseBody <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency> web场景自动引入了json场景<dependenc…

在黑金zynqmp 上用emmc 启动的那些坑

缘由&#xff1a;由于硬件设计可能存在问题&#xff0c;导致sd 开启动失败&#xff0c;高速的时候&#xff0c;在建立VFS 时就会引起kernel 崩溃&#xff0c;官方的板子就正常&#xff0c;自己的板子第一版硬件可以&#xff0c;第二版就不行 思路&#xff1a;从emmc 启动 替代…

Hadoop 的基础知识

Hadoop 的基础知识1. Hadoop 简介2. Hadoop 的发展简史3. Hadoop 现状4. Hadoop 特性优点5. Hadoop 发行版本6. Hadoop 架构变迁7. Hadoop 集群集体概念1. Hadoop 简介 Hadoop 官网: https://hadoop.apache.org/ Apache Hadoop 软件库是一个框架, 是 Apache 软件基金会的一款开…

IoT 物联网将如何影响 SCADA ?

IoT 物联网将如何影响 SCADA &#xff1f;-钡铼技术 随着工业物联网&#xff08;IIOT&#xff09;或工业4.0的提出&#xff0c;未来工业自动化将是大势所趋&#xff0c;机器设备运行自动化&#xff0c;人不断被机器替代。那么&#xff0c;这些发展方向会对SCADA有什么影响&…

【DevOps:一、开始】

名词安全组类似于防火墙的设置&#xff0c;打开安全组&#xff0c;但要防止被当成矿机&#xff0c;需要设置主机的容器密码VPC专有网络&#xff0c;私有网络&#xff1a;VPC虽然网段相同&#xff1a;但资源物理层隔离&#xff0c;不能使用内网相互通Ipv4网段子网计数器容器平台…

产品设计表现技能的学习要点

在产品设计过程中&#xff0c;当你心中有创意设计时&#xff0c;你需要写下创意设计&#xff0c;并生成一个例子来总结你以前的想法。此时&#xff0c;您需要设计性能。在设计性能的过程中&#xff0c;我们需要使用各种设计工具&#xff0c;这些设计工具的应用技术和方法通常称…

玩以太坊链上项目的必备技能(内联汇编 [inline assembly]-Solidity之旅十八)

概要 大抵是讲到汇编&#xff0c;身为编程开发者的我们脑瓜子早就嗡嗡作响了。看那晦涩难懂的低级汇编代码&#xff0c;敢断言&#xff0c;那一行不是我写的&#xff0c;其他行也不是我写的。 自从C语言问世&#xff0c;而后类C语言犹如雨后春笋般地搅动着IT界&#xff0c;而…