Clickhouse的联合索引

Clickhouse 有了单独的键索引,为什么还需要有联合索引呢?了解过mysql的兄弟们应该都知道这个事。
对sql比较熟悉的兄弟们估计看见这个联合索引心里大概有点数了,不过clickhouse的联合索引相比mysql的又有些不一样了,mysql 很遵循最左匹配原则,但是clickhouse 又似乎有些不一样,具体哪里不一样呢,那么我们还是和上一篇 Q220240407–clickhouse 中的索引大白话一样,通过引用这张表hits_UserID_URL 来揭开下这个谜底。

一:联合索引
回到hits_UserID_URL的建表语句如下,我们当时创建表的时候,我们的主键就指定了UserID 和URL 如下:

CREATE TABLE hits_UserID_URL
(
UserID UInt32,
URL String,
EventTime DateTime
)
ENGINE = MergeTree
PRIMARY KEY (UserID, URL)
ORDER BY (UserID, URL, EventTime);
这样的主键大于1的情况,生成的那个primary.idx 索引文件(参考上一篇) 也就是会每一行都会生成n列记录(n>1),这样的索引文件我们就可以称之为联合索引文件是吧,故而UserID 和URL就形成了一个联合索引。其实为什么需要联合索引呢?那肯定是说在实际业务中这两个列经常放在一起查询,所以才会考虑给这两个列加索引。

但是联合索引的效果就真的好吗,这个取决于第一列索引的基数,什么是基数呢?说白了,就是某个列的值去重之后的个数,如果个数多说明基数高,如果个数少就说明基数低;那为什么联合索引的效果是取决于基数呢?因为联合索引在找数据的时候是使用排除搜索法的,下面我们来讲讲排除搜索法

1.1: 排除搜索法

排除搜索法顾名思义就是排除掉一些数据,然后搜索一波:

因为userId 和url 是按照字典顺序升序排列的,假如左边的键userID 的基数比较低,那么相同的userId可能会分布在很多的grandule 里面,也就可能分布在索引标记上,如下所示:

在这里插入图片描述

所以假如我们查询url <=w3 的数据,那么我们就可以看看相邻的两个grandule的最后一个url的值和第一个url的值;如果第一个url的值是w3, 第二个url 的值是w4,那么我们需要搜索的grandule 就只有前面的第一个grandule 和 之前的grandule 里面了,如法炮制,对着前面的所有的grandule,使用二分法,就可以在小于等于 o(logn)的时间复杂度找到对应的grandule;而且需要加载的grandule 非常少,这个效率挺高

但是,哈哈哈,凡事都怕但是,在实际生产环境中,userID和url的基数可能都很高,那么相同的userID 值可能就不会分布在多个表行和grandule 中,所以索引文件中的url 值就不太可能单调递增,如下所示:在这里插入图片描述

这个时候使用排除搜索法估计就要全表扫描了,使用不了二分法了;这个查询效率就贼低了,所以如果我们需要提高查找效率,就要使用多个主键索引了,而不是单纯的用联合索引。

二: 使用多个主键索引

2.1: 创建另一张表

最常见的方法就是我们再建另一张表,这张表的主键索引我们设置为url,如下所示:

CREATE TABLE hits_URL_UserID
(
UserID UInt32,
URL String,
EventTime DateTime
)
ENGINE = MergeTree
PRIMARY KEY (URL, UserID)
ORDER BY (URL, UserID, EventTime)
SETTINGS index_granularity = 8192, index_granularity_bytes = 0;

INSERT INTO hits_URL_UserID
SELECT * from hits_UserID_URL;

OPTIMIZE TABLE hits_URL_UserID FINAL;
这个当然可以加快查询,但是如果我们的系统已经上线了的话,这个改动还要应用系统那边改,不太实用

2.2: 使用物化视图

我们可以使用物化视图,不改动原表的情况下,如下所示:

CREATE MATERIALIZED VIEW mv_hits_URL_UserID
ENGINE = MergeTree()
PRIMARY KEY (URL, UserID)
ORDER BY (URL, UserID, EventTime)
POPULATE
AS SELECT * FROM hits_UserID_URL;

使用populate 的关键字是让原表的所有值钱的数据都导入这个物化视图,后续有的数据也会同步到这个物化视图;

但是这个的麻烦点也和刚刚的第一种方法一样,需要应用系统改动适配查询物化视图表,但是物化视图在做聚合方面很有用,如下所示:

–agg+物化视图本地表
–drop table if exists glab.bi_dws_lbs_adapter_agg_mv_local on cluster glab_cluster;

