Clickhouse 引擎之MergeTree详解

news2024/12/26 21:38:51

分区详解

数据存储底层分布

# 数据在这个位置
root@fjj001:~# cd /var/lib/clickhouse/data
root@fjj001:/var/lib/clickhouse/data# ls
# 数据库
default  system
root@fjj001:/var/lib/clickhouse/data# cd default/
root@fjj001:/var/lib/clickhouse/data/default# ls
#表
enum  test_array test

root@fjj001:/var/lib/clickhouse/data/default# cd test
root@fjj001:/var/lib/clickhouse/data/default/test# ls
# 分区目录 443ada38955287b00e4adb6717a67dc5_3_3_0
# detached  通过DETACH语句卸载后的表分区存放位置
# format_version.txt 纯文本,记录存储的格式
443ada38955287b00e4adb6717a67dc5_3_3_0  549e808cec4345228ef62a54e16b445c_1_1_0  detached  fb922197bb861a044ab5b3f46a94d710_2_2_0  format_version.txt

root@fjj001:/var/lib/clickhouse/data/default/test# cd 443ada38955287b00e4adb6717a67dc5_3_3_0/
root@fjj001:/var/lib/clickhouse/data/default/test/443ada38955287b00e4adb6717a67dc5_3_3_0# ls

checksums.txt  columns.txt  count.txt  data.bin  data.mrk3  default_compression_codec.txt  minmax_birthday.idx  partition.dat  primary.idx
# columns.txt 纯文本文件,记录了表的结构信息
# count.txt 纯文本文件 记录表的数据行数
# checksums.txt 校验和
#  default_compression_codec.txt 默认压缩算法
# data.bin 二进制文件
# data.mrk3 标记文件
# skp_idx_[column].idx:跳数索引,在使用了二级索引时会生成,否则这不生成。

数据分区目录命名规则

比如20231115_2_2_0,其中 20231115是分区ID ,2_2对应的是最小的数据块编号和最大的数据块编号,最后的 _0 表示目前分区合并的层级。这么命名是为了数据目录合并算法。

分区ID:该值由 insert 数据时分区键的值来决定。分区键支持使用任何一个或者多个字段组合表达式,针对取值数据类型的不同,分区ID的生成逻辑目前有四种规则:

不指定分区键:如果建表时未指定分区键,则分区ID默认使用all,所有数据都被写入all分区中。
整型字段:如果分区键取值是整型字段,并且无法转换为YYYYMMDD的格式,则会按照该整型字段的字符形式输出,作为分区ID取值。
日期类型:如果分区键属于日期格式,或可以转换为YYYYMMDD格式的整型,则按照YYYYMMDD格式化后的字符形式输出,作为分区ID取值。
其他类型:如果使用其他类似Float、String等类型作为分区键,会通过对其插入数据的128位Hash值作为分区ID的取值。
MinBlockNum 和 MaxBlockNum: BlockNum 是一个整型的自增长型编号,该编号在单张MergeTree表中从1开始全局累加,当有新的分区目录创建后,该值就加1,对新的分区目录来讲,MinBlockNum 和 MaxBlockNum 取值相同。

Level: 表示合并的层级。相当于某个分区被合并的次数,它不是以表全局累加,而是以分区为单位,初始创建的分区,初始值为0,相同分区ID发生合并动作时,在相应分区内累计加1。

分区目录的合并过程

在这里插入图片描述
ergeTree的分区目录和传统意义上其他数据库有所不同。MergeTree的分区目录并不是在数据表被创建之后就存在的,而是在数据写入过程中被创建的。也就是说如果一张数据表没有任何数据,那么也不会有任何分区目录存在。 **MergeTree的分区目录伴随着每一批数据的写入(一次INSERT语句),MergeTree都会生成一批新的分区目录。即便不同批次写入的数据属于相同分区,也会生成不同的分区目录。**也就是说,对于同一个分区而言,也会存在多个分区目录的情况。在之后的某个时刻(写入后的10~15分钟,也可以手动执行optimize查询语句),ClickHouse会通过后台任务再将属于相同分区的多个目录合并成一个新的目录。已经存在的旧分区目录并不会立即被删除,而是在之后的某个时刻通过后台任务被删除(默认8分钟)。

但是有些特殊场景下,用户希望立刻删除老的数据目录,这种需求可以在创建MergeTree表的时候通过Settings属性来设置。如下所示

CREATE TABLE partition_directory_merge
(
    partition_key Int32,
    orderBy_key String,
    data String
)
ENGINE = MergeTree()
PARTITION BY partition_key
ORDER BY orderBy_key
SETTINGS old_parts_lifetime = 1
# old_parts_lifetime 这个属性的含义是多久删除老的分区数据目录。单位是秒

