文章目录

  • Clickhouse的应用场景
  • 1、索引介绍
    • 1.1、主要索引
    • 1.2、二级索引
    • 1.2.1、数据跳过索引
  • 2、一级索引优化
    • 2.1、CK的sql的优化方向
    • 2.2、验证过程
      • 结论:
    • 2.3、如何修改排序键
      • 总结:
    • 3、二级索引(跳数索引)
  • 知识点
    • 查看Clickhouse的执行计划
    • 查询中的主键和索引
    • 选择主键
      • 分区
    • 将索引用于部分单调的主键

Clickhouse的应用场景

绝大多数是读请求
数据以相当大的批次(> 1000行)更新,而不是单行更新;或者根本没有更新。
已添加到数据库的数据不能修改。
对于读取,从数据库中提取相当多的行,但只提取列的一小部分。
宽表,即每个表包含着大量的列
查询相对较少(通常每台服务器每秒查询数百次或更少)
对于简单查询,允许延迟大约50毫秒
列中的数据相对较小:数字和短字符串(例如,每个URL 60个字节)
处理单个查询时需要高吞吐量(每台服务器每秒可达数十亿行)
事务不是必须的
对数据一致性要求低
每个查询有一个大表。除了他以外,其他的都很小。
查询结果明显小于源数据。换句话说,数据经过过滤或聚合,因此结果适合于单个服务器的RAM中

1、索引介绍

1.1、主要索引

通过按主键对数据进行物理排序,可以提取其特定值或值范围的数据,延迟低,小于几十毫秒。(也就是下文分析的内容)

  • 改进数据压缩。
    ClickHouse按主键对数据进行排序,因此一致性越高,压缩效果越好。

1.2、二级索引

与其他数据库管理系统不同,ClickHouse 中的二级索引不指向特定的行或行范围。相反,它们允许数据库提前知道某些数据部分中的所有行都与查询筛选条件不匹配,并且根本不读取它们,因此它们被称为数据跳过索引

1.2.1、数据跳过索引

对于系列(**MergeTree)中的表,可以指定数据跳过索引。

这些索引聚合有关块上指定表达式的一些信息,块由粒状组成(粒度的大小是使用表引擎中的设置指定的)。然后,这些聚合用于查询,通过跳过查询无法满足的大块数据块来减少要从磁盘读取的数据量。
ck默认索引粒度:8192 = 1024 * 8
也就是查看表中自动生成的 SETTINGS index_granularity = 8192

2、一级索引优化

总结一下可以优化的地方:

  1. 一级索引
  2. 二级索引
  3. 可以根据现有字段直接分区 PARTITION BY 字段
  4. 可以根据函数分区 PARTITION BY toYYYYMM(EventDate) toYYYYMM是函数,(eventDate)属性需要是Data类型

目前业务中可优化的选项:

  • 日期尽量都使用date类型,目前使用的是Int64(待研究)
    按月分区的键仅允许读取那些包含适当范围的日期的数据块。在这种情况下,数据块可能包含许多日期(最多一整个月)的数据。在块中,数据按主键排序,主键可能不包含日期作为第一列。因此,使用仅具有未指定主键前缀的日期条件的查询将导致读取的数据多于单个日期的数据

  • 针对常查询的字段,使用ORDER BY(c1, c2, xxx) 创建索引,括号中从左到右的优先级会导致查询效率有别;
    注意:

  • 有Nullable的字段,不能创建索引,一些查询字段不要设置Nullable()

2.1、CK的sql的优化方向

  • 分区,原则是尽量把经常一起用到的数据放到相同区(也可以根据where条件来分区),如果一个区太大再放到多个区

大量分区是一种常见的误解。这将导致严重的负面性能影响,包括服务器启动速度慢、插入查询速度慢和选择查询速度慢。表的建议分区总数在1000以下。请注意,分区并不是为了加速SELECT查询(按键排序足以使范围查询变得快速)。分区用于数据操作(删除分区等)

  • 主键(索引,即排序)order by字段选择: 就是把where 里面肯定有的字段加到里面,where 中一定有的字段放到第一位,注意字段的区分度适中即可 区分度太大太小都不好,因为ck的索引时稀疏索引,采用的是按照固定的粒度抽样作为实际的索引值,不是mysql的二叉树,所以不建议使用区分度特别高的字段。

    • 值得一提的是,通常只有在使用SummingMergeTreeAggregatingMergeTree 的时候,才需要同时设置ORDER BYPRIMARY KEY。显式的设置PRIMARY KEY,是为了将主键和排序键设置成不同的值,是进一步优化的体现。(PS:因为我们使用的引擎并不是上面几种,省略PRIMARY KEY。默认情况下,主键与排序键(ORDER BY)相同,所以通常直接使用ORDER BY代为指定主键。)
    • 创建索引尽量选择基数大的,也就是重复相对较多的(因为是稀疏索引) 在mysql中正好是相反的

