为啥读取“从 1 个范围内读取 2972​​ 个标记”比读取“从 126 个范围内读取 238 个标记”更快

Posted

技术标签:

【中文标题】为啥读取“从 1 个范围内读取 2972​​ 个标记”比读取“从 126 个范围内读取 238 个标记”更快【英文标题】:Why does reading '2972 marks from 1 ranges' faster than reading '238 marks to read from 126 ranges' of the same data为什么读取“从 1 个范围内读取 2972​​ 个标记”比读取“从 126 个范围内读取 238 个标记”更快 【发布时间】:2021-12-24 18:50:59 【问题描述】:

我有下表

CREATE TABLE slice (
    `uid` Int64,
    `start` DateTime64(3),
    ....
)
ENGINE = ReplacingMergeTree()
PARTITION BY toMonth(start)
ORDER BY (start, uid)
SETTINGS index_granularity = 8192

我需要通过uids在给定的时间段内选择一些切片

SELECT uid, start, ...
FROM slice
WHERE start => '2021-12-23 00:00:00.000'
  AND start <  '2021-12-24 00:00:00.000'
  AND uid IN (...uids_list...)

uids_list 包含数百个 uid

调试日志说

(SelectExecutor): Selected 1 parts by partition key, 1 parts by primary key, 2972 marks by primary key, 2972 marks to read from 1 ranges
executeQuery: Read 24306405 rows, 405.29 MiB in 0.371851802 sec., 65365838 rows/sec., 1.06 GiB/sec.

返回了大约 1000 行。 好的,我添加了数据跳过索引以减少要读取的行数

ALTER TABLE slice ADD INDEX slice_uid_idx(uid) TYPE set(0) GRANULARITY 1;

执行相同的查询给出

(SelectExecutor): Index `slice_uid_idx` has dropped 2734 / 2972 granules.
(SelectExecutor): Selected 1 parts by partition key, 1 parts by primary key, 2972 marks by primary key, 238 marks to read from 126 ranges
executeQuery: Read 1948986 rows, 234.64 MiB in 1.328573644 sec., 1466976 rows/sec., 176.61 MiB/sec.

问题是

    为什么请求变慢了:0.37 vs 1.32 秒,尽管读取行数从24 306 405 减少到1 948 986 在这种情况下range 是什么意思? (范围数量从1增加到126

【问题讨论】:

什么样的数据库?看起来像mysql?什么版本? 【参考方案1】:

PARTITION BY toMonth(start)

这是不正确的。您将不同年份的数据放入同一个分区。 应该是toYYYYMM(start)

start DateTime64(3) ORDER BY(开始,uid)

这是不正确的。在像 DateTime64(3) 这样高的基值之后将任何东西放在第二位是没有意义的。

尝试使用这里的示例https://github.com/ClickHouse/ClickHouse/issues/33056


添加索引 slice_uid_idx(uid) TYPE set(0) GRANULARITY 1

你只需让 CH 读取相同的数据两次。

类型集(0) 粒度 1

字面意思是从uid 列中取出所有数据,将其从垂直结构转换为水平结构并将结果放入skip_index。

您的跳过索引比原始列更难处理。但是SELECT查询处理完索引后,查询也需要读取列。


AFAIK TYPE set(0) 通常几乎没有意义。在一些非常罕见的情况下,它对索引 GRANULARITY >= 10 的低基数列有意义。


对于具有高基数的列(我从列名 uid 猜测),使用带有 GRANULARITY 2 / 4 的bloom_filter 跳过索引是有意义的。

【讨论】:

我删除了旧的跳过索引并添加了一个bloom_filter 类型的新索引。使用新索引的查询仍然比没有任何跳过索引的查询要慢) 日志中的range 是什么? 提供您如何创建布隆过滤器的信息 ALTER TABLE slice ADD INDEX slice_uid_idx(uid) TYPE bloom_filter GRANULARITY 4 试试bloom_filter(0.1) granularity 10 or 50 range - 连续的颗粒范围

以上是关于为啥读取“从 1 个范围内读取 2972​​ 个标记”比读取“从 126 个范围内读取 238 个标记”更快的主要内容,如果未能解决你的问题,请参考以下文章

为啥nodejs文件系统先读取控制台再读取文件

为啥在 C# 中读取的原始磁盘从稍微偏移的偏移中读取?

为啥读取“从 1 个范围内读取 2972​​ 个标记”比读取“从 126 个范围内读取 238 个标记”更快

在 python 中,为啥从数组读取比从列表读取慢?

为啥我无法读取这些数据帧

为啥不使用指针读取结构?