主键索引

概述

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

ClickHouse的主键索引和别的数据库是不一样的,他不是唯一的,可以使用相同的主键插入多行。

ClickHouse的主键索引使用了稀疏索引实现,即每一行索引表记录对应的是一段数据,而不是一行数据。它使用少量的索引标记就可以记录大量数据的区间位置信息。

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

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

索引粒度

ClickHouse通过index_granularity参数来控制索引粒度,默认为8192,最新版本可以使用自适应索引粒度大小,则标记文件会被命名为(column.mrk2)。数据会以该参数的大小被标记为多个小区间,每个区间默认最多8192行数据,MergeTree使用MarkRange来表示一个具体区间,并通过start和end表示具体范围。
在这里插入图片描述
ClickHouse中,索引粒度不仅影响主键索引(primary.idx),同时也影响数据标记文件(column.mrk)和数据文件(column.bin)。这是由于MergeTree无法只通过索引来完成查询工作,通过标记文件建立以稀疏索引(primary.idx)和对应数据文件(column.bin)的映射关系,MergeTree会先通过稀疏索引(primary.idx)找到对应数据的偏移量信息(column.mrk),再通过偏移量直接从数据文件(column.bin)读取数据。所以主键索引和数据标记的间隔粒度相同,均有index_granularity参数决定,数据文件也会依据该参数生成压缩数据块。

二级索引

1.除以及索引之外,MergeTree 还支持二级索引
2.二级索引又称为跳数索引,由数据的聚合信息构成
3.根据索引类型不用,内容不同
4.设计二级索引的目的与一级索引一样 为了在查询时减少数据的范围
5.二级索引需要在create 语句内定义 支持元祖喝表达式的声明形式
6.如果声明了二级索引,那么在会额外生成对应的索引文件(skp_idx[column].idx)跟标记文件(skp_idx_[colimn].mrk)

创建跳数索引

INDEX index_name expr TYPE type(...) GRANULARITY granularity_value

跳数索引

针对不同的场景,ClickHouse提供了不同类型的跳数索引。每种跳数索引都有一个 GRANULARITY 参数,表示每隔 GRANULARITY 个索引粒度 (index_granularity ) 才会生成一次跳数索引。

※ 注意区分 GRANULARITY 和 index_granularity 的不同。
(1)minmax
minmax顾名思义就是和分区目录下的 minmax_{column_name}.idx 文件类似,只不过不再是只有一个min/max值,例如上面的minmax跳数索引 a,表示每隔 3 * index_granularity 的区间就会记录一次 u64 * i32 和 s 的最大最小值。当我们通过s查询数据时,可以先基于minmax值判断,从而跳过大多数不需要扫描的索引粒度。
(2)set(max_rows)
保存指定表达式的去重值,尤其是对于那些重复性很高的列,例如性别、年龄段等,max_rows 参数表示在一个索引粒度内,最多记录不超过 max_rows 行,即不多于 max_rows 个去重值,max_rows=0 表示不限制。
(3)ngrambf_v1(n, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)
存储一个包含数据块中所有 n元短语(ngram) 的布隆过滤器(Bloom filter)。对String, FixedString 和 Map类型数据有效,可用于优化 EQUALS, LIKE 和 IN表达式。

什么是布隆过滤器呢?

本质上布隆过滤器是一种数据结构,一种比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你:某个数据一定不存在或者可能存在(注意:布隆过滤器是不能判断某条数据一定存在的,存在误报率 (false_positive) )。相比于传统的 List、Set、Map 等数据结构,布隆过滤器更高效、占用空间更少,因为不需要存储原始值,但是缺点是其返回的结果是概率性的,而不是确切的。
布隆过滤器会定义一个初始值全为0,长度为m的 bit 向量或者说 bit 数组:
然后再定义k个hash函数,使得原始数据可以映射到bit向量中(是不是和redis的bitmap很像,还记得那个怎么记录上亿用户在线状态的问题吗?)。例如,定义3个hash函数,

对于“python”得到3个hash值:1、5、6,就将bit向量中的1、5、6 bit位置1。

对于“java”得到3个hash值:2、3、9,就将bit向量中的2、3、9 bit位置1。

对于“rust”得到3个hash值:7、8、9,就将bit向量中的7、8、9 bit位置1。