create materialized view glab.bi_dws_lbs_adapter_agg_mv_local on cluster glab_cluster(
id String,
idType AggregateFunction(argMax, String, UInt64 ),
geohash AggregateFunction(argMax, String, UInt64 ),
locType AggregateFunction(argMax, Int64, UInt64 ),
wifimac AggregateFunction(argMax, String, UInt64 ),
ip AggregateFunction(argMax, String, UInt64 ),
baseStation AggregateFunction(argMax, String, UInt64 ),
connectType AggregateFunction(argMax, Int64, UInt64 ),
equipmentId AggregateFunction(argMax, String, UInt64 ),
time AggregateFunction(max, UInt64 )
)
engine = AggregatingMergeTree()
partition by tuple()
order by id
as
select
id,
argMaxState ( id_type, ts ) AS idType,
argMaxState ( geohash12, ts ) AS geohash,
argMaxState ( loc_type, ts ) AS locType,
argMaxState ( wifimac, ts ) AS wifimac,
argMaxState ( ip, ts ) AS ip,
argMaxState ( base_station, ts ) AS baseStation,
argMaxState ( connect_type, ts ) AS connectType,
argMaxState ( equipment_id, ts ) AS equipmentId,
maxState( ts ) AS time
from glab.bi_dws_lbs_adapter_local
where geohash12 != ‘’ AND geohash12 IS NOT NULL
group by id;

–agg+物化视图集群表
CREATE TABLE glab.bi_dws_lbs_adapter_agg_mv on cluster glab_cluster as bi_dws_lbs_adapter_agg_mv_local
ENGINE = Distributed(‘glab_cluster’, ‘glab’, ‘bi_dws_lbs_adapter_agg_mv_local’, murmurHash3_32(id))

参考链接:https://cf.cloudglab.cn/pages/viewpage.action?pageId=226211113

2.3: 使用投影projection

上述两个方法都是需要应用系统适配,但是这个projection 就很好的解决了这个问题,BI这边改动就行,不需要应用改动,如下所示:

ALTER TABLE hits_UserID_URL
ADD PROJECTION prj_url_userid
(
SELECT *
ORDER BY (URL, UserID)
);

ALTER TABLE hits_UserID_URL
MATERIALIZE PROJECTION prj_url_userid; (使用materalize 是为了让原始的数据形成另一个索引)

所以当系统查询慢的情况下,如果就是因为联合索引的问题,我们可以使用projection,成本最小;如果需要查询诸如max(ts) 的geohash ,那就要用到物化视图了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3223661.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

谷粒商城 - 编写一个自定义校验注解

目录 开始 未来实现效果 第一步&#xff1a;编写自定义校验注解 第二步&#xff1a;编写自定义校验器 第三步&#xff1a;编写配置文件 效果演示 开始 未来实现效果 编写一个 ListValue 注解&#xff0c;可以实现功能有&#xff1a; 限定字段的值&#xff0c;例如指定只…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【密钥协商(ArkTS)】

密钥协商(ArkTS) 以协商密钥类型为X25519 256&#xff0c;并密钥仅在HUKS内使用为例&#xff0c;完成密钥协商。 开发步骤 生成密钥 设备A、设备B各自生成一个非对称密钥&#xff0c;具体请参考[密钥生成]或[密钥导入]。 密钥生成时&#xff0c;可指定参数HUKS_TAG_DERIVE…

【BUG】RestTemplate发送Post请求后,响应中编码为gzip而导致的报错

