根据监控日志的特点,我们对 message 字段添加了 tokenbf_v1 索引。

tokenbf_v1 索引的工作原理如下:

  1. 对于每个列,ClickHouse 会生成一个 tokenbf_v1 过滤器。
  2. 当插入新数据行时,ClickHouse 会将所有列的值分成单独的标记,并将这些标记添加到相应的 tokenbf_v1 过滤器中。
  3. 在执行查询时,如果查询包含 WHERE 子句并使用了支持 tokenbf_v1 索引的列,则 ClickHouse 将从 tokenbf_v1 过滤器中获取所有相关的标记,并检查这些标记是否存在于该列的值中。如果标记存在于 tokenbf_v1 过滤器中但不存在于列的值中,则该行数据不符合查询条件,因此可以快速被过滤掉。

由于 tokenbf_v1 索引使用了布隆过滤器算法,因此其大小相对较小,可以有效地减少文件 I/O 和内存开销。同时,它还支持多列索引和复合索引,使得在复杂查询中也能够实现高效率的数据访问。

在Bloom filter设置之前需要一个额外的参数,即要索引的ngram的大小。一个ngram是长度为n的任何字符串,比如如果n是4,A short string会被分割为'A sh', ' sho', 'shor', 'hort', 'ort ', 'rt s', 't st', ' str', 'stri', 'trin', 'ring'。这个索引对于文本搜索也很有用,特别是没有单词间断的语言,比如中文。

布隆过滤器参数与效果的查询:https://hur.st/bloomfilter/

image.png

根据评估不过过滤器的大小我们设置为 30720 ,hash次数 3;

加了 tokenbf_v1 索引的表结构:

CREATE TABLE monitor.qunhe_log
(
    `timestamp` DateTime64(3, 'Asia/Shanghai'),
    `hostGroup` String,
    `ip` String,
    `podname` String,
    `level` String,
    `cls` String,
    `behavior` String,
    `message` String,
    `id` String DEFAULT '',
    INDEX message_index message TYPE tokenbf_v1(30720, 3, 0) GRANULARITY 1
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/qunhelog', '{replica}')
PARTITION BY toYYYYMMDD(timestamp)
ORDER BY (level, hostGroup, podname, ip, timestamp)
SETTINGS storage_policy = 'hdd0_hdd1_only', index_granularity = 8192 

支持的 函数:

虽然支持 like 函数,但是 像 like '%hunter%' 这样的查询显然是不会走索引的,因为clickhouse 不知道 hunter 是不是一个词。当使用 like 'hunter' 时等同于 = 方法,就会索引;

因此为了走索引,hasToken() 是一个比较常用的方法,使用 hasToken('hunter') 也就意味着告诉 clickhouse 'hunter' 是一个词,可以走分词索引,过滤结果是可靠的;
image.png

查询索引效果

以查询某服务最近10分钟 taskId=L3D607S41ENDPFVB2EAUWI5ACLUF3P3XG888 为例:

模糊查询
SELECT 
  ip, 
  timestamp, 
  podname, 
  cls, 
  message, 
  level, 
  behavior, 
  hostGroup 
FROM 
  monitor.qunhe_log
WHERE 
  timestamp >= '2023-05-30 19:50:00.000' 
  AND timestamp < '2023-05-30 20:00:00.000' 
  AND hostGroup IN (
    '*********隐私信息**********'
  ) 
  AND message LIKE '%L3D607S41ENDPFVB2EAUWI5ACLUF3P3XG888%' 
ORDER BY 
  (timestamp,) DESC 
LIMIT 
  50 OFFSET 0

耗时10s
image.png
查看索引过滤效果
image.png

  • 用like查询:耗时 10S,读取了 2.47 million rows 行数据
分词查询
SELECT 
  ip, 
  timestamp, 
  podname, 
  cls, 
  message, 
  level, 
  behavior, 
  hostGroup 
FROM 
  monitor.qunhe_log 
WHERE 
  timestamp >= '2023-05-30 19:50:00.000' 
  AND timestamp < '2023-05-30 20:00:00.000' 
  AND hostGroup IN (
    '*********隐私信息**********'
  ) 
  AND hasToken(message,'L3D607S41ENDPFVB2EAUWI5ACLUF3P3XG888') 
ORDER BY 
  (timestamp,) DESC 
LIMIT 
  50 OFFSET 0

耗时0.28s
image.png
查看索引过滤效果
image.png

  • 用 hasToken查询:耗时 0.24S,读取了102.40 thousand rows 行数据;
  • 分词索引筛掉了 95% 以上的数据,效果非常好,可以明显看到排掉了6个parts,查询速率明显提升。

总结

  1. 需要注意的是虽然很多查询方法支持tokenbf_v1索引查询,但是不一定能走索引,比如 like 查询;
  2. 使用 TokenBF_v1 索引需要考虑到索引大小、误判率、创建时间和资源消耗、更新和删除的限制,以及查询优化和版本兼容性等方面的因素。在实际应用中,根据数据集规模和查询需求做出合理的选择和平衡。

赵栩彬
358 声望600 粉丝

愿,你在遭受打击时,记起你的珍贵,抵抗恶意;