TOC
MergeTree
MergeTree
引擎和改系列的其他引擎(*MergeTree
)是最稳健的ClickHouse表引擎。
MergeTree
系列的引擎是为将大量数据插入到表中而设计的。
数据一个数据片段一个数据片段的快速写入表中,然后在后台应用规则合并数据片段。这种方法比在插入期间不断重写存储中的数据要有效得多。
主要特点:
- 存储数据按主键排序。 这允许您创建一个小的稀疏索引,有助于更快地查找数据。
- 如果指定了分区键,就可以使用分区。 ClickHouse支持某些分区的某些操作,这些操作在具有相同结果的相同数据上比一般操作更有效。ClickHouse还会自动切分查询中指定分区键的分区数据。这同样提高了查询性能。
- 数据复制支持。
ReplicatedMergeTree
系列的表提供数据复制。更多信息,请参考数据复制。 - 数据采样支持。 如果必要的话,可以在表中设置数据抽样方法。
Info:
Merge
引擎不属于*MergeTree
系列。
创建一张表
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2],
...
INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,
INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2
) ENGINE = MergeTree()
[PARTITION BY expr]
[ORDER BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...]
[SETTINGS name=value, ...]
有关参数的描述,参考CREATE
查询描述。
Note: 索引是实验功能,参考
数据跳跃索引
。
查询子句
-
ENGINE
— 引擎的名称和参数。ENGINE = MergeTree()
。MergeTree
引擎没有参数。 -
PARTITION BY
— 分区键。按月分区,可以使用
toYYYYMM(date_column)
表达式,date_column是Date
类型的列。这里的分区名称采用“YYYYMM”
格式。 -
ORDER BY
— 排序键。列的元组或任意表达式。例如:
ORDER BY (CounterID, EventDate)
。 -
PRIMARY KEY
— 主键(如果它与排序键不同)。默认情况下,主键与排序键相同(由
ORDER By
子句指定)。 因此在大多数情况下,没有必要单独指定的PRIMARY KEY
子句。 -
SAMPLE BY
— 抽样的表达式。如果使用抽样表达式,主键必须包含它。示例:
SAMPLE BY intHash32(UserID) ORDER BY (CounterID, EventDate, intHash32(UserID))
。 -
TTL
— 指定行存储时间和定义在磁盘与卷之间逻辑自动移动数据片段的规则列表。表达式的结果必须有一个
Date
或DateTime
列。例如:TTL date + INTERVAL 1 DAY
规则的类型
DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'
指明如果满足表达式(达到当前时间)会在数据片段上做一个操作:删除过期的行,移动数据片段(如果一个数据片段中的所有行满足表达式)到指定磁盘(TO DISK 'xxx'
)或卷(TO VOLUME 'xxx'
)。默认的规则类型是移除(DELETE
)。可以指定多个规则表,但DELETE
规则不应该超过一个。有关详细信息,参考列和表的
TTL
-
SETTINGS
— 控制MergeTree
树行为的附加参数:index_granularity
— 索引标记间的最大数据行数。默认值:8192。参考数据存储。index_granularity_bytes
— 最大的索引粒度,以字节为单位。默认值:10 Mb。若要仅根据行数限制粒度大小,请将其设置为0(不建议)。参考数据存储。enable_mixed_granularity_parts
— 启用或禁用转换,通过设置index_granularity_bytes
来控制粒度大小的。在版本19.11之前,只有index_granularity
设置用于限制粒度大小。index_granularity_bytes
设置在具有很多行(几十兆和几百兆字节)的表选择数据时提升了ClickHouse的性能。如果表中行比较多,可以为表启用此设置,以提高SELECT
查询的效率。use_minimalistic_part_header_in_zookeeper
— 数据片段头在 ZooKeeper 中的存储方式。如果设置了use_minimalistic_part_header_in_zookeeper=1
,ZooKeeper 会存储更少的数据。有关更多信息,请参考“服务器配置参数”中的设置说明。min_merge_bytes_to_use_direct_io
— 合并操作使用直接I/O访问存储磁盘要求的最小数据量。 在合并数据片段时,ClickHouse计算要合并的所有数据的总存储容量。 如果大小超过min_merge_bytes_to_use_direct_io
字节,则ClickHouse使用直接I/O接口(O_DIRECT
选项)读写数据到存储磁盘。 如果min_merge_bytes_to_use_direct_io = 0
,则禁用直接I/O。 默认值:10 * 1024 * 1024 * 1024
字节。merge_with_ttl_timeout
— TTL合并频率的最小间隔时间(以秒为单位)。默认值: 86400 (1 天)。write_final_mark
— 启动或禁用在数据片段末尾写入最终索引标记。默认值:1。不要关掉它。merge_max_block_size
— 合并操作中数据块的最大行数。默认值:8192。storage_policy
— 存储策略。参考使用使用多个区块装置进行数据存储。
章节设置示例
ENGINE MergeTree() PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDate, intHash32(UserID)) SAMPLE BY intHash32(UserID) SETTINGS index_granularity=8192
在本例中,我们按月设置分区。
我们还根据用户ID将采样表达式设置为散列。这允许您为每个CounterID和EventDate伪随机化表中的数据。
同时我们设置了一个按UserID
哈希的抽样表达式。这让你可以为每个 CounterID
和 EventDate
下面的数据伪随机分布。如果你在查询时指定了 SAMPLE
子句。 ClickHouse会返回一个均匀的伪随机的用户子集的数据采样。
可以省略index_granularity
设置,因为8192是默认值。
建表弃置方法
Attention:不要在新项目中使用此方法。如果可能,将旧项目切换到上面描述的方法。
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE [=] MergeTree(date-column [, sampling_expression], (primary, key), index_granularity)
MergeTree()
参数:
date-column
—Date
类型列名。ClickHouse 会自动基于这个列按月创建分区。分区名格式为 “YYYYMM” 。sampling_expression
— 采样表达式。(primary, key)
— 主键。类型 —Tuple()
index_granularity
— 索引粒度。即索引中相邻标记间的数据行数。设为8192可以适用大部分场景。
示例
MergeTree(EventDate, intHash32(UserID), (CounterID, EventDate, intHash32(UserID)), 8192)
MergeTree
引擎的配置跟上面示例的主引擎配置方法相同。
数据存储
表由按主键排序的数据片段组成。
当数据被插入到表中时,会创建单独的数据片段,并且每个数据片段都按序按主键的字典顺序顺序。例如,如果主键是(CounterID, Date)
,则数据片段中的数据按CounterID
排序,在每个CounterID
中,按Date
排序。
属于不同分区的数据被分成不同的数据片段。在后台,ClickHouse合并数据片段以实现更高效的存储。不合并属于不同分区的数据片段。合并机制不保证具有相同主键的所有行都位于同一个数据片段中。
每个数据片段在逻辑上被划分为颗粒。颗粒是ClickHouse在读取时选择数据的最小的不可分割的数据集。ClickHouse不拆分行或值,因此每个颗粒始终包含一个整数行号。颗粒的第一行被标记为该行的主键值。对于每个数据片段,ClickHouse创建一个存储这些标记的索引文件。对于每一列,无论它是否在主键中,ClickHouse也存储相同的标记。这些标记允许您直接在列文件中查找数据。
粒度大小受表引擎的index_granularity
和index_granularity_bytes
设置的限制。一个颗粒中的行数位于[1,index_granularity]
范围内,这取决于行大小。如果单行的大小大于设置的值,则颗粒的大小可以超过index_granularity_bytes
。在本例中,颗粒的大小等于行大小。
查询中的主键和索引
以(CounterID, Date)
主键为例。在这个例子中,排序和索引如下图所示:
Whole data: [---------------------------------------------]
CounterID: [aaaaaaaaaaaaaaaaaabbbbcdeeeeeeeeeeeeefgggggggghhhhhhhhhiiiiiiiiikllllllll]
Date: [1111111222222233331233211111222222333211111112122222223111112223311122333]
Marks: | | | | | | | | | | |
a,1 a,2 a,3 b,3 e,2 e,3 g,1 h,2 i,1 i,3 l,3
Marks numbers: 0 1 2 3 4 5 6 7 8 9 10
如果数据查询指明:
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不需要唯一的主键。可以使用相同的主键插入多行。
选择主键
主键中的列数没有明确的限制。根据数据结构的不同,可以在主键中包含更多或更少的列。这可能:
-
提升索引的性能。 如果主键是
(a, b)
,那么在满足以下条件的情况下,添加另一列c将提高性能:- 查询中有带c列条件。
- 很长的数据范围(比
index_granularity
长几倍)内具有相同(a, b)
值。换句话说,当其他列时,可以跳过相当长的数据范围。
-
提升数据压缩。 ClickHouse根据主键对数据进行排序,因此一致性越高,压缩效果就越好。
-
在合并
CollapsingMergeTree
和SummingMergeTree
引擎中的数据片段时,提供额外的逻辑。 在这种情况下,指定与主键不同的排序键是有意义的。
长主键会对插入性能和内存消耗产生负面影响,但是在SELECT
查询期间,主键中的额外列不会影响ClickHouse的性能。
选择与排序键不同的主键
可以指定与排序键(用于对数据片段中的行进行排序的表达式)不同的主键(为每个标记值写入索引文件中的表达式的值)。在这种情况下,主键表达式元组必须是排序键表达式元组的前缀。
这个特性在使用SummingMergeTree
和AggregatingMergeTree
表引擎时非常有用。在使用这些引擎常见情况下,表有两种类型的列:维度和度量。典型的查询 是按维度任意GROUP BY
和过滤,然后聚合度量列的值。因为SummingMergeTree
和AggregatingMergeTree
聚合了具有相同排序键值的行,所以向它添加所有维读是很自然的。
因此,键表达式由一个很长的列的列表组成,这个列表必须经常使用新添加的维度进行更新。
在这种情况下,只在主键中保留一些列是有意义的,这些列将提供有效的范围扫描,并将其余的维度列添加到排序键元组中。
更改排序键是一种轻量级操作,因为当同时向表和排序键添加新列时,不需要更改现有的数据部分。 由于旧的排序键是新排序键的前缀,并且新添加的列中没有数据,因此在修改表时,数据是按新旧排序键排序的。
这种情况下,主键中仅预留少量列保证高效范围扫描,剩下的维度列放到排序键元组里,这样是合理的。
排序键的ALTER
是轻量级的操作,因为一个新列同时被加入到表里和排序键时,已存在的数据片段并不需要修改。由于旧的排序键是新排序键的前缀,并且刚刚添加的列中没有数据,因此在表修改时的数据对于新旧的排序键来说都是有序的。
查询中索引和分区的使用
对于SELECT
查询,ClickHouse分析是否可以使用索引。如果WHERE/PREWHERE
子句中有一个代表相等或不相等比较操作的表达式。
索引可以使用 如果/ PREWHERE条款在一个表达式(结合的元素,或完全)代表一个平等或不平等比较操作,或如果它像一个固定的前缀在主键列或表达式,或分区键,或其中的某些部分重复的功能列,或逻辑关系的表达式。
ENGINE MergeTree() PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDate) SETTINGS index_granularity=8192
在这种情况下,在查询:
SELECT count() FROM table WHERE EventDate = toDate(now()) AND CounterID = 34
SELECT count() FROM table WHERE EventDate = toDate(now()) AND (CounterID = 34 OR CounterID = 42)
SELECT count() FROM table WHERE ((EventDate >= toDate('2014-01-01') AND EventDate <= toDate('2014-01-31')) OR EventDate = toDate('2014-05-01')) AND CounterID IN (101500, 731962, 160656) AND (CounterID = 101500 OR EventDate != toDate('2014-05-01'))
ClickHouse将使用主键索引来切除不适当的数据,使用按月分区键来切除日期范围不适当的分区。
上面的查询表明,索引甚至可以用于复杂的表达式。 从表中读取是有组织的,因此使用索引不会比全表扫描慢。
在下面的示例中,不能使用索引。
SELECT count() FROM table WHERE CounterID = 34 OR URL LIKE '%upyachka%'
要检查ClickHouse在运行查询时是否可以使用索引,可以使用设置force_index_by_date
和force_primary_key
。
按月分区键允许只读取那些包含来自正确范围的日期的数据块。在这种情况下,数据块可能包含多个日期(最多完整一个月)的数据。在一个块中,数据按主键排序,主键可能不包含日期作为第一列。因此,只使用日期条件而不指定主键前缀的查询会导致读取比单个日期更多的数据。
对部分单调主键使用索引
例如,考虑每月的天数。它们在一个月的时间里形成单调序列,但在更长的时间里则不是单调序列。这是一个部分单调序列。如果用户使用部分单调的主键创建表,ClickHouse像往常一样创建一个稀疏索引。当用户从此类表中选择数据时,ClickHouse分析查询条件。如果用户希望获得索引的两个标记之间的数据,并且这两个标记都在一个月内,ClickHouse可以在这种特殊情况下使用索引,因为它可以计算查询的参数与索引标记之间的距离。
如果查询参数范围内的主键值不表示单调序列,则ClickHouse不能使用索引。 在本例中,ClickHouse使用全表扫描方法。
ClickHouse不仅对月份的天数序列使用这种逻辑,而且对表示部分单调序列的任何主键都使用这种逻辑。
跳数索引(实验)
索引声明位于CREATE
查询的列部分。
INDEX index_name expr TYPE type(...) GRANULARITY granularity_value
对于来自*MergeTree
系列的表,可以指定跳数索引。
这些索引聚合了关于块上的指定表达式的一些信息,这些块由granularity_value
颗粒组成(颗粒的大小是使用表引擎中指定的index_particle
设置)。
然后,在SELECT查询中使用这些聚合,通过跳过不满足where
查询的大数据块来减少从磁盘读取的数据量。
示例:
CREATE TABLE table_name
(
u64 UInt64,
i32 Int32,
s String,
...
INDEX a (u64 * i32, s) TYPE minmax GRANULARITY 3,
INDEX b (u64 * length(s)) TYPE set(1000) GRANULARITY 4
) ENGINE = MergeTree()
...
在以下例子中可以ClickHouse可以使用的索引来减少从磁盘读取的数据量:
SELECT count() FROM table WHERE s < 'z'
SELECT count() FROM table WHERE u64 * i32 == 10 AND u64 * length(s) >= 1234
可用的索引类型
-
minmax
存储指定表达式的极端值(如果表达式是
tuple
,那么它将存储tuple
内每个元素的极端值),使用存储的信息像主键一样跳过数据块。 -
set(max_rows)
存储指定表达式的唯一值(不超过
max_rows
行,max_rows=0
表示“无限制”)。使用这些值检查WHERE
表达式在数据块上是否不满足。 -
ngrambf_v1(n, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)
存储一个包含来自数据块的所有N元语法的Bloom过滤器。只对字符串有效。 可以用于优化
equals
、like
和in
表达式。n
- ngram大小size_of_bloom_filter_in_bytes
- Bloom过滤器的大小以字节为单位(可以在这里使用较大的值,例如256或512,因为它可以很好地压缩)。number_of_hash_functions
- 在Bloom过滤器中使用的哈希函数的数量。random_seed
- Bloom过滤器哈希函数的种子。
-
tokenbf_v1(size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)
与
ngrambf_v1
相同,但存储标记而不是语词。标记是由非字母数字字符分隔的序列。 -
bloom_filter([false_positive])
- 存储指定列的Bloom过滤器。可选的
false_positive
参数是从过滤器接收到假阳性响应的概率。可能的值:(0,1)。默认值:0.025。
支持的数据类型:Int*
, UInt*
, Float*
, Enum
, Date
, DateTime
,String
,FixedString
,Array
,LowCardinality
, Nullable
。
可以使用它的函数有:equals
,notEquals
,in
,notIn
,has
。
INDEX sample_index (u64 * length(s)) TYPE minmax GRANULARITY 4
INDEX sample_index2 (u64 * length(str), i32 + f64 * 100, date, str) TYPE set(100) GRANULARITY 4
INDEX sample_index3 (lower(str), str) TYPE ngrambf_v1(3, 256, 2, 0) GRANULARITY 4
函数支持
WHERE
子句中的条件包含调用函数对列操作。如果该列是索引的一部分,则ClickHouse在执行函数时尝试使用该索引。ClickHouse支持不同函数子集使用索引。
set
索引可以用于所有函数。其他索引的函数子集如下表所示。
Function (operator) / Index | primary key | minmax | ngrambf_v1 | tokenbf_v1 | bloom_filter |
---|---|---|---|---|---|
equals (=, ==) | ✔ | ✔ | ✔ | ✔ | ✔ |
notEquals(!=, <>) | ✔ | ✔ | ✔ | ✔ | ✔ |
like | ✔ | ✔ | ✔ | ✗ | ✗ |
notLike | ✔ | ✔ | ✔ | ✗ | ✗ |
startsWith | ✔ | ✔ | ✔ | ✔ | ✗ |
endsWith | ✗ | ✗ | ✔ | ✔ | ✗ |
multiSearchAny | ✗ | ✗ | ✔ | ✗ | ✗ |
in | ✔ | ✔ | ✔ | ✔ | ✔ |
notIn | ✔ | ✔ | ✔ | ✔ | ✔ |
less (<) | ✔ | ✔ | ✗ | ✗ | ✗ |
greater (>) | ✔ | ✔ | ✗ | ✗ | ✗ |
lessOrEquals (<=) | ✔ | ✔ | ✗ | ✗ | ✗ |
greaterOrEquals (>=) | ✔ | ✔ | ✗ | ✗ | ✗ |
empty | ✔ | ✔ | ✗ | ✗ | ✗ |
notEmpty | ✔ | ✔ | ✗ | ✗ | ✗ |
hasToken | ✗ | ✗ | ✗ | ✔ | ✗ |
具有小于ngram大小的常量参数的函数不能被ngrambf_v1
用于查询优化。
Bloom过滤器可能有假阳性匹配,因此ngrambf_v1
、tokenbf_v1
和bloom_filter
索引不能用于优化预期函数结果为假的查询,例如:
- 可以优化:
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')
并发数据访问
对于并发表访问,我们使用多版本控制。换句话说,当同时读取和更新一个表时,将从当前查询时的一组数据片段读取数据。没有长锁。插入不会妨碍读操作。
从表中读取数据会自动并行化。
列和表的TTL
决定值的生存周期。
可以为整个表和每个单独的列设置TTL
子句。
表级TTL
还可以指定逻辑在磁盘和卷之间自动移动数据。
表达式必须计算到Date
或DateTime
数据类型。
示例:
TTL time_column
TTL time_column + interval
要定义interval
,请使用时间间隔操作符。
TTL date_time + INTERVAL 1 MONTH
TTL date_time + INTERVAL 15 HOUR
列TTL
当列中的值过期时,ClickHouse用列数据类型的默认值替换它们。如果数据片段中的所有列值都过期,ClickHouse将从文件系统中的数据片段中删除这一列。
TTL
子句不能用于关键列。
创建一个使用TTL
的表
CREATE TABLE example_table
(
d DateTime,
a Int TTL d + INTERVAL 1 MONTH,
b Int TTL d + INTERVAL 1 MONTH,
c String
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(d)
ORDER BY d;
添加TTL
到已有的表的列中
ALTER TABLE example_table
MODIFY COLUMN
c String TTL d + INTERVAL 1 DAY;
修改列的TTL
ALTER TABLE example_table
MODIFY COLUMN
c String TTL d + INTERVAL 1 MONTH;
表的TTL
表可以有一个用于删除过期行的表达式,以及多个用于在磁盘或卷之间自动移动数据片段的表达式。当表中的行过期时,ClickHouse删除所有相应的行。对于数据片段移动功能,数据片段的所有行都必须满足移除表达式的条件。
TTL expr [DELETE|TO DISK 'aaa'|TO VOLUME 'bbb'], ...
TTL规则的类型可以遵循每个TTL表达式。它影响一旦表达式被满足时的操作(达到当前时间):
DELETE
- 删除过期行(默认操作);TO DISK 'aaa'
- 将数据片段移至磁盘aaa
处;TO VOLUME 'bbb'
- 将数据片段移至磁盘bbb
处。
示例:
创建一个使用TTL的表
CREATE TABLE example_table
(
d DateTime,
a Int
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(d)
ORDER BY d
TTL d + INTERVAL 1 MONTH [DELETE],
d + INTERVAL 1 WEEK TO VOLUME 'aaa',
d + INTERVAL 2 WEEK TO DISK 'bbb';
改变表的TTL
ALTER TABLE example_table
MODIFY TTL d + INTERVAL 1 DAY;
移除数据
当ClickHouse合并数据片段时,将删除TTL
过期的数据。
当ClickHouse看到数据过期时,它会执行计划外合并。要控制这种合并的频率,可以设置merge_with_ttl_timeout
。如果值太低,它将执行许多计划外合并,这可能会消耗大量资源。
如果在合并之间执行SELECT
查询,则可能会得到过期数据。要避免这种情况,SELECT
之前使用OPTIMIZE
查询。
使用多块设备进行数据存储
概述
MergeTree
系列表引擎可以在多块设备上存储数据。例如,当某个表的数据被隐式地分为“热”和“冷”时,它可能很有用。最近的数据经常请求,而且只需要很少的空间。相反,厚尾历史数据很少被请求。如果有多个磁盘可用,“热”数据可能位于快速磁盘上(例如NVMe SSD
或内存中),而“冷”数据位于相对较慢的磁盘上(例如HDD)。
数据片段是MergeTree
引擎表的最小可移动单元。属于一个片段的数据存储在一个磁盘上。数据片段可以在后台的磁盘之间移动(根据用户设置),也可以通过ALTER
查询进行移动。
术语
Disk
— 挂载到文件系统的块设备。Default disk
— 在服务器设置中指定的磁盘存储路径。Volume
— 等价于磁盘的有序集合(类似于JBOD)。Storage policy
— 一组卷和在卷之间移动数据的规则。
配置
磁盘、卷和存储策略应该在<storage_configuration>
标签内声明,要么在主文件config.xml
中声明,要么在config.d
目录中的不同的文件中声明。
配置结构:
<storage_configuration>
<disks>
<disk_name_1> <!-- disk name -->
<path>/mnt/fast_ssd/clickhouse/</path>
</disk_name_1>
<disk_name_2>
<path>/mnt/hdd1/clickhouse/</path>
<keep_free_space_bytes>10485760</keep_free_space_bytes>
</disk_name_2>
<disk_name_3>
<path>/mnt/hdd2/clickhouse/</path>
<keep_free_space_bytes>10485760</keep_free_space_bytes>
</disk_name_3>
...
</disks>
...
</storage_configuration>
标签:
<disk_name_N>
— 磁盘的名称。所有磁盘的名称必须不同。path
— 服务器存储数据(数据和影子文件夹)的路径,应该以“/”
结束。keep_free_space_bytes
— 要保留的空闲磁盘空间的大小。
磁盘定义的顺序并不重要。
存储策略配置标记:
<storage_configuration>
...
<policies>
<policy_name_1>
<volumes>
<volume_name_1>
<disk>disk_name_from_disks_configuration</disk>
<max_data_part_size_bytes>1073741824</max_data_part_size_bytes>
</volume_name_1>
<volume_name_2>
<!-- configuration -->
</volume_name_2>
<!-- more volumes -->
</volumes>
<move_factor>0.2</move_factor>
</policy_name_1>
<policy_name_2>
<!-- configuration -->
</policy_name_2>
<!-- more policies -->
</policies>
...
</storage_configuration>
标签:
policy_name_N
— 策略的名字。策略名称必须是唯一的。volume_name_N
— 卷名。卷名必须是惟一的。disk
— 卷中的一个磁盘。max_data_part_size_bytes
— 可存储在任何卷的磁盘上的数据片段的最大大小。move_factor
— 当可用空间数量低于此因子时,数据将自动开始移动到下一个卷(默认情况下为0.1)。
配置示例:
<storage_configuration>
...
<policies>
<hdd_in_order> <!-- policy name -->
<volumes>
<single> <!-- volume name -->
<disk>disk1</disk>
<disk>disk2</disk>
</single>
</volumes>
</hdd_in_order>
<moving_from_ssd_to_hdd>
<volumes>
<hot>
<disk>fast_ssd</disk>
<max_data_part_size_bytes>1073741824</max_data_part_size_bytes>
</hot>
<cold>
<disk>disk1</disk>
</cold>
</volumes>
<move_factor>0.2</move_factor>
</moving_from_ssd_to_hdd>
</policies>
...
</storage_configuration>
在给定的示例中,hdd_in_order
策略实现了循环方法。因此,该策略只定义一个卷(单个卷),数据片段按循环顺序存储在所有磁盘上。如果系统上安装了多个类似的磁盘,但是没有配置RAID,那么这种策略非常有用。请记住,每个单独的磁盘驱动器都是不可靠的,您可能希望使用3或更多的复制因子来补偿它。
如果系统中有不同类型的可用磁盘,则可以使用moving_from_ssd_to_hdd
策略。卷hot
由一个SSD磁盘(fast_ssd
)组成,可存储在该卷上的数据片段的最大大小为1GB。所有大于1GB的数据片段都将直接存储在cold
卷中,该卷包含一个HDD磁盘disk1
。
此外,一旦磁盘fast_ssd
被填充超过80%,数据将通过后台进程传输到disk1
。
存储策略中的卷枚举顺序非常重要。 一旦一个卷被填满,数据就会被移动到下一个卷。 磁盘枚举的顺序也很重要,因为数据依次存储在它们上面。
当创建一个表时,可以应用其中一个配置的存储策略:
CREATE TABLE table_with_non_default_policy (
EventDate Date,
OrderID UInt64,
BannerID UInt64,
SearchPhrase String
) ENGINE = MergeTree
ORDER BY (OrderID, BannerID)
PARTITION BY toYYYYMM(EventDate)
SETTINGS storage_policy = 'moving_from_ssd_to_hdd'
默认的存储策略意味着只使用一个卷,它只包含<path>
中给定的一个磁盘。
一旦创建了表,就不能更改它的存储策略。
详情
在合并树表的情况下,数据以不同的方式到达磁盘:
- 作为插入(
INSERT
查询)的结果。 - 在后台合并和修改。
- 从另一个副本下载时。
- 作为分区冻结的结果
ALTER TABLE … FREEZE PARTITION
在所有这些情况下,除了修改和分区冻结外,按照给定的存储策略数据片段存储在卷和磁盘上:
- 1.第一个卷(按照定义的顺序)有足够的磁盘空间来存储一个数据片段(
unreserved_space > current_part_size
),并且允许存储给定大小的数据片段(max_data_part_size_bytes > current_part_size
)。 - 2.在这个卷中,选择与用于存储前一个数据块的磁盘相同的那个磁盘,这个磁盘的空闲空间大于数据片段大小(
unreserved_space - keep_free_space_bytes > current_part_size
)。
本质上,修改和分区冻结利用了硬链接。不同磁盘之间的硬链接不支持,因此在这种情况下导致数据片段存储在与初始部分相同的磁盘上。
在后台,根据配置文件中声明的卷的顺序,基于空闲空间量(move_factor
参数)在卷之间序移动数据片段。
数据从不从最后一个传输到第一个。可以使用系统表系统system.part_log
(字段type = MOVE_PART
) 和system.parts
(字段path
和disk
)监控后台移动。此外,详细信息可以在服务器日志中找到。
用户可以使用ALTER TABLE … MOVE PART|PARTITION … TO VOLUME|DISK …
查询强制将数据片段或分区从一个卷移动到另一个卷,需要考虑后台操作的所有限制。查询自己发起一个移动,并不需要等待后台操作完成。如果没有足够的可用空间,或者没有满足任何必要的条件,用户将收到一条错误消息。
移动数据不会干扰数据复制。因此,可以为同一个表的不同副本指定不同的存储策略。
在完成后台合并和修改之后,只有在一定的时间之后(old_parts_lifetime
)才会删除旧的数据片段。
在此期间,它们不会被移动到其他卷或磁盘。
因此,在最终删除这些数据片段之前,仍然要考虑它们以评估所占用的磁盘空间。
「如果这篇文章对你有用,请支持一下哦」
如果这篇文章对你有用,请支持一下哦
使用微信扫描二维码完成支付