如何合理选择ClickHouse表主键

这篇具有很好参考价值的文章主要介绍了如何合理选择ClickHouse表主键。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

ClickHouse提供索引和数据存储的复杂机制,能够实现在高负载下仍有优异的读写性能。当创建MergeTree表时需要选择主键,主键影响大多数查询性能。本文介绍主键的工作原理,让我们知道如何选择合适的主键。

设置主键

MergeTree表可以设置主键,必须在创建表时指定,示例如下:

CREATE TABLE test
(
    `dt` DateTime,
    `event` String,
    `user_id` UInt64,
    `context` String
)
ENGINE = MergeTree
PRIMARY KEY (event, user_id, dt)
ORDER BY (event, user_id, dt)

上面在三个列上按一定顺序创建了主键:event, user_id, dt。注意,主键应该与排序键相同,或作为排序键的前缀。

排序键定义在磁盘上的排列顺序,主键定义查询使用的数据结构。通常主键与顺序键相同,如何相同可以忽略主键的定义,ClickHouse会自动采用排序键的字段。

数据存储粒度

ClickHouse把表记录划分为多个组,组称为粒度:

clickhouse 添加主键,ClickHouse,clickhouse,数据结构,主键索引

粒度大小基于表的设置(创建表时设置),缺省为8192,粒度数量可以通过下面公式进行计算:

clickhouse 添加主键,ClickHouse,clickhouse,数据结构,主键索引

单个粒度可以理解为虚拟小表(包括较少记录的子集,缺省为8192)。每个粒度按顺序存储行(order by 指定的键顺序):

clickhouse 添加主键,ClickHouse,clickhouse,数据结构,主键索引

主键标记和索引存储

主键仅存储每个粒度第一行,而不是每一行:

clickhouse 添加主键,ClickHouse,clickhouse,数据结构,主键索引

这就是ClickHouse查询快的原因。不保存所有值,技能保存部分使得主键特别小。Clickhouse不是查找单个行,而是先找到某个粒度,然后只对找到的粒度执行完整扫描(这是非常高效的,因为每个颗粒的尺寸都很小):

clickhouse 添加主键,ClickHouse,clickhouse,数据结构,主键索引

查询性能

这里填充5千万测试记录验证查询性能:

insert into test 
select * FROM generateRandom(
    'dt datetime, event Text, user_id UInt64, context Text',1, 20
) LIMIT 50000;

前节表定义了主键包括三个字段:

clickhouse 添加主键,ClickHouse,clickhouse,数据结构,主键索引

如果ClickHouse查询条件使用主键则能够利用主键提升性能:


SELECT *
FROM test
WHERE event = 'YJ9'

返回结果:

Query id: d237e4d9-5b6e-453f-befb-57e5ae84fd28

┌──────────────────dt─┬─event─┬──────────────user_id─┬─context─┐
│ 2079-09-01 19:10:41 │ YJ9   │ 16936621875208636777 │         │
└─────────────────────┴───────┴──────────────────────┴─────────┘

1 rows in set. Elapsed: 0.002 sec. Processed 8.19 thousand rows, 414.23 KB (3.78 million rows/s., 190.99 MB/s.)

我们看到搜索特定event值,仅扫描了单个粒度。这里YJ9只有一条,可以通过下面语句进行确认:

SELECT
    event,
    count(event != '') AS cnt
FROM test
GROUP BY event
HAVING cnt = 1
LIMIT 10

Query id: c5dc21e7-929a-4e0d-9ae9-0213caffee41

┌─event────┬─cnt─┐
│ Po$h\VLc │   1 │
│ YJ9      │   1 │
│ PS6>;.f  │   1 │
│ ov       │   1 │
│ |FYQ~1 │
│ yZ$~cP   │   1 │
│ kUAfps@  │   1 │
│ kX{]/:g(1 │
│ ]R,gw,vA │   1 │
│ qu       │   1 │
└──────────┴─────┘

下面通过explain查看执行计划进行确认:

explain indexes = 1 
SELECT *
FROM test
WHERE event = 'YJ9'

返回结果:

EXPLAIN json = 1, indexes = 1
SELECT *
FROM test
WHERE event = 'YJ9'

Query id: 6954664a-6e9c-4417-adba-d6f04270734b