BUG描述 20240613-09:59:59.062|INFO|null|810184|xxx|xxx||8|http-nio-xxx-exec-1|com.xxx.jim.xxx.XXXController.?.?|MSG接收到来自xxx的文件请求 headers:[host:"xxx", accept:"text/html,application/json,application/xhtmlxml,application/xml;q0.9,*…

Redis 实现的延时队列组件

最近看开源看到一个好用的延时队列组件&#xff0c;已经上生产。代码量很少&#xff0c;主要就是利用Redis监听过期键实现的。然后搞点策略模式柔和柔和。利用Spring Start 封装了一下&#xff0c;全是俺掌握的知识&#xff0c;稍微研究了下就搞懂了。觉得挺有用的&#xff0c;…

2024.7.9作业

1、提示并输入一个字符串&#xff0c;统计该字符串中字母、数字、空格以及其他字符的个数 #include <stdio.h> #include <string.h> int main(int argc,const char *argv[]) { char arr[30]{0}; int zm0,kg0,sz0,qt0; printf("请输入字符串&…

utf8mb4和utf8的不同、若依框架,代码生成器,gitee,前端vue的下载、修复和启动(寻求大佬帮助若依框架三、2.3)

2024.7.9 一、数据库的排序和统一问题。utf8mb4和utf8的不同1.1 发现问题1.2 解决问题-在idea中用sql生成器&#xff0c;生成sql语句&#xff0c;然后在里面修改1.3 utf8和utf8mb4的区别 二、若依前后端框架。代码生成器&#xff08;还没研究懂&#xff0c;但有三个方案&#x…

微软清华提出全新预训练范式,指令预训练让8B模型实力暴涨!实力碾压70B模型

现在的大模型训练通常会包括两个阶段&#xff1a; 一是无监督的预训练&#xff0c;即通过因果语言建模预测下一个token生成的概率。该方法无需标注数据&#xff0c;这意味着可以利用大规模的数据学习到语言的通用特征和模式。 二是指令微调&#xff0c;即通过自然语言指令构建…

Python基础-成年人判断(if条件语句联系)

注意输入的年龄需要转化为字符串 代码&#xff1a; print("欢迎来到游乐场&#xff1a;儿童免费&#xff0c;成人收费") age int(input("请输入你的年龄:")) if age>18:print("你已经成年&#xff0c;需要补票10元") # 四个空格缩进print…

使用Mplayer实现MP3功能

核心功能 1. 界面设计 项目首先定义了一个clearscreen函数&#xff0c;用于清空屏幕&#xff0c;为用户界面的更新提供了便利。yemian函数负责显示主菜单界面&#xff0c;提供了包括查看播放列表、播放控制、播放模式选择等在内的9个选项。 2. 文件格式支持 is_supported_f…

gpt-4o看图说话-根据图片回答问题

问题&#xff1a;中国的人口老龄化究竟有多严重&#xff1f; 代码下实现如下&#xff1a;&#xff08;直接调用openai的chat接口&#xff09; import os import base64 import requests def encode_image(image_path): """ 对图片文件进行 Base64 编码 输入…

Nacos2.X 配置中心源码分析:客户端如何拉取配置、服务端配置发布客户端监听机制

文章目录 Nacos配置中心源码总流程图NacosClient源码分析获取配置注册监听器 NacosServer源码分析配置dump配置发布 Nacos配置中心源码 总流程图 Nacos2.1.0源码分析在线流程图 源码的版本为2.1.0 &#xff0c;并在配置了下面两个启动参数&#xff0c;一个表示单机启动&#…

pytest-yaml-sanmu(六):YAML数据驱动测试

如果说 pytest 中哪些标记使用得最多&#xff0c;那无疑是 parametrize 了&#xff0c; 它为用例实现了参数化测试的能力&#xff0c;进而实现了数据驱动测试的能力。 1. 使用标记 parametrize 的使用需要提高两个内容&#xff1a; 参数名 参数值 pytest 在执行用例时&…

【LeetCode刷题笔记】LeetCode.11.盛最多水的容器

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法知识专栏&#xff1a;算法分析&#x1f525; 给大家跳段街舞感谢…

020-GeoGebra中级篇-几何对象之点与向量

本文概述了在GeoGebra中如何使用笛卡尔或极坐标系输入点和向量。用户可以通过指令栏输入数字和角度&#xff0c;使用工具或指令创建点和向量。在笛卡尔坐标系中&#xff0c;示例如“P(1,0)”&#xff1b;在极坐标系中&#xff0c;示例如“P(1;0)”或“v(5;90)”。文章还介绍了点…

Python大数据分析——决策树和随机森林

Python大数据分析——决策树和随机森林 决策树决策树节点字段的选择信息熵条件熵信息增益信息增益率 基尼指数条件基尼指数基尼指数增益 决策树函数 随机森林函数 决策树 图中的决策树呈现自顶向下的生长过程&#xff0c;深色的椭圆表示树的根节点&#xff1b;浅色的椭圆表示树…

Raylib 实现超大地图放大缩小与两种模式瓦片地图刷新

原理&#xff1a; 一种刷新模式&#xff1a; 在宫格内整体刷新&#xff0c;类似九宫格移动到边缘&#xff0c;则九宫格整体平移一个宫格&#xff0c;不过这里是移动一个瓦片像素&#xff0c;实际上就是全屏刷新&#xff0c;这个上限是 笔记本 3060 70帧 100*100个瓦片每帧都…

思维+并查集,1670C - Where is the Pizza?

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1670C - Where is the Pizza? 二、解题报告 1、思路分析 考虑两个数组a&#xff0c;b的每个位置只能从a&#xff0c;b中挑一个 不妨记posa[x]为x在a中位置&#xff0c;posb同理 我们假如位置i挑选a[i]&a…

Java--instanceof和类型转换

1.如图&#xff0c;Object&#xff0c;Person&#xff0c;Teacher&#xff0c;Student四类的关系已经写出来了&#xff0c;由于实例化的是Student类&#xff0c;因此&#xff0c;与Student类存在关系的类在使用instanceof时都会输出True&#xff0c;而无关的都会输出False&…

小试牛刀--对称矩阵压缩存储

学习贺利坚老师对称矩阵压缩存储 数据结构实践——压缩存储的对称矩阵的运算_计算压缩存储对称矩阵 a 与向量 b 的乘积-CSDN博客 本人解析博客 矩阵存储和特殊矩阵的压缩存储_n阶对称矩阵压缩-CSDN博客 版本更新日志 V1.0: 对老师代码进行模仿 , 我进行名字优化, 思路代码注释 …

ARM裸机:一步步点亮LED(汇编)

硬件工作原理及原理图查阅 LED物理特性介绍 LED本身有2个接线点&#xff0c;一个是LED的正极&#xff0c;一个是LED的负极。LED这个硬件的功能就是点亮或者不亮&#xff0c;物理上想要点亮一颗LED只需要给他的正负极上加正电压即可&#xff0c;要熄灭一颗LED只需要去掉电压即可…