可以发现,对于不同的数据,因为bit向量的长度是有限的,所以可能会出现重复的bit位被置1的情况,即不同数据之间有交集。当我们查数据的时候,例如:“go”,得到的三个hash值为:4、6、7,因为4号bit位是0,所以“go”一定不存在。如果查询数据“shell”得到的hash值是:2、5、9,虽然这个三个bit位都有数据,但是我们也不能确定“shell”一定存在。

布隆过滤器的好处是可以用较少的空间存储多条复杂数据的存在信息,但是不容易确定bit向量的长度m的值,因为m越小,bit向量就越容易被占满,越容易误报(false_positive越大),起不到过滤作用。hash函数的个数k也不容易确定,因为k越大,bit向量也越容易被占满,k越小,越容易误报,因为更容易撞车。而且,可以发现布隆过滤器是不支持删除的,因为被置1的bit位可能存储了多条数据信息,对于经常变动的数据是不合适的。

再来介绍一下ngrambf_v1的参数:

n:ngram短语长度。
size_of_bloom_filter_in_bytes:布隆过滤器的大小 m,以字节为单位(可以使用较大的值,例如256或512,因为它可以很好地被压缩,并且有更强的表达能力)。
number_of_hash_functions:布隆过滤器中使用的哈希函数的个数 k 。
random_seed:布隆过滤器哈希函数的种子。
(4)tokenbf_v1(size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)

跟 ngrambf_v1 类似,但是存储的是token而不是ngrams。Token是由非字母数字的符号分割的序列。不再需要ngram size参数,自动通过原始数据中的非字母数字字符切分原始数据。

(5)bloom_filter([false_positive])

为指定的列存储布隆过滤器,可用于优化 equals, notEquals, in, notIn, has, hasAny, hasAll 函数,false_positive 表示从过滤器接收到假阳性响应的概率(误报率),取值范围是 (0,1),默认值:0.025。支持数据类型:Int*, UInt*, Float*, Enum, Date, DateTime, String, FixedString, Array, LowCardinality, Nullable, UUID, Map.

对于Map类型数据,可以使用mapKeys或mapValues函数指定是否为键或值创建索引。如:

INDEX map_key_index mapKeys(map_column) TYPE bloom_filter GRANULARITY 1
INDEX map_key_index mapValues(map_column) TYPE bloom_filter GRANULARITY 1
假设已知布隆过滤器误报率 p,插入元素个数为n,则hash函数个数 k 和布隆过滤器bit向量长度 m 为:

跳数索引函数支持

在使用where条件查询的时候,如果where条件表达式中包含跳数索引列,ClickHouse会在执行函数时尝试使用索引。不同的函数对索引的支持是不同的,其中 set 索引对所有函数都生效,其他跳数索引支持如下:
请添加图片描述
※ 注意:参数小于ngram大小的常量函数不能被ngrambf_v1用于查询优化。例如,ngrambf_v1的参数n为5,即5元短语,则 where like ‘ab%’ 不能被优化。

因为布隆过滤器存在误报率,所以 ngrambf_v1, tokenbf_v1, 和 bloom_filter 不能用于优化结果逻辑为False的查询。例如,下面查询语句可以优化:

s LIKE '%test%'
NOT s NOT LIKE '%test%'
s = 1
NOT s != 1
startsWith(s, 'test')

下面的语句不能被优化:

NOT s LIKE '%test%'
s NOT LIKE '%test%'
NOT s = 1
s != 1
NOT startsWith(s, 'test')

因为布隆过滤器可以明确地判断该条语句是不存在,但是却不能明确地判断一定是存在的,所以对于返回存在like后数据的块还需要全部解压扫描。

所有跳数索引的原则都是“排除法”,即尽可能的排除那些一定不满足条件的索引粒度。在写查询where条件时也要基于这一原则考虑。

数据TTL

Time To Live,MergeTree提供了可以管理数据表或者列的生命周期的功能,TTL用于设置值的生命周期,它既可以为整张表设置,也可以为每个列字段单独设置。表级别的 TTL 还会指定数据在磁盘和卷上自动转移的逻辑。
要定义TTL,必须要使用时间间隔操作符,如下所示
TTL date_time + INTERVAL 5 SECONDS
TTL date_time + INTERVAL 15 HOUR
TTL date_time + INTERVAL 1 MONTH

要想在建表时使用TTL,则TTL 表达式的计算结果必须是日期或日期时间 类型的字段。
如 TTL time_column
TTL time_column + interval

列级别的TTL

1)建立带有TTL列的表,这里定义total_amount 这一列的数据30s后结束生命周期

create table t_order_mt3(
    id UInt32,
    sku_id String,
    total_amount Decimal(16,2)  TTL create_time+interval 10 SECOND,
    create_time  Datetime 
 ) engine =MergeTree
 partition by toYYYYMMDD(create_time)
   primary key (id)
   order by (id, sku_id);