也就是说,业务中经常需要查询的字段需要放在order by靠前位置。

举个例子:统计了一下业务中的风险实际表的查询sql语句,按照优先级高到低整理出来的字段

create_time , score , vehicle_id,id

也就是说需要这么写:

order  by(create_time , score , vehicle_id,id).

官网原话:长主键会对插入性能和内存消耗产生负面影响 , 但主键中的额外列不会影响查询期间的ClickHouse 性能。

2.2、验证过程

下面只取两个字段create_time,id来验证

创建4个表,插入相同的204w条数据。(开启trace log,查看查询从机器上面读取的条数,不会弄),暂时以执行时间来参考。

4个表的排序情况为:

表1: ORDER BY (id)

表2: ORDER BY (id,create_time)

表3: ORDER BY (create_time)

表6: ORDER BY (create_time,id) //推荐

CREATE TABLE hf_ai_hbs.t_base_data_record1
(`id` Int64 COMMENT '事件ID',`time` Int64 COMMENT '数据记录时间,单位毫秒',...省略了一些字段...`create_time` Int64 DEFAULT 0 COMMENT '创建时间,毫秒时间戳,设备事件时间'
)
ENGINE = ReplacingMergeTree
ORDER BY id
SETTINGS index_granularity = 8192
CREATE TABLE hf_ai_hbs.t_base_data_record2
(`id` Int64 COMMENT '事件ID',`time` Int64 COMMENT '数据记录时间,单位毫秒',...省略了一些字段...`create_time` Int64 DEFAULT 0 COMMENT '创建时间,毫秒时间戳,设备事件时间'
)
ENGINE = ReplacingMergeTree
ORDER BY (id,create_time)
SETTINGS index_granularity = 8192

其余两个字段、属性、数据相同,只是order by里面的内容不同。

-- 执行十次-- SQL 1
SELECT * FROM t_risk_event_record1 trer WHERE id=171972290612035584;   --执行十次,单位ms:  平均耗时:16.0
SELECT * FROM t_risk_event_record2 trer WHERE id=171972290612035584;   --  平均耗时:19.0
SELECT * FROM t_risk_event_record3 trer WHERE id=171972290612035584;   --  平均耗时:23
SELECT * FROM t_risk_event_record6 trer WHERE id=171972290612035584;   --  平均耗时:25.0
-- SQL 2
SELECT * from t_risk_event_record1 trer order by id DESC LIMIT 3;   --平均耗时:35
SELECT * from t_risk_event_record2 trer order by id DESC LIMIT 3;   --56,50,61,52,57,56,54,55,55,54             平均耗时:55
SELECT * from t_risk_event_record3 trer order by id DESC LIMIT 3;   --平均耗时:138
SELECT * from t_risk_event_record6 trer order by id DESC LIMIT 3;   --平均耗时:148
-- SQL 3
SELECT * from t_risk_event_record1 trer order by create_time DESC LIMIT 3;  --执行十次,平均耗时:125.0    129,122,121,125,133,120,119,125,131,125
SELECT * from t_risk_event_record2 trer order by create_time DESC LIMIT 3;  --执行十次,平均耗时:135.0    146,153,130,140,127,130,125,124,130,145
SELECT * from t_risk_event_record3 trer order by create_time DESC LIMIT 3;  --执行十次,平均耗时:54.0     50,43,56,56,57,54,65,50,61,48
SELECT * from t_risk_event_record6 trer order by create_time DESC LIMIT 3;  --执行十次,平均耗时:53.0     56,57,40,63,53,54,47,44,57,59
-- SQL 4
SELECT * from t_risk_event_record1 trer order by create_time,id DESC LIMIT 3;   --执行十次,平均耗时:120
SELECT * from t_risk_event_record2 trer order by create_time,id DESC LIMIT 3;   --执行十次,平均耗时:135
SELECT * from t_risk_event_record3 trer order by create_time,id DESC LIMIT 3;   --执行十次,平均耗时:100
SELECT * from t_risk_event_record6 trer order by create_time,id DESC LIMIT 3;   --执行十次,平均耗时:65
-- SQL 5
SELECT * from t_risk_event_record1 trer order by id,create_time DESC LIMIT 3;   --执行十次,平均耗时:45
SELECT * from t_risk_event_record2 trer order by id,create_time DESC LIMIT 3;   --执行十次,平均耗时:50
SELECT * from t_risk_event_record3 trer order by id,create_time DESC LIMIT 3;   --执行十次,平均耗时:140
SELECT * from t_risk_event_record6 trer order by id,create_time DESC LIMIT 3;   --执行十次,平均耗时:150
-- SQL 6
SELECT * from t_risk_event_record1 trer WHERE (create_time>=1649324393021) AND (create_time <=1649324595021);   --执行十次,平均耗时:35
SELECT * from t_risk_event_record2 trer WHERE (create_time>=1649324393021) AND (create_time <=1649324595021);   --执行十次,平均耗时:34
SELECT * from t_risk_event_record3 trer WHERE (create_time>=1649324393021) AND (create_time <=1649324595021);   --执行十次,平均耗时:25
SELECT * from t_risk_event_record6 trer WHERE (create_time>=1649324393021) AND (create_time <=1649324595021);   --执行十次,平均耗时:20