┌─explain─────────────────────────────────────────────────────────────────────┐
│ Expression ((Projection + Before ORDER BY))                                 │
│   Filter (WHERE)                                                            │
│     SettingQuotaAndLimits (Set limits and quota after reading from storage) │
│       ReadFromMergeTree                                                     │
│       Indexes:                                                              │
│         PrimaryKey                                                          │
│           Keys:                                                             │
│             event                                                           │
│           Condition: (event in ['YJ9', 'YJ9'])                              │
│           Parts: 1/1                                                        │
│           Granules: 1/6                                                     │
└─────────────────────────────────────────────────────────────────────────────┘

可以看到仅使用了1/6,即扫描一个粒度。这说明不扫描全表,ClickHouse使用主键索引首先定义相应的粒度,然后在该粒度中扫描过滤,我们也可以在查询中使用多个主键列:

SELECT *
FROM test
WHERE event = 'YJ9' and user_id = '16936621875208636777'

返回结果:


SELECT *
FROM test
WHERE (event = 'YJ9') AND (user_id = '16936621875208636777')

Query id: 8deeb18f-8e85-4cb6-b8e4-fe6e11f2715c

┌──────────────────dt─┬─event─┬──────────────user_id─┬─context─┐
│ 2079-09-01 19:10:41 │ YJ9   │ 16936621875208636777 │         │
└─────────────────────┴───────┴──────────────────────┴─────────┘

1 rows in set. Elapsed: 0.003 sec. Processed 8.19 thousand rows, 414.23 KB (2.65 million rows/s., 133.93 MB/s.)

仍然仅扫描了单个粒度。相反如果使用条件列不在主键中,ClickHouse则需要全表扫描:

SELECT count(*)
FROM test where context = '{'

返回结果:


SELECT count(*)
FROM test
WHERE context = '{'

Query id: 0c6f01a1-7192-4d46-ad1b-89e465f051fd

┌─count()─┐
│      24 │
└─────────┘

1 rows in set. Elapsed: 0.002 sec. Processed 50.00 thousand rows, 951.29 KB (21.15 million rows/s., 402.31 MB/s.)

可以看到ClickHouse执行了全表扫描。另外,如果使用条件列跳过主键的前缀,ClickHouse也不能完全利用主键索引:

SELECT *
FROM test
WHERE user_id = '16936621875208636777'

返回结果:


SELECT *
FROM test
WHERE user_id = '16936621875208636777'

Query id: d9d2675d-b231-4458-9bc1-6854cf4cf865

┌──────────────────dt─┬─event─┬──────────────user_id─┬─context─┐
│ 2079-09-01 19:10:41 │ YJ9   │ 16936621875208636777 │         │
└─────────────────────┴───────┴──────────────────────┴─────────┘

1 rows in set. Elapsed: 0.003 sec. Processed 50.00 thousand rows, 748.69 KB (17.25 million rows/s., 258.23 MB/s.)

主键索引利用

下面总结ClickHouse 利用主键索引的场景:

  1. 查询where或order子句包含主键的第一列
  2. 查询where包含主键的前x列,order子句包含主键的前x列
  3. 查询where包含主键的所有列
  4. 其他情况,ClickHouse需要扫描全表获取请求数据。

clickhouse 添加主键,ClickHouse,clickhouse,数据结构,主键索引

总结

基于ClickHouse优化结构和排序数据,正确利用主键索引能节约资源,极大提升查询性能。总之选择主键需遵循下面简单规则:文章来源地址https://www.toymoban.com/news/detail-576731.html

  1. 选择计划在大多数查询中使用的列
  2. 选择大部分查询需要的列,如主键包含3列,查询包括1列或2列
  3. 如果查询不确定,首先使用低基数列,然后再使用高基数列,从而获得更好的压缩和提高磁盘利用率
    参考资料:https://medium.com/datadenys/how-clickhouse-primary-key-works-and-how-to-choose-it-4aaf3bf4a8b9