2)插入数据

insert into  t_order_mt3 values
(106,'sku_001',1000.00,now()),
(107,'sku_002',2000.00,now()),
(110,'sku_003',600.00,now());

此时查询还有数据
在这里插入图片描述
执行手动合并分区

optimize table t_order_mt3 final

执行手动合并后再次查询可以看到此时的total_amount这列数据已经清空了.
在这里插入图片描述

表级别TTL

可以在MergeTree的表参数中增加TTL表达式 为整张表设置TTL。

CREATE TABLE stu
(
    id Int32 ,
    name String ,
    create_time DateTime
    
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(create_time)
ORDER BY id    TTL create_time + INTERVAL 30  SECOND DELETE ;  

这里可以看到与列级TTL相比,表级TTL只不过是把TTL直接定义在了表外,相对于对全局的一个TTL定义,最后的DELETE 可加可不加,默认到期了整行数据都会删除.

插入数据

insert into  stu values
(100,'tom',now()),
(101,'jack',now()),
(102,'mary',now()),
(103,'lili','2021-08-18 07:12:34');

此刻查询还有数据
在这里插入图片描述
强制合并
在这里插入图片描述
适用场景:
数仓建设需要考虑数据的生命周期问题,数据的生命周期包括数据最初的写入,存储,处理,查询,归档和销毁几个基本的阶段。
实际中数仓数据量的成倍增长,不但产生了巨大容量的存储,同时也造成管理的困难,更换存储方式和存储迁移对项目来讲都是需要考虑
成本和风险的。clickhouse这样的一个设计,可以有效处理解决数据有效的存储周期和销毁的问题。ck的出现对数据存储的
数仓的业务选型又添加一种选择。

概言之:
1.定期删除过期数据
2.定期移动过期数据进行归档

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

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

相关文章

ASEMI代理AD8400ARZ10-REEL原装ADI车规级AD8226ARZ-R7

编辑:ll ASEMI代理AD8400ARZ10-REEL原装ADI车规级AD8226ARZ-R7 型号:AD8400ARZ10-REEL 品牌:ADI/亚德诺 封装:SOIC-8 批号:2023 引脚数量:8 安装类型:表面贴装型 AD8400ARZ10-REEL汽车芯…

Zabbix监控系统——附详细步骤和图解

文章目录一、Zabbix概述1、使用zabbix的原因2、zabbix的概念和构成3、zabbix 监控原理:4、zabbix的程序组件二、安装 zabbix 5.01、部署 zabbix 服务端的操作步骤2、实例操作:部署 zabbix 服务端3、部署 zabbix 客户端4、实例操作:部署 zabbi…

【Linux】揭开套接字编程的神秘面纱(下)

​🌠 作者:阿亮joy. 🎆专栏:《学会Linux》 🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根 目录👉前言&…

(二十三)槽函数的书写规则导致槽函数触发2次的问题

在创建QT的信号和槽时,经常无意间保留着QT书写槽函数的习惯,或者在QT设计界面直接右键【转到槽】去创建槽函数,但是后期需要用到disconnect时,又重新写了一遍connect函数,那么你会发现实际槽函数执行了2遍。 首先来看…

要在Ubuntu中查找进程的PID,可以使用pgrep或pidof命令。

一 查找进程 1.pgrep命令 pgrep命令可以根据进程名或其他属性查找进程的PID。例如,要查找名为"firefox"的进程的PID,可以在终端中输入以下命令: pgrep firefox如果有多个名为"firefox"的进程,pgrep命令将返…

互联网一个赛道只剩下几家,真要爆品

互联网一个赛道剩下几家,真要爆品 2017年的书,案例基本上是马后炮总结 趣讲大白话:说起来容易,做起来难 【趣讲信息科技136期】 **************************** 书中讲的范冰冰翻车了 书中不看好的线下渠道,现在成香饽饽…

面试篇-Java并发之CAS:掌握原理、优缺点和应用场景分析,避免竞态问题

1、CAS介绍及原理 多线程中的CAS(Compare-and-Swap)操作是一种常见的并发控制方法,用于实现原子性更新共享变量的值。其核心思想是通过比较内存地址上的值和期望值是否相等来确定是否可以进行更新操作,从而避免多线程条件下的竞态…

HMI实时显示网络摄像机监控画面——以海康威视网络摄像机为例

随着IOT技术的快速发展,网络摄像机快速应用于工业领域,结合其他智能设备建立一个智能系统,提高用户与机器设备之间的交互体验,帮助企业优化人员配置。 作为重要的可视化设备,HMI不仅可以采集现场设备数据,…

uniapp系列-使用uniapp携带收件人信息调用手机邮件应用发邮件的2种方案

背景描述 我们使用uniapp打包之后,某些情况下,需要使用uniapp打开手机其他应用去发邮件,携带对方email 信息以及主题信息等,那我们应该怎么处理呢? 方案一:使用uniapp标签-uni-link,注意这种方…

BGP实验(一)

实验要求: 1、As1存在两个环回,一个地址为192.168.1.0/24,该地址不能在任何协议中宣告, As3存在两个环回,.一个地址为192.168.2.0/24,该地址不能在任何协议中宣告, As1还有一个环回地址为10.1.1.0/24&…

研读Rust圣经解析——Rust learn-8(match,if-let简洁控制流,包管理)

研读Rust圣经解析——Rust learn-8(match,if-let简洁控制流,包管理)matchother和占位符_区别easy matchenum matchno valuematch innerOption matchmore better wayif-let整洁控制包管理模块(mod)拆分声明modpub公开use展开引用拆解模块结构m…

docker cmd

sudo docker run --gpus all --name uavrl1 themvs/uav_swarm_reinforcement_learning sudo docker p s-a 86850d5a9dc3 sudo docker run --gpus all --name uavrl12 uavrl:v1.2 ---------- 共享屏幕输入类似指令,实测可行 sudo docker run -it --nethost --ipc…

Leetcode每日一题——“轮转数组”

各位CSDN的uu们你们好呀,今天,小雅兰的内容是轮转数组,下面,让我们进入轮转数组的世界吧 小雅兰之前其实就已经写过了字符串旋转的问题了: C语言刷题(7)(字符串旋转问题&#xff09…

优化 Kafka 的生产者和消费者

背景 如今,分布式架构已经成为事实上的架构模范,这使得通过 REST API 和 消息中间件来降低微服务之间的耦合变得必然。就消息中间件而言,Apache Kafka 已经普遍存在于如今的分布式系统中。Apache Kafka 是一个强大的、分布式的、备份的消息服…

HBase高手之路5—HBase的JavaAPI编程

文章目录Hbase高手之路5—Hbase的JavaAPI编程一、需求与数据集二、准备工作1.下载安装Java2.下载安装Idea3.下载安装maven4.Maven配置国内的镜像库5.Idea使用自定义的maven配置6.创建一个maven测试项目7.创建所需要的包8.创建类文件,输入代码9.运行项目三、创建HBas…

【2023 年第十三届 MathorCup 高校数学建模挑战赛】A 题 量子计算机在信用评分卡组合优化中的应用 详细建模过程解析及代码实现

更新信息:2023-4-15 更新了代码 【2023 年第十三届 MathorCup 高校数学建模挑战赛】A 题 量子计算机在信用评分卡组合优化中的应用 更新信息:2023-4-15 更新了代码 1 题目 在银行信用卡或相关的贷款等业务中,对客户授信之前,需…

Linux程序的内存

要研究程序的运行环境,首先要弄明白程序与内存的关系。程序与内存的关系,好比鱼和水一般密不可分。内存是承载程序运行的介质,也是程序进行各种运算和表达的场所。了解程序如何使用内存,对程序本身的理解,以及后续章节…

【CSS-Part3 样式显示模式、背景设置、三大特性 】

CSS-Part3 样式显示模式、背景设置、三大特性一 CSS元素显示模式:1.1块元素:1.2行内元素:1.3行内块元素:(同时具有行内元素和块元素的特点)元素显示模式总结:1.4元素显示模式转换:一种模式的元素需要另一模…

从Navicat 和 DBeaver中导出数据不要文本识别符号 “”

今天需要从MySQL和ClickHouse数据库中导出CSV数据文件,打开CSV数据文件后发现字段的数据带着""这种不需要的符号,研究了一下终于成功导出了不要文本识别符号“”的CSV文件 一、演示从DBeaver导出ClickHouse数据库的表文件 第一步&#xff0c…

SSH、OpenSSH、SSL、OpenSSL及CA

OpenSSL1. SSH、OpenSSH、SSL、OpenSSL关系及区别2. SSH介绍2.1 概念2.2 SSH的主要功能2.3 示例讲解2.4 ssh和sshd的区别3. OpenSSH介绍3.1 概念3.2 OpenSSH程序简介3.3 OpenSSH 包含的组件1. ssh2. scp3. sftp4. sshd5. ssh-keygen6. ssh-copy-id7. ssh-agent8. ssh-add9. ssh…