结论:

  • sql1中,根据id查询,四种情况的查询效率相差不大
  • sql2中,根据id排序,因为建表时候,表1和表2排序键前面都是id,所以查询效率明显比表3、6要好。sql3中情况刚好相反
  • sql4中,表3、4的order by 排序键的create_time比较靠前,所以,他们的查询效率相对表1、2快了两倍多,sql5中相反。
  • sql6中,表3、4的order by 排序键的create_time比较靠前
  • 除了在sql2根据id排序的情况下,表6排序键的查询效率在这几个sql中都是比较快的

2.3、如何修改排序键

这里有几个问题

  1. 看这段话排序键的AlTER对象是新增的列才行,而不能像二级索引(跳数索引)一样可以随意更改成其他字段

排序键可以更新除了主键外的键。比如主键是id,可以从order by(id,name)更新成(id,age);但是更新的字段必须是新增的列

  1. 更改完排序键后索引文件应该没有任何变化,这时我们设置的新排序规则应该对查询没有任何作用?
  2. 如何才能根据新设置的排序键进行重建索引?
  3. ClickHouse 在什么情况下会自动重建索引?

修改排序键 sql 如下:

ALTER TABLE 数据库.表名
ADD COLUMN age Int16,
MODIFY ORDER BY (id, age)

总结:

  • 针对常查询的字段,使用ORDER BY(c1, c2, xxx) 创建索引,而且业务中经常需要查询的字段需要放在order by靠前位置。
  • 长主键会对插入性能和内存消耗产生负面影响 , 但主键中的额外列不会影响查询期间的ClickHouse 性能。

参考链接:
clickhouse使用经验总结:https://blog.csdn.net/TankRuning/article/details/111317090

clickhouse的索引结构和查询优化:https://blog.csdn.net/h2604396739/article/details/86172756

从Clickhouse执行计划看跳数/稀疏索引的效果:https://www.gbase8.cn/6596

Clickhouse orderby之谜:https://zhuanlan.zhihu.com/p/379848085

clickhouse 在order by非常慢的情况下优化:https://www.cnblogs.com/niutao/p/15313070.html

ClickHouse的执行计划以及优化策略:https://www.cnblogs.com/traditional/p/15264282.html

clickhouse分区操作实践:https://blog.csdn.net/m0_37813354/article/details/110847747

3、二级索引(跳数索引)

跳数索引:能够快速跳过无用的数据区间(跳跃粒度是我们设置的,默认8192)。有些区间可能不会被扫描到,提高我们查询的效率

类似布隆过滤器,所有跳数索引的原则都是“排除法”,即尽可能的排除那些一定不满足条件的索引粒度。

告诉我们某个数据一定不存在或者可能存在(注意:布隆过滤器是不能判断某条数据一定存在的,存在误报率 (false_positive) )。相比于传统的 List、Set、Map 等数据结构,布隆过滤器更高效、占用空间更少,因为不需要存储原始值,但是缺点是其返回的结果是概率性的,而不是确切的。

知识点

查看Clickhouse的执行计划

在前面加explain

注意其中的数据读取位置,分别是从原始的存储(ReadFromStorage),和预处理的源(ReadFromPreparedSource)。

