MergeTree 引擎以及 MergeTree 家族中的其他引擎 (例如 ReplacingMergeTree、AggregatingMergeTree) 是 ClickHouse 中最常用、最稳健的表引擎。
MergeTree 家族表引擎专为高数据摄取速率和海量数据而设计。
插入操作会创建表 parts,这些 parts 会由后台进程与其他表 parts 合并。
MergeTree 家族表引擎的主要特性:
- 表的主键决定了每个表 part 内部的排序顺序 (聚簇索引) 。主键也不是引用单独的行,而是引用由 8192 行组成、称为粒度的块。这使得海量数据集的主键足够小,能够始终加载在主存中,同时仍可快速访问磁盘上的数据。
- 表可以使用任意分区表达式进行分区。分区裁剪可确保在查询条件允许时跳过读取某些分区。
- 数据可以在多个集群节点之间复制,以实现高可用、故障转移和零停机升级。请参阅 数据复制。
-
MergeTree表引擎支持多种统计信息类型和采样方法,以帮助进行查询优化。
尽管名称相似,Merge 引擎与
*MergeTree 引擎并不相同。创建表
查询子句
ENGINE
ENGINE — 引擎名称及其参数。ENGINE = MergeTree()。MergeTree 引擎没有参数。
ORDER BY
ORDER BY — 排序键。
由列名或任意表达式组成的 Tuple。例如:ORDER BY (CounterID + 1, EventDate)。
如果未定义主键 (即未指定 PRIMARY KEY) ,ClickHouse 会将排序键作为主键使用。
如果不需要排序,可以使用语法 ORDER BY tuple()。
另外,如果启用了设置 create_table_empty_primary_key_by_default,则会在 CREATE TABLE 语句中隐式添加 ORDER BY ()。另请参见选择主键。
PARTITION BY
PARTITION BY — 分区键。可选。在大多数情况下,不需要分区键;即使需要分区,分区键通常也无需细于按月分区。分区不会加速查询 (这与 ORDER BY 表达式不同) 。切勿使用粒度过细的分区方式。不要按客户端标识符或名称对数据进行分区 (应改为将客户端标识符或名称作为 ORDER BY 表达式中的第一列) 。
对于按月分区,请使用 toYYYYMM(date_column) 表达式,其中 date_column 是一个类型为 Date 的日期列。此处的分区名称采用 "YYYYMM" 格式。
PRIMARY KEY —— 如果主键不同于排序键,则在此处指定。可选。
指定排序键 (使用 ORDER BY 子句) 时,也会隐式指定主键。
通常不需要在排序键之外再单独指定主键。
SAMPLE BY
SAMPLE BY — 采样表达式。可选。
如果指定了 SAMPLE BY,则该表达式必须包含在主键中。
采样表达式的结果必须为无符号整数。
示例:SAMPLE BY intHash32(UserID) ORDER BY (CounterID, EventDate, intHash32(UserID))。
TTL
TTL — 一组规则,用于指定行的存储时长,以及 在磁盘和卷之间自动移动 parts 的逻辑。可选。
表达式必须返回 Date 或 DateTime,例如 TTL date + INTERVAL 1 DAY。
规则类型 DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'|GROUP BY 指定当表达式条件满足时 (即达到当前时间) 要对 part 执行的操作:删除过期行、将 part (如果表达式对 part 中的所有行都成立) 移动到指定磁盘 (TO DISK 'xxx') 或卷 (TO VOLUME 'xxx') ,或者对过期行中的值进行聚合。规则的默认类型为删除 (DELETE) 。可以指定多条规则,但 DELETE 规则最多只能有一条。
更多详情,请参见 列和表的 TTL
设置
CounterID 和 EventDate 呈现伪随机分布。如果你在查询数据时指定 SAMPLE 子句,ClickHouse 将为部分用户返回均匀的伪随机数据样本。
可以省略 index_granularity 设置,因为 8192 是默认值。
数据存储
(CounterID, Date),则 part 中的数据先按 CounterID 排序,并在每个 CounterID 内再按 Date 排序。
属于不同分区的数据会分开存放到不同的数据 parts 中。在后台,ClickHouse 会合并数据 parts 以提高存储效率。属于不同分区的 parts 不会被合并。合并机制并不保证具有相同主键的所有行都位于同一个数据 part 中。
数据 parts 可以采用 Wide 或 Compact 格式存储。在 Wide 格式中,每一列都存储为文件系统中的单独文件;在 Compact 格式中,所有列都存储在一个文件中。Compact 格式可用于提升小批量高频写入的性能。
数据存储格式由表引擎的 min_bytes_for_wide_part 和 min_rows_for_wide_part 设置控制。如果数据 part 中的字节数或行数小于相应设置值,则该 part 以 Compact 格式存储。否则,以 Wide 格式存储。如果这两个设置都未设置,则数据 parts 以 Wide 格式存储。
每个数据 part 在逻辑上都会划分为粒度。粒度是 ClickHouse 在查询数据时读取的最小不可分割数据集。ClickHouse 不会拆分行或值,因此每个粒度始终包含整数数量的行。粒度的第一行会用该行主键的值进行标记。对于每个数据 part,ClickHouse 都会创建一个存储这些标记的索引文件。对于每一列,无论它是否属于主键,ClickHouse 也会存储相同的标记。这些标记使你能够直接在列文件中定位数据。
粒度大小受表引擎的 index_granularity 和 index_granularity_bytes 设置限制。粒度中的行数位于 [1, index_granularity] 范围内,具体取决于行的大小。如果单行大小大于该设置的值,则粒度大小可以超过 index_granularity_bytes。在这种情况下,粒度大小等于该行的大小。
查询中的主键和索引
(CounterID, Date) 主键为例。在这种情况下,排序方式和索引如下所示:
CounterID in ('a', 'h'),服务器会读取标记范围[0, 3)和[6, 8)中的数据。CounterID IN ('a', 'h') AND Date = 3,服务器会读取标记范围[1, 3)和[7, 8)中的数据。Date = 3,服务器会读取标记范围[1, 10]中的数据。
index_granularity * 2 行。
稀疏索引使你能够处理数量非常庞大的表中的行,因为在大多数情况下,这类索引都能装入计算机的 RAM。
ClickHouse 不要求主键是唯一的。你可以插入多行具有相同主键的数据。
你可以在 PRIMARY KEY 和 ORDER BY 子句中使用 Nullable 类型的表达式,但强烈不建议这样做。要启用此功能,请打开 allow_nullable_key 设置。对于 ORDER BY 子句中的 NULL 值,适用 NULLS_LAST 原则。
选择主键
-
提高索引性能。
如果主键是
(a, b),那么在满足以下条件时,加入另一列c可以提升性能:- 存在对列
c进行条件过滤的查询。 - 值相同的
(a, b)对应的长数据范围 (长度达到index_granularity的数倍) 很常见。换句话说,增加另一列后,就能跳过相当长的数据范围。
- 存在对列
- 改善数据压缩效果。 ClickHouse 按主键对数据排序,因此数据一致性越高,压缩效果越好。
- 在 CollapsingMergeTree 和 SummingMergeTree 引擎合并数据 parts 时,提供额外的逻辑。 在这种情况下,指定一个不同于主键的排序键是合理的。
SELECT 查询期间的性能。
你可以使用 ORDER BY tuple() 语法创建不带主键的表。在这种情况下,ClickHouse 会按插入顺序存储数据。如果你希望在通过 INSERT ... SELECT 查询插入数据时保留数据顺序,请设置 max_insert_threads = 1。
如果要按初始顺序读取数据,请使用单线程 SELECT 查询。
选择与排序键不同的主键
GROUP BY,并按维度过滤。由于 SummingMergeTree 和 AggregatingMergeTree 会聚合排序键值相同的行,因此很自然会将所有维度都加入排序键。这样一来,键表达式就会由一长串列组成,而且每次新增维度时,都需要频繁更新这个列表。
在这种情况下,更合理的做法是仅在主键中保留少量能够提供高效范围扫描的列,并将其余维度列加入排序键 Tuple。
对排序键执行 ALTER 是一种轻量级操作,因为当新列同时添加到表和排序键中时,现有的数据 parts 不需要修改。由于旧排序键是新排序键的前缀,并且新添加的列中还没有数据,因此在修改表的那一刻,数据同时满足按旧排序键和新排序键排序。
在查询中使用索引和分区
SELECT 查询,ClickHouse 会分析是否能够使用索引。如果 WHERE/PREWHERE 子句中包含某个表达式 (无论是作为合取条件之一,还是整个子句本身) ,且该表达式表示等值或不等值比较,或者包含带固定前缀的 IN 或 LIKE,并且作用于主键或分区键中的列或表达式、这些列的某些部分重复函数,或这些表达式之间的逻辑关系,那么就可以使用索引。
因此,可以在主键的一个或多个范围上快速执行查询。在此示例中,如果查询条件是特定跟踪标签、特定标签加日期范围、特定标签加日期、多个标签加日期范围等,查询都会很快。
下面来看如下配置的引擎:
在主键中为确定性表达式使用索引
length()、toDate()、lower()、left()、cityHash64()、toUUID();不同于 now() 或 rand()) ,那么它就是确定性的。如果主键包含确定性表达式,ClickHouse 就可以将它们应用到查询中的常量值上,并利用结果在主键索引上构造条件。这使得 =、IN 和 has 等谓词也能触发数据跳过。
一个常见用例是让主键保持紧凑 (例如,存储哈希而不是较长的 String) ,同时仍然允许针对原始列的谓词使用索引。
确定性 (但非单射) 主键示例:
length('alice') (以及其他常量) 预先计算一次,并利用这些长度值来缩小主键索引中的范围。由于字符串长度不是单射函数,不同的 user_id 字符串可能具有相同的长度,因此索引可能会读取额外的粒度 (假阳性) 。不过结果仍然是正确的,因为读取之后仍会继续应用原始谓词 (user_id = ...、IN 等) 。
如果该确定性表达式同时也是单射的 (对于所使用的参数类型,不同输入不会产生相同输出) ,那么 ClickHouse 还可以将索引有效地用于其否定形式:!=、NOT IN 和 NOT has(...)。例如,reverse(p) 和 hex(p) 对 String 来说都是单射的。
单射主键示例:
部分单调主键的索引使用
数据跳过索引
CREATE 语句的 columns 部分。
*MergeTree 家族的表,可以指定数据跳过索引。
这些索引会在由 granularity_value 个粒度组成的块上,为指定表达式聚合一些信息 (粒度大小通过表引擎中的 index_granularity 设置指定) 。随后,这些聚合信息会用于 SELECT 查询,通过跳过那些无法满足 where 查询条件的大块数据,减少从磁盘读取的数据量。
GRANULARITY 子句可以省略,granularity_value 的默认值为 1。
示例
跳过索引类型
MergeTree 表引擎支持以下类型的跳过索引。
有关如何使用跳过索引来优化性能的更多信息,
请参阅“了解 ClickHouse 数据跳过索引”。
MinMax索引Set索引bloom_filter索引ngrambf_v1索引 (已弃用)tokenbf_v1索引 (已弃用)text索引vector_similarity索引
MinMax 跳过索引
tuple,则会分别存储元组中每个元素的最小值和最大值。)
Syntax
Set
max_rows 个唯一值。
max_rows = 0 表示“存储所有唯一值”。
Syntax
布隆过滤器
Syntax
false_positive_rate 参数可以取 0 到 1 之间的值 (默认值:0.025) ,用于指定产生误报的概率 (这会增加需要读取的数据量) 。
支持以下数据类型:
(U)Int*Float*EnumDateDateTimeStringFixedStringArrayLowCardinalityNullableUUIDMap
JSON 数据类型:为 JSON 路径创建索引对于
JSON 数据类型,可以使用 JSONAllPaths 函数在路径集合上创建 bloom filter 索引。这样可以跳过不存在所查询 JSON 路径的粒度。详见 JSON 的数据跳过索引。N-gram 布隆过滤器 (已弃用)
自 ClickHouse 26.2 版本起,随着
text 索引正式进入 GA,ngrambf_v1 索引已不再建议用于全文检索。详情请参阅页面 “使用文本索引进行全文检索”。Syntax
该索引仅适用于以下数据类型:
要估算
ngrambf_v1 的参数,可以使用以下用户自定义函数 (UDF) 。
UDFs for ngrambf_v1
total_number_of_all_gramsprobability_of_false_positives
4300 个 ngram,并且您预计误报率会低于 0.0001。
然后可以通过执行以下查询来估算其他参数:
标记布隆过滤器
自 ClickHouse 26.2 版本起,随着
text 索引进入正式可用 (GA) 阶段,tokenbf_v1 索引已不再推荐用于全文检索。详情请参见页面 “使用文本索引进行全文检索”。Syntax
稀疏 grams 布隆过滤器
ngrambf_v1 类似,但它使用的是稀疏 grams 标记,而非 ngrams。
Syntax
文本索引
向量相似性
函数支持
WHERE 子句中的条件包含对作用于列的函数调用。如果该列属于某个索引的一部分,ClickHouse 会在执行这些函数时尝试使用该索引。ClickHouse 对可使用索引的函数支持不同的子集。
set 类型的索引可用于所有函数。其他索引类型的支持情况如下:
常量参数小于 ngram 大小的函数,不能被
ngrambf_v1 用于查询优化。
(*) 要使 hasTokenCaseInsensitive 和 hasTokenCaseInsensitiveOrNull 生效,必须基于转为小写的数据创建 tokenbf_v1 索引,例如 INDEX idx (lower(str_col)) TYPE tokenbf_v1(512, 3, 0)。
布隆过滤器可能出现误报,因此
ngrambf_v1、tokenbf_v1、sparse_grams 和 bloom_filter 索引不能用于优化那些函数结果预期为 false 的查询。例如:- 可以优化:
s LIKE '%test%'NOT s NOT LIKE '%test%'s = 1NOT s != 1startsWith(s, 'test')
- 不能优化:
NOT s LIKE '%test%'s NOT LIKE '%test%'NOT s = 1s != 1NOT startsWith(s, 'test')
投影
在实现投影时,还应考虑 force_optimize_projection 设置。
SELECT 语句不支持投影。
投影查询
投影索引
_part_offset 的形式存储,即 SELECT _part_offset ORDER BY <index_expr>。
语法
索引类型
- basic:等同于表达式上的普通 MergeTree 索引。
投影存储
MergeTree 表的分片。该表由投影的定义查询推导而来。如果存在 GROUP BY 子句,底层存储引擎就会变为 AggregatingMergeTree,并且所有聚合函数都会转换为 AggregateFunction。如果存在 ORDER BY 子句,MergeTree 表会将其用作主键表达式。在合并过程中,投影分片会通过其存储引擎的合并例程进行合并。父表分片的校验和会与投影分片的校验和组合在一起。其他维护作业与跳过索引类似。
查询分析
- 检查投影是否可用于回答给定的查询,也就是说,它产生的结果应与查询基表得到的结果相同。
- 选择最佳的可行匹配,即需要读取的粒度最少的匹配。
- 使用投影的查询管道会不同于使用原始 parts 的查询管道。如果某些 parts 中缺少投影,我们可以在运行时添加一个管道来即时对其进行“投影”。
并发数据访问
列和表的 生存时间 (TTL)
TTL 子句。表级 TTL 还可以指定数据在磁盘和卷之间自动移动的规则,或者在数据全部过期后重新压缩 parts。
表达式的求值结果必须为 Date、Date32、DateTime 或 DateTime64 数据类型。
语法
为列设置生存时间:
interval,请使用时间间隔运算符,例如:
列 生存时间 (TTL)
TTL 子句不能用于键列。
示例
创建带 TTL 的表:
为现有表的列添加生存时间 (TTL)
修改列的生存时间 (TTL)
表生存时间 (TTL)
TTL 表达式的条件。
DELETE- 删除已过期的行 (默认操作) ;RECOMPRESS codec_name- 使用codec_name重新压缩数据分区片段;TO DISK 'aaa'- 将分区移动到磁盘aaa;TO VOLUME 'bbb'- 将分区移动到卷bbb;GROUP BY- 对已过期的行进行聚合。
DELETE 操作可以与 WHERE 子句配合使用,根据过滤条件仅删除部分已过期的行:
GROUP BY 表达式必须是表主键的前缀。
如果某一列不在 GROUP BY 表达式中,且未在 SET 子句中显式设置,则在结果行中,该列会包含分组后各行中的某个值 (就像对其应用了聚合函数 any 一样) 。
示例
创建带有 TTL 的表:
修改表的 TTL:
创建表,并对过期行重新压缩:
x 包含分组后各行中的最大值,y——最小值,d——分组后各行中的任意一个值。
删除过期数据
TTL 的数据会在 ClickHouse 合并数据 parts 时被删除。
当 ClickHouse 检测到数据已过期时,会执行一次计划外合并。要控制此类合并的频率,可以设置 merge_with_ttl_timeout。如果该值过低,系统会执行大量计划外合并,可能消耗大量资源。
如果你在两次合并之间执行 SELECT 查询,可能会读到已过期的数据。为避免这种情况,请在执行 SELECT 之前先使用 OPTIMIZE 查询。
另请参见
磁盘类型
s3用于 S3 和 MinIOgcs用于 GCSblob_storage_disk用于 Azure Blob 存储hdfs用于 HDFSweb用于从 Web 进行只读访问cache用于本地缓存s3_plain用于备份到 S3s3_plain_rewritable用于存储在 S3 中的不可变非复制表
使用多个块设备存储数据
简介
MergeTree 家族表引擎可以将数据存储在多个块设备上。例如,当某个表的数据天然分为“热”数据和“冷”数据时,这会很有用。较新的数据会被频繁查询,但只占用少量空间。相反,呈长尾分布的历史数据很少被查询。如果有多个磁盘可用,“热”数据可以放在高速磁盘上 (例如 NVMe SSD 或内存中) ,而“冷”数据则放在相对较慢的磁盘上 (例如 HDD) 。
这适用于所有磁盘类型,包括 S3 和其他对象存储磁盘。例如,你可以在单个卷内将数据分散到多个 S3 存储桶中,或者创建分层策略,将数据从本地磁盘迁移到 S3。详见使用带多个卷的 S3 磁盘。
数据分区片段是 MergeTree 引擎表中最小的可移动单元。一个数据分区片段中的数据存储在同一个磁盘上。parts 既可以在后台于磁盘之间移动 (根据用户设置) ,也可以通过 ALTER 查询移动。
术语
- 磁盘 — 挂载到文件系统上的块设备。
- 默认磁盘 — 用于存储 path 服务器设置中指定路径的磁盘。
- 卷 — 由相同类型磁盘组成的有序集合 (类似于 JBOD) 。
- 存储策略 — 由一组卷及其之间的数据移动规则构成。
MergeTree 引擎家族表的 storage_policy 设置。
配置
<storage_configuration> 标签内声明,并放在 config.d 目录中的某个文件中。
配置结构:
<disk_name_N>— 磁盘名称。所有磁盘的名称都必须不同。path— 服务器用于存储数据 (data和shadow文件夹) 的路径,应以 ’/’ 结尾。keep_free_space_bytes— 需要预留的磁盘可用空间大小。
policy_name_N— 策略名称。策略名称必须唯一。volume_name_N— 卷名称。卷名称必须唯一。disk— 卷中的一个磁盘。max_data_part_size_bytes— 该卷中任一磁盘可存储的数据分区片段的最大大小。如果估算出的合并后分区片段大小超过max_data_part_size_bytes,则该分区片段会写入下一个卷。该功能主要用于将新的或较小的parts保存在热 (SSD) 卷上,并在其变大后移动到冷 (HDD) 卷上。如果你的策略只有一个卷,请不要使用此设置。move_factor— 当可用空间低于该系数时,数据会自动开始移动到下一个卷 (如果存在,默认值为 0.1) 。ClickHouse 会按大小从大到小 (降序) 对现有parts进行排序,并选择总大小足以满足move_factor条件的parts。如果所有parts的总大小仍不足,则会移动全部parts。perform_ttl_move_on_insert— 禁止在数据分区片段 INSERT 时执行 TTL 移动。默认情况下 (启用时) ,如果插入的数据分区片段按照 TTL 移动规则已经过期,它会立即进入移动规则中声明的卷/磁盘。如果目标卷/磁盘较慢 (例如 S3) ,这可能会显著拖慢插入速度。如果禁用,则已过期的数据分区片段会先写入默认卷,然后立即移动到 TTL 卷。load_balancing- 磁盘均衡策略,round_robin或least_used。least_used_ttl_ms- 配置所有磁盘可用空间更新的超时时间 (毫秒) (0- 始终更新,-1- 从不更新,默认值为60000) 。注意,如果该磁盘仅供 ClickHouse 使用,且不会发生在线文件系统扩容/缩容,可以使用-1;其他情况下不建议这样做,因为最终会导致空间分布不正确。prefer_not_to_merge— 你不应该使用此设置。它会禁用该卷上的数据分区片段合并 (这有害,并会导致性能下降) 。启用此设置时 (不要这样做) ,将不允许在该卷上合并数据 (这很糟糕) 。这让你可以 (但其实不需要) 控制 (如果你想控制这个,说明你做错了) ClickHouse 如何使用慢速磁盘 (但 ClickHouse 更清楚该怎么做,所以请不要使用此设置) 。volume_priority— 定义卷的填充优先级 (顺序) 。值越小,优先级越高。参数值应为自然数,并且整体上必须连续覆盖从 1 到 N (N 为最低优先级) 之间的范围,中间不能跳过任何数字。- 如果 所有 卷都设置了该标签,则按给定顺序确定优先级。
- 如果只有 部分 卷设置了该标签,则未设置标签的卷优先级最低,并按其在配置中定义的顺序确定优先级。
- 如果 没有 卷设置该标签,则其优先级将按它们在配置中声明的顺序确定。
- 两个卷不能具有相同的优先级值。
hdd_in_order 策略采用了 round-robin 方法。因此,该策略只定义了一个卷 (single) ,parts 会按循环顺序存储到该卷的所有磁盘上。如果系统挂载了多个性能相近的磁盘,但未配置 RAID,这种策略会非常有用。请注意,单个磁盘本身并不可靠,你可能需要通过将复制因子设为 3 或更高来弥补这一点。
如果系统中有不同类型的磁盘,则可以改用 moving_from_ssd_to_hdd 策略。卷 hot 由一个 SSD 磁盘 (fast_ssd) 组成,且可存储在该卷上的单个分片最大为 1GB。所有大于 1GB 的 parts 都会直接存储到 cold 卷上,该卷包含一个 HDD 磁盘 disk1。
此外,一旦 fast_ssd 磁盘的使用率超过 80%,后台进程就会将数据转移到 disk1。
在存储策略中,卷的枚举顺序很重要,尤其是在所列卷中至少有一个未显式设置 volume_priority 参数时。
一旦某个卷满了,数据就会被移动到下一个卷。磁盘的枚举顺序也同样重要,因为数据会依次存储到这些磁盘上。
创建表时,可以为其应用一个已配置的存储策略:
default 存储策略表示只使用一个卷,而该卷仅包含 <path> 中指定的一个磁盘。
你可以在创建表后使用 [ALTER TABLE … MODIFY SETTING] 查询更改存储策略;新策略应包含所有旧磁盘和卷,且名称必须保持不变。
执行数据分区片段后台移动的线程数可以通过 background_move_pool_size 设置进行调整。
详细说明
MergeTree 表,数据会通过不同方式写入磁盘:
- 作为插入 (
INSERT查询) 的结果。 - 在后台合并和变更期间。
- 从另一个副本下载时。
- 作为分区冻结 ALTER TABLE … FREEZE PARTITION 的结果。
- 选择第一个 (按定义顺序) 有足够磁盘空间存储 parts (
unreserved_space > current_part_size) ,并且允许存储该大小 parts (max_data_part_size_bytes > current_part_size) 的卷。 - 在该卷内,选择紧随上一个用于存储前一个 chunk 数据的磁盘之后的那个磁盘,并且该磁盘的可用空间大于 parts 的大小 (
unreserved_space - keep_free_space_bytes > current_part_size) 。
move_factor parameter) ,按照各卷在配置文件中声明的顺序,在卷之间移动。
数据永远不会从最后一个卷转移到第一个卷,也不会从第一个卷转移到最后一个卷。可以使用系统表 system.part_log (field type = MOVE_PART) 和 system.parts (fields path 和 disk) 来监控后台移动。此外,也可以在服务器日志中找到详细信息。
用户可以使用查询 ALTER TABLE … MOVE PART|PARTITION … TO VOLUME|DISK … 强制将某个 parts 或分区从一个卷移动到另一个卷或磁盘,同时会考虑后台操作的所有限制条件。该查询会自行发起移动,不会等待后台操作完成。如果可用空间不足,或者任何必需条件未满足,用户将收到错误消息。
移动数据不会干扰数据复制。因此,对于同一张表,可以在不同副本上指定不同的存储策略。
后台合并和变更完成后,旧 parts 只有在经过一定时间 (old_parts_lifetime) 之后才会被删除。
在此期间,它们不会被移动到其他卷或磁盘。因此,在这些 parts 最终被删除之前,它们仍会被计入已占用磁盘空间的评估中。
用户可以使用 min_bytes_to_rebalance_partition_over_jbod setting,将新的大 parts 以均衡方式分配到某个 JBOD 卷的不同磁盘上。
使用外部存储来存储数据
s3、azure_blob_storage、hdfs 的磁盘,将数据存储到 S3、AzureBlobStorage 和 HDFS 中。更多详情请参见配置外部存储选项。
以下示例展示了如何使用类型为 s3 的磁盘将 S3 用作外部存储。
配置如下:
在多个卷中使用 S3 磁盘
使用
use_environment_credentials 进行 S3 身份验证时,环境凭据 (AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_SESSION_TOKEN) 会在所有 S3 磁盘之间共享。无法为不同磁盘使用不同的环境凭据。如果每个 S3 磁盘都需要不同的凭据,请改为在每个磁盘上显式设置 access_key_id 和 secret_access_key。table_disk = true) 。请参阅 refresh_parts_interval and table_disk。
缓存配置ClickHouse 22.3 到 22.7 版本使用不同的缓存配置;如果您使用的是其中一个版本,请参阅 using local cache。
虚拟列
_part— 分片 的名称。_part_index— 该 分片 在查询结果中的顺序索引。_part_starting_offset— 该 分片 在查询结果中的累计起始行。_part_offset— 该 分片 中的行号。_part_granule_offset— 该 分片 中的粒度编号。_partition_id— 分区名称。_part_uuid— 分片 的唯一标识符 (如果启用了 MergeTree 设置assign_part_uuids) 。_part_data_version— 分片 的数据版本 (最小块编号或变更版本) 。_partition_value—partition by表达式的值 (一个元组) 。_sample_factor— 样本系数 (来自查询) 。_block_number— 在插入时分配给该行的原始块编号;启用设置enable_block_number_column后,在合并时会保留。_block_offset— 在插入时分配给该行的原始块内行号;启用设置enable_block_offset_column后,在合并时会保留。_disk_name— 用于存储的磁盘名称。
列统计信息
*MergeTree* 家族的表,统计信息是在 CREATE 查询的 columns 部分中声明的:
ALTER 语句来修改统计信息:
set use_statistics = 1 后,才能将其用于 prewhere 优化。
利用统计信息进行分片裁剪
use_statistics_for_part_pruning 后,即可使用统计信息进行分片裁剪。
目前,只有 basic 统计信息 (以及已弃用的 minmax 统计信息) 支持分片裁剪。为某一列定义此类统计信息后,ClickHouse 会跟踪该列在每个数据分片中的最小值和最大值。
分片裁剪可以在查询过滤条件不可能匹配该分片中的任何行时,跳过读取整个数据分片。
示例:
可用的列统计信息类型
-
basic从列中提取的一组紧凑的单值摘要。根据列类型,会填充以下内容:-
对于任何以数字表示值的列 (整数、浮点数、
Decimal*、Date*、DateTime*、Enum*、IPv4,……) :最小值和最大值,可用于估算范围过滤器的选择性并启用分片裁剪; -
对于
String和FixedString列:非NULL值的总字节长度 (可由此推导出平均字符串长度) ; -
对于
Nullable和LowCardinality(Nullable)列:NULL值的数量,优化器会用它在选择性估算中扣除NULL行。 单个basic统计信息可以同时填充其中多项——例如在Nullable(UInt32)列上,它会同时跟踪数值最小/最大值和NULL计数。与minmax相比,basic还可用于String/FixedString列,并且也可以仅为了跟踪NULL计数而声明在UUID或IPv6等类型的Nullable包装类型上。
-
对于任何以数字表示值的列 (整数、浮点数、
-
minmax(已弃用)
minmax 统计信息已弃用,且无法再创建 (CREATE TABLE ... STATISTICS(minmax) 和 ALTER TABLE ... ADD/MODIFY STATISTICS ... TYPE minmax 会返回错误) 。带有 minmax 统计信息的现有表和 parts 仍可继续工作。请改用 basic 统计信息。tdigest
-
uniqBJKST 草图,可用于估算某列包含多少个不同值。其内部使用uniq。 -
uniq_v2与uniq类似,但内部使用uniqCombined(12)(HyperLogLog 的一种变体) 。其内存消耗比uniq更少,并且构建速度更快。 -
countmin
支持的数据类型
上述各项也都支持所列类型的
Nullable 和 LowCardinality(Nullable) 包装类型。Basic 还可以额外声明在诸如 UUID 或 IPv6 等类型的 Nullable 包装器上,仅用于统计空值数量。
支持的操作
对于
String / FixedString 列上的 basic,该统计信息仅记录非 NULL 值的总字节长度
(用于估算平均字符串长度) 以及 NULL 计数;
它不用于范围过滤器或分片裁剪。
列级设置
max_compress_block_size— 写入表时,压缩前未压缩数据块的最大大小。min_compress_block_size— 写入下一个标记时,触发压缩所需的未压缩数据块最小大小。
- 从列声明中删除
SETTINGS:
- 修改设置:
- 重置一个或多个设置,并移除该表 CREATE 查询中列表达式里的设置声明。