【ClickHouse内核】对于分区、索引、标记和压缩数据的协同工作

目录

概述

写入过程

生成分区目录

生成索引

生成标记和数据压缩文件

各个底层物理文件生成的过程

查询过程

借助索引文件降低扫描范围

借助标记文件降低解压数据的大小

数据标记与压缩数据块的对应关系

多个数据标记对应一个压缩数据块

一个数据标记对应一个压缩数据块

一个数据标记对应多个压缩数据块

结论


概述

分区、索引、标记和压缩数据,这些组件配合在一起给ClickHouse数据库带来非常高效的查询性能。前面的文章也单独介绍了这几个组件。接下来,就分别从写入过程、查询过程,以及数据标记与压缩数据块的三种对应关系的角度展开介绍。

 

写入过程

生成分区目录

数据写入的第一步是生成分区目录,伴随着每一批数据的写入,都会生成一个新的分区目录。在后续的某一时刻,属于相同分区的目录会依照规则合并到一起。

 

生成索引

按照index_granularity索引粒度,会分别生成primary.idx主键索引(如果声明了二级索引,还会创建二级索引文件)。

 

生成标记和数据压缩文件

按照index_granularity索引粒度,分别生成每一个列字段的.mrk数据标记和.bin压缩数据文件。

 

各个底层物理文件生成的过程

下图所示是一张MergeTree表在写入数据时,它的分区目录、索引、标记和压缩数据的生成过程。

从分区目录201403_1_34_3能够得知,该分区数据共分34次写入,期间发生过3次合并。在数据写入的过程中,依据index_granularity的粒度,依次为每个区间的数据生成索引、标记和压缩数据块。其中,索引和标记区间是对齐的,而标记与压缩块则根据区间数据大小的不同,会生成多对一、一对一和一对多三种关系。

 

查询过程

借助索引文件降低扫描范围

数据查询的本质,可以看作一个不断减小数据范围的过程。在最理想的情况下,MergeTree首先可以依次借助分区索引、主键索引和二级索引,将数据扫描范围缩至最小。

 

借助标记文件降低解压数据的大小

然后再借助数据标记,将需要解压数据块与计算的数据范围缩至最小。以下图所示为例,它示意了在最优的情况下,经过层层过滤,最终获取最小范围数据的过程。

 

如果一条查询语句没有指定任何WHERE条件,或是指定了WHERE条件,但条件没有匹配到任何索引(分区索引、主键索引和二级索引),那么MergeTree就不能预先减小数据范围。在后续进行数据查询时,它会扫描所有分区目录,以及目录内索引段的最大区间。虽然不能减少数据范围,但是MergeTree仍然能够借助数据标记,以多线程的形式同时读取多个压缩数据块,以提升性能。

 

数据标记与压缩数据块的对应关系

由于压缩数据块的划分,与一个间隔(index_granularity)内的数据大小相关,每个压缩数据块的体积都被严格控制在64KB~1MB。而一个间隔(index_granularity)的数据,又只会产生一行数据标记。那么根据一个间隔内数据的实际字节大小,数据标记和压缩数据块之间会产生三种不同的对应关系。接下来使用具体示例做进一步说明,对于示例数据,我们使用的是官方测试数据集中的测试表hits_v1,其中index_granularity粒度为8192,数据总量为8873898行。如果要下载测试数据如下所示