explain SYNTAX 加sql语句,会输出优化的sql,如果和我们写的一样,说明我们的sql已经是最优的了

查询中的主键和索引

以主键为例。在这种情况下,排序和索引可以如下图示:(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] 取并集

上面的示例表明,使用索引始终比完全扫描更有效。

ClickHouse 不需要唯一的主键。可以使用相同的主键插入多行。

选择主键

主键中的列数没有显式限制。根据数据结构,您可以在主键中包含更多或更少的列。这可能:

  • 提高索引的性能。

如果主键为,则在满足以下条件时,添加另一列将提高性能:(a,b)``c

    • 有些查询在列上具有条件, 例如:c
    • 具有相同值的长数据范围(比长几倍)很常见。换句话说,添加另一列时,可以跳过相当长的数据范围。index_granularity (a,b)
  • 改进数据压缩。

ClickHouse按主键对数据进行排序,因此一致性越高,压缩效果越好。

  • 在CollapseingMergeTree和SummingMergeTree 引擎中合并数据部分时提供其他逻辑。在这种情况下,指定与主键不同的排序建是有意义的。

值得一提的是,通常只有在使用SummingMergeTreeAggregatingMergeTree 的时候,才需要同时设置ORDER BYPRIMARY KEY。

显式的设置**PRIMARY KEY,**是为了将主键和排序键设置成不同的值,是进一步优化的体现。

长主键会对插入性能和内存消耗产生负面影响 , 但主键中的额外列不会影响查询期间的ClickHouse 性能。

可以使用语法创建没有主键的表。在这种情况下,ClickHouse将按插入顺序存储数据。如果要在按查询插入数据时保存数据顺序,请设置max_insert_threads = 1ORDER BY tuple() INSERT ... SELECT

若要按初始顺序选择数据,请使用单线程查询。SELECT

分区

单个插入块的分区太多(超过100个)。该限制由“每个插入块的最大分区数”设置控制。大量分区是一种常见的误解。这将导致严重的负面性能影响,包括服务器启动速度慢、插入查询速度慢和选择查询速度慢。表的建议分区总数在1000以下。。请注意,分区并不是为了加速SELECT查询(按键排序足以使范围查询变得快速)。分区用于数据操作(删除分区等)

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。

按月分区的键仅允许读取那些包含适当范围的日期的数据块。在这种情况下,数据块可能包含许多日期(最多一整个月)的数据。在块中,数据按主键排序,主键可能不包含日期作为第一列。因此,使用仅具有未指定主键前缀的日期条件的查询将导致读取的数据多于单个日期的数据。

  • 创建分区

建表时使用关键字 PARTITION BY 【分区名称】

  • 使用函数创建分区

比如根据toYYYYMM(date字段)会生成yyyyMM格式的年月日期分区

数据****PARTITION 在ClickHouse中主要有两方面应用:

  1. 在PARTITION KEY上进行分区裁剪,只查询必要的数据。灵活的PARTITION expression设置,使得可以根据SQL PARTITION 进行分区设置,最大化的贴合业务特点。
  2. 对PARTITION 进行TTL管理,淘汰过期的分区数据。

[

](https://blog.csdn.net/m0_37813354/article/details/110847747)

将索引用于部分单调的主键

例如,考虑一个月中的几天。它们在一个月内形成单调序列,但在更长的时间内不形成单调序列。这是一个部分单调的序列。如果用户使用部分单调的主键创建表,ClickHouse 将像往常一样创建稀疏索引。当用户从此类表中选择数据时,ClickHouse 会分析查询条件。如果用户想要获取索引的两个标记之间的数据,并且这两个标记都在一个月内,ClickHouse 可以在此特定情况下使用索引,因为它可以计算查询参数和索引标记之间的距离。

如果查询参数范围中的主键值不表示单调序列,则 ClickHouse 不能使用索引。在这种情况下,ClickHouse使用完全扫描方法。

ClickHouse 不仅将此逻辑用于月份序列中的天数,还对表示部分单调序列的任何主键使用此逻辑。

Clickhouse一级索引优化方案相关推荐

  1. MySQL索引(二)索引优化方案有哪些

    在上一篇文章中,我们介绍了MySQL中常见的索引类型以及每种索引的各自特点,那么这篇文章带你来与我一起看一下聚集索引与二级索引的关系,最后在附上常见的索引优化方案.首先我们还是看一下聚集索引和二级索引 ...

  2. left join 索引失效无条件_从零开始学数据分析-mysql索引优化方案

    接上节,执行计划还有一个重要的字段是extra,以下是出现的选项. 1.using filesort: 需要一次额外的查询或者排序,性能开销大.常见于order by语句中. create 对于单索引 ...

  3. 某局点分区表索引优化方案

    一 .操作背景 按照要求,业务数据库的分区表应为本地索引而不能为全局索引,如果为全局索引, 当维护人员对分区表进行维护的时候,将导致分区索引失效而影响业务,而业务数据库中存在一些分区表索引为全局索引情 ...

  4. 【clickhouse】ClickHouse表引擎 MergeTree 索引与数据存储方式 一级索引 二级索引

    1.概述 转载:ClickHouse表引擎 MergeTree 索引与数据存储方式 2.一级索引 MergeTree 主键使用 primary key 定义,定义主键后,会将数据依据 index_gr ...

  5. MySQL的索引及优化方案

    目录 索引的概念 索引的分类 索引的机制 索引的优缺点 在哪些表上适合使用索引 索引的使用 索引使用注意事项 各个索引区别 索引优化策略 索引的概念 通过给字段添加索引可以提高数据的读取速度,提高项目 ...

  6. SQL数据库管理系统的优化方案(表的主键、外键、索引设计)

    在基本表设计中,表的主键.外键.索引设计占有非常重要的地位,但系统设计人员往往只注重于满足用户要求,而没有从系统优化的高度来认识和重视它们.实际上,它们与系统的运行性能密切相关.现在从系统数据库优化角 ...

  7. (转)Hibernate的优化方案

    http://blog.csdn.net/yerenyuan_pku/article/details/70768603 HQL优化 使用参数绑定 使用绑定参数的原因是让数据库一次解析SQL,对后续的重 ...

  8. 数据库RocksDB优化方案

    写放大优化 若数据库写入为10M/s,磁盘写入的30MB/s(如compaction时io导致),则写入放大为3(使用iostats),[写入放大率会缩短闪存寿命](Optimizing Space ...

  9. 大数据量数据库设计与优化方案

    一.数据库结构的设计 如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能.所以,在一个系统开始实施之前,完备的数据库模型的设计是必须的. ...

最新文章

  1. python操作sqlserver如何判断删除的数据不存在_Python MongoDB 插入数据时已存在则不执行,不存在则插入的解决方法...
  2. sift计算描述子代码详解_SIFT解析(三)生成特征描述子
  3. 为什么vs会输出一个框作为结果_检测与分类专辑 | 对精准目标检测的含不确定性的边界框回归...
  4. HBase的列族式存储介绍
  5. Apache Camel简介
  6. 前端学习(3001):vue+element今日头条管理--项目初始化总结
  7. python必读_学好Python必读的几篇文章
  8. 交流适配器行业调研报告 - 市场现状分析与发展前景预测
  9. 外星人到底在哪?普利茅斯大学新建AI寻系外生命系统
  10. 论文笔记_CV_AD_Visual Perception for Autonomous Driving
  11. 大势至服务器共享文件监控软件8.6,大势至服务器共享文件夹监控软件、局域网共享管理软件、局域网共享设置软件...
  12. VBScript 教程
  13. 根据卫星运动矢量计算轨道六根数
  14. python爬虫随心所欲地爬取百度图片
  15. 用极限定义证明微积分基本定理
  16. 互联网发展简史(1)ARPANET的建立
  17. 单核CPU与多核CPU的区别
  18. 菜鸟入门:Java语言学习
  19. 【机器学习】浅谈正规方程法梯度下降
  20. POS机IC卡参数、公钥下载流程及POSP数据库设计

热门文章

  1. 浅谈架构师之逻辑架构和物理架构
  2. 关于使用XD制作软件UI
  3. 区块链技术助力甘肃建食安信息追溯平台 为食品安全“立规矩”
  4. HTML让一张图片变暗,css怎么让图片变暗?
  5. 2022-04-06 动态界定图像HSV颜色范围
  6. 服务器如何关闭硬盘自检,去除非法关机硬盘错误造成的开机硬盘自检的两种方法...
  7. Consider defining a bean of type ‘com.XXX‘ in your configuration.
  8. 无需正版我的世界服务器IP,我的世界1.7.10永恒服务器
  9. 完美世界2最新服务器,我的世界1.7.2服务器完美世界
  10. HTML Echarts图形统计实时显示DHT11温度(三)