到了这里,关于如何合理选择ClickHouse表主键的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 「从ES到CK 04」Clickhouse表引擎选择和表结构设计

            在完成将公司日志数据从Elasticsearch(下称ES)转战到Clickhouse后,个人认为有必要将过程记录分享。限于篇幅及便于分类组织,我会以一个系列文章的形式记录: 01 《Elasticsearch vs Clickhouse》 02 《Clickhouse的基础知识扫盲》 03 《​Clickhouse多分片多副本集群部署​》 04

    2024年02月03日
    浏览(28)
  • 如何解决ClickHouse的表处于只读状态

    在使用ClickHouse的过程中,经常会遇到某个副本的表A处于readonly状态,无法更新数据。 原因:说是zookeeper的压力大(建议data和log分开存储到不同的磁盘),metadata元数据丢失。 此时通常有两种解决办法: 1)创建一个和该表A结构相同的表A\\\',然后将数据从A表导入到A\\\',多个副

    2023年04月09日
    浏览(45)
  • 【clickhouse实践】clickhouse如何在查询中对某字段空值设置默认值及对Nullable值的处理

    在ClickHouse中,我们可以使用一些函数来处理可空性(nullable)列。可空列是指允许包含空值(null)的列。在处理可空列时,我们需要考虑如何处理这些空值。以下是几个常用的ClickHouse函数,用于处理可空性列。 IFNULL 函数用于将一个可空性列中的空值替换为指定的默认值。它

    2024年02月12日
    浏览(31)
  • 如何给ClickHouse表生成随机真实测试数据

    学习ClickHouse数据库,通常需要下载官网一些示例数据。我们也可以通过内置函数 generateRandom 快速生成测试数据,从而测试学习一些特性的性能及底层原理。 generateRandom函数基于给定schema生成随机数据,用于填充测试表。不是所有类型都支持。 name — 列名称. TypeName — 列类型

    2023年04月14日
    浏览(29)
  • clickhouse 代替 es 如何对文档做模糊查询?

    模糊查询在日志存储的场景中非常普遍。 ClickHouse 作为大数据分布式引擎,理所当然地会被作为日志存储的备选方案。事实上使用 ClickHouse 作为日志存储方案,业界目前也已经在多家企业落地,比如 Uber 、石墨文档、映客、快手、携程、唯品会等。 日志查询的一个最大的特点

    2024年01月21日
    浏览(47)
  • 阿里云RDS MySQL 数据如何快速同步到 ClickHouse

    云数据库 RDS MySQL 和 云数据库 ClickHouse 是阿里云推出的两个备受欢迎的数据库解决方案,它们为用户提供了可靠的数据存储方案、分析数仓方案,本文介绍如何快速将 RDS MySQL 的数据同步到云数据库 ClickHouse。 如何快速将RDSMySQL的数据同步到云数据库 云数据库 RDS MySQL 和云数据

    2024年02月04日
    浏览(31)
  • ClickHouse进阶|如何自研一款企业级高性能网关组件?

    使用原生ClickHouse集群进行节点数据查询和写入时,离不开第三方开源网关组件chproxy支持。但由于chproxy缺少TCP协议支持,导致性能、查询能力等受限。这也成为困扰众多ClickHouse开发者的一大难题。那么,究竟应该如何突破?本文将揭秘火山引擎ByteHouse企业版自研网关组件如何

    2024年02月07日
    浏览(37)
  • clickhouse如何将一台服务器上的数据导入到另一台服务器

    使用remote函数: 其中remote分别对应的参数: 参数1:ip+port,即为源数据的服务器ip+端口; 参数2:源数据服务器的数据库名; 参数3:源数据的表名; 参数4:源clickhouse数据库的客户端访问账号; 参数5:源clickhouse数据库的客户端访问密码;

    2024年02月15日
    浏览(52)
  • ClickHouse进阶(三):ClickHouse 索引

    进入正文前,感谢宝子们订阅专题、点赞、评论、收藏!关注IT贫道,获取高质量博客内容! 🏡个人主页:含各种IT体系技术, IT贫道_Apache Doris,大数据OLAP体系技术栈,Kerberos安全认证-CSDN博客 📌订阅:拥抱独家专题,你的订阅将点燃我的创作热情! 👍点赞:赞同优秀创作,

    2024年02月10日
    浏览(28)
  • ClickHouse列存储(十一)—— ClickHouse

    1.数据库基本概念 2.列式存储 3.clickHouse存储设计 4.clickHouse典型应用场景 1、了解数据库基本概念 数据库 DBMS:数据库管理系统 OLTP 数据库 : OLTP(Online transactional processing) OLAP 数据库:OLAP (Online analytical processing) SQL (Structured Query Language) 词法分析 语法分析 AST (Abstract syntax t

    2024年02月10日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包