# download test datacurl https://clickhouse-datasets.s3.yandex.net/hits/tsv/hits_v1.tsv.xz | unxz --threads=`nproc` > hits_v1.tsv# new create tableclickhouse-client --query "CREATE DATABASE IF NOT EXISTS datasets"clickhouse-client --query "CREATE TABLE datasets.hits_v1 ( WatchID UInt64,  JavaEnable UInt8,  Title String,  GoodEvent Int16,  EventTime DateTime,  EventDate Date,  CounterID UInt32,  ClientIP UInt32,  ClientIP6 FixedString(16),  RegionID UInt32,  UserID UInt64,  CounterClass Int8,  OS UInt8,  UserAgent UInt8,  URL String,  Referer String,  URLDomain String,  RefererDomain String,  Refresh UInt8,  IsRobot UInt8,  RefererCategories Array(UInt16),  URLCategories Array(UInt16), URLRegions Array(UInt32),  RefererRegions Array(UInt32),  ResolutionWidth UInt16,  ResolutionHeight UInt16,  ResolutionDepth UInt8,  FlashMajor UInt8, FlashMinor UInt8,  FlashMinor2 String,  NetMajor UInt8,  NetMinor UInt8, UserAgentMajor UInt16,  UserAgentMinor FixedString(2),  CookieEnable UInt8, JavascriptEnable UInt8,  IsMobile UInt8,  MobilePhone UInt8,  MobilePhoneModel String,  Params String,  IPNetworkID UInt32,  TraficSourceID Int8, SearchEngineID UInt16,  SearchPhrase String,  AdvEngineID UInt8,  IsArtifical UInt8,  WindowClientWidth UInt16,  WindowClientHeight UInt16,  ClientTimeZone Int16,  ClientEventTime DateTime,  SilverlightVersion1 UInt8, SilverlightVersion2 UInt8,  SilverlightVersion3 UInt32,  SilverlightVersion4 UInt16,  PageCharset String,  CodeVersion UInt32,  IsLink UInt8,  IsDownload UInt8,  IsNotBounce UInt8,  FUniqID UInt64,  HID UInt32,  IsOldCounter UInt8, IsEvent UInt8,  IsParameter UInt8,  DontCountHits UInt8,  WithHash UInt8, HitColor FixedString(1),  UTCEventTime DateTime,  Age UInt8,  Sex UInt8,  Income UInt8,  Interests UInt16,  Robotness UInt8,  GeneralInterests Array(UInt16), RemoteIP UInt32,  RemoteIP6 FixedString(16),  WindowName Int32,  OpenerName Int32,  HistoryLength Int16,  BrowserLanguage FixedString(2),  BrowserCountry FixedString(2),  SocialNetwork String,  SocialAction String,  HTTPError UInt16, SendTiming Int32,  DNSTiming Int32,  ConnectTiming Int32,  ResponseStartTiming Int32,  ResponseEndTiming Int32,  FetchTiming Int32,  RedirectTiming Int32, DOMInteractiveTiming Int32,  DOMContentLoadedTiming Int32,  DOMCompleteTiming Int32,  LoadEventStartTiming Int32,  LoadEventEndTiming Int32, NSToDOMContentLoadedTiming Int32,  FirstPaintTiming Int32,  RedirectCount Int8, SocialSourceNetworkID UInt8,  SocialSourcePage String,  ParamPrice Int64, ParamOrderID String,  ParamCurrency FixedString(3),  ParamCurrencyID UInt16, GoalsReached Array(UInt32),  OpenstatServiceName String,  OpenstatCampaignID String,  OpenstatAdID String,  OpenstatSourceID String,  UTMSource String, UTMMedium String,  UTMCampaign String,  UTMContent String,  UTMTerm String, FromTag String,  HasGCLID UInt8,  RefererHash UInt64,  URLHash UInt64,  CLID UInt32,  YCLID UInt64,  ShareService String,  ShareURL String,  ShareTitle String,  ParsedParams Nested(Key1 String,  Key2 String, Key3 String, Key4 String, Key5 String,  ValueDouble Float64),  IslandID FixedString(16),  RequestNum UInt32,  RequestTry UInt8) ENGINE = MergeTree() PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDate, intHash32(UserID)) SAMPLE BY intHash32(UserID) SETTINGS index_granularity = 8192"# import datacat hits_v1.tsv | clickhouse-client --query "INSERT INTO datasets.hits_v1 FORMAT TSV"# optionally you can optimize tableclickhouse-client --query "OPTIMIZE TABLE datasets.hits_v1 FINAL"

 

多个数据标记对应一个压缩数据块

多个数据标记对应一个压缩数据块,当一个间隔(index_granularity)内的数据未压缩大小size小于64KB时,会出现这种对应关系。

以hits_v1测试表的JavaEnable字段为例。JavaEnable数据类型为UInt8,大小为1B,则一个间隔内数据大小为8192B。所以在此种情形下,每8个数据标记会对应同一个压缩数据块,如下图所示。

 

一个数据标记对应一个压缩数据块

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

以hits_v1测试表的URLHash字段为例。URLHash数据类型为UInt64,大小为8B,则一个间隔内数据大小为65536B,恰好等于64KB。所以在此种情形下,数据标记与压缩数据块是一对一的关系,如下图所示。

 

一个数据标记对应多个压缩数据块

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

以hits_v1测试表的URL字段为例。URL数据类型为String,大小根据实际内容而定。如下图所示,编号45的标记对应了2个压缩数据块。

 

 

结论

ClickHouse号称最快的OLAP型数据库不是空穴来风的,ClickHouse从OLAP场景需求出发,定制开发了一套全新的高效列式存储引擎,并且实现了数据有序存储、主键索引、二级索引、数据压缩和解压等丰富功能。以上功能共同为ClickHouse极速的分析性能奠定了基础。

 

参考资料

  • 《ClickHouse原理解析与应用实践》
    分享大数据行业的一些前沿技术和手撕一些开源库的源代码
    微信公众号名称:技术茶馆
    微信公众号ID    :    Night_ZW

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值