clickhouse 数据存储 相关概念
- granule
granule
granule:颗粒的意思。
颗粒由很多行组成。一个颗粒对应一个索引项。clickhouse为每个颗粒
建立一个索引,这也就是稀疏索引的名称的来源。
与 granule 大小相关的配置参数
index_granularity_bytes:满足多少字节大小 划分为一个颗粒
index_granularity:多少行数划分为一个颗粒
clickhouse 在磁盘上的数据存储位置
默认的存储路径是 /var/lib/clickhouse 目录
clickhouse 某个数据库下的某个表的数据 在磁盘上的存储路径。
假设默认的存储路径为 /var/lib/clickhouse
/var/lib/clickhouse/data/数据库名/表名
这个目录对应着一个表在磁盘上的数据存储。当然这个目录是一个软链接。
clickhouse 表的元数据 在磁盘上的存储路径。
/var/lib/clickhouse/metadata/数据库名/表名
注意:可复制表的元数据 还会在 zookeeper 之中存储一份。
clickhouse 数据存储 是列式存储 还是行式存储
列式存储。
在数据量比较小的情况下,一行的所有列都存储在磁盘上的一个文件之中。
数据量大的情况下,不同的列各自对应着磁盘上的一个文件。
具体的划分标准由具体的参数来控制。
clickhouse 数据存储路径 相关的配置参数
config.xml 配置文件之中的 path 参数,参数含义为 指定数据在磁盘上的存储位置。
clickhouse part 在磁盘上的表现形式
part 是磁盘上的一个目录,目录下的文件可能有以下这些
- checksums.txt:存储校验和,用来判断data_part 是否损坏
- columns.txt:存储字段的信息
- count.txt:存储这个data_part之中的数据行数
- default_compression_codec.txt:存储默认的压缩算法类型
- partition.dat :设置了分区键之后才会生成的文件,存储当前分区表达式生成的值
- minmax_[column].idx:设置了分区键之后才会生成的文件, 存储当前分区的分区键的原始列的最大值和最小值
*xxx.bin:存储数据的文件。前缀是字段名。数据的压缩文件 - xxx.mrk2/3:存储block 以及 granule 在文件之中的偏移量。数据的标记文件
- primary.idx: 索引文件
primary.idx
存储每一个granule之中,索引列的最小值的相关信息。
xxx.[mrk2]/[mrk3] 标记文件
存储每一个索引对应的数据在物理文件上的对应位置
clickhouse 表数据在磁盘上的组织关系
由表的元数据信息 primary key 字段 , 以及 order by 的字段来决定表之中的数据在磁盘上的存储方式。
clickhouse表数据是排序过后在磁盘上进行存储的,所依据的排序规则为 表的 primary key 字段 和 order by 字段, 并以正向排序。clickhouse 会根据表的index_granularity设置值,例如index_granularity 默认值为 8192,那么为排序后的数据,按照8192行,生成一个索引记录,索引记录之中存储的是 每一个颗粒之中的第一行所包含的主键字段的值。clickhouse 还会对每个列生成一个mrk[2]/[3] 类型的文件,文件之中的每一行记录表示存储的是 此字段在某个granule之中第一行在物理文件之中的具体位置,用以读取数据时定位。
注意:
1: primary key 之中的字段 一定是 order by 字段的前缀。
2:mrk[2]/[3] 文件之中存储的数据是一个数组,数组的每个元素是 [block_offset, granule_offset]
block_offset 则是列式存储 而产生的block概念,用以表示block的具体位置。
granule_offset 是指 某个granule之中第一行在xxx.bin文件之中的具体位置。
压缩算法
注意:压缩算法所针对的对象是表之中的列,所以可以对一个表的不同的字段设置压缩算法。
查看表的某个字段所使用的压缩算法
select compression_codec from system.columns where table = '表名'
注意:如果输出的值为空,那么应用的是默认的压缩算法,默认的压缩算法是lz4。
如何修改表字段所使用的压缩算法
使用 alter table 语句修改表的字段结构,来修改表字段所使用的压缩算法。
监控
磁盘使用量监控
建议使用 node-exporter + prometheus 进行监控。
查看各个表的 分区数 主键索引的大小 未压缩后的数据大小 压缩后的数据大小 在磁盘上占据的总大小
注:在磁盘上占据的总大小 约等于 压缩后的数据大小 + 主键索引的大小
select
database,
table,
count(*) as partition_num, // 分区数
sum(primary_key_bytes_in_memory) as primary_key_bytes_in_memory, // 主键索引的大小
sum(data_uncompressed_bytes) as data_uncompressed_bytes, // 未压缩的数据量大小
sum(data_compressed_bytes) as data_compressed_bytes, // 压缩后的数据量大小
sum(bytes_on_disk) as bytes_on_disk // 在磁盘上占据的总大小
from (
select
database,
table,
partition,
sum(primary_key_bytes_in_memory) as primary_key_bytes_in_memory,
sum(data_uncompressed_bytes) as data_uncompressed_bytes,
sum(data_compressed_bytes) as data_compressed_bytes,
sum(bytes_on_disk) as bytes_on_disk
from
clusterAllReplicas('default', 'system.parts')
where active = 1
group by
database,
table,
partition
) a
group by database, table
order by bytes_on_disk desc
各节点不活跃的part 数目 以及数据量
select
hostName,
count(*) as num,
sum(bytes_on_disk) as bytes_on_disk
from (
select
hostName() as hostName,
bytes_on_disk
from
clusterAllReplicas('default', 'system.parts')
where active = 0
)
group by
hostName
小技巧
查看某个压缩数据文件的信息
clickhouse-compressor --stat xxx.bin
这样会显示每一个块的未压缩前的数据量大小,以及压缩后的数据量大小。
问题
2:为什么索引文件之中不直接存储对应字段的物理位置,而是拆分出一个个mrk类型的文件进行存储呢?
一个原因是因为索引文件会被加载到内存之中,索引文件越小,则占据的内存越小。毕竟是针对大数据量的场景。另一个方面,针对列裁剪输出,如果只需输出两列,那么可能只需要加载此两列所对应的mrk文件,也是减小了内存的消耗。