Clickhouse学习笔记(10)—— 查询优化

单表查询

Prewhere 替代 where

prewhere与where相比,在过滤数据的时候会首先读取指定的列数据,来判断数据过滤,等待数据过滤之后再读取 select 声明的列字段来补全其余属性

简单来说就是先过滤再查询,而where过滤是先查询出对应的列字段来,再根据过滤条件过滤数据;因此对比之下,使用prewhere过滤处理的数据量要更少,效率也就更高;

但需注意,prewhere只可适用于mergetree引擎

默认情况下,where 条件会自动优化成 prewhere,通过参数optimize_move_to_prewhere来控制;

参数值为1表示使用怕prewhere


使用where和prewhere的对比:

①使用where:

关闭prewhere自动优化之后,执行语句:

select WatchID, JavaEnable, Title, GoodEvent, EventTime, EventDate, CounterID, ClientIP, ClientIP6, RegionID, UserID, CounterClass, OS, UserAgent, URL, Referer, URLDomain, RefererDomain, Refresh, IsRobot, RefererCategories, URLCategories, URLRegions, RefererRegions, ResolutionWidth, ResolutionHeight, ResolutionDepth, FlashMajor, FlashMinor, FlashMinor2
from datasets.hits_v1 where UserID='3198390223272470366';

结果如下,耗时2.467s

②使用prewhere:

开启优化后执行相同语句,结果如下,耗时仅用0.172s,可以看到效率提升很明显


在一些场景下,自动优化不生效,如下:

  • 使用常量表达式
  • 使用默认值为 alias 类型的字段
  • 包含了 arrayJOIN,globalIn,globalNotIn 或者 indexHint 的查询
  • select 查询的列字段和 where 的谓词相同
  • 使用了主键字段

例如select 查询的列字段和 where 的谓词相同的情况:

explain syntax select UserID from hits_v1 where UserID='3198390223272470366';

结果如下:

可以看到并没有进行优化;

因此我们在使用prewhere的时候,最好直接在sql中使用prewhere,而不是依赖于自动优化;

例如:

explain syntax select UserID from hits_v1 prewhere UserID='3198390223272470366';

结果如下,是可以生效的:

数据采样 SAMPLE

示例:

SELECT Title,count(*) AS PageViews 
FROM hits_v1
SAMPLE 0.1
WHERE CounterID =57
GROUP BY Title
ORDER BY PageViews DESC LIMIT 1000

采样语法:SAMPLE 样本比例(1代表100%)

SAMPLE Clause | ClickHouse Docs

注意:采样修饰符只有在 MergeTree engine 表中才有效,且在创建表时需要指定采样策略

这里的采样策略是在建表时指定的:

表示根据UserID字段的哈希值进行采样

列裁剪与分区裁剪

列裁剪:数据量太大时应避免使用 select * 操作

分区裁剪:就是只读取需要的分区,在过滤条件中指定

例如:

select WatchID, JavaEnable, Title, GoodEvent, EventTime, EventDate, CounterID, ClientIP, ClientIP6, RegionID, UserID
from datasets.hits_v1
where EventDate='2014-03-23';

这里的EventDate就是分区字段

orderby 结合 where、limit

千万以上数据集进行 order by 查询时需要搭配 where 条件和 limit 语句一起使用

对比:

①仅使用order by:

SELECT UserID,Age
FROM hits_v1 
ORDER BY Age DESC;

②使用where和limit:

SELECT UserID,Age
FROM hits_v1 
WHERE CounterID=57
ORDER BY Age DESC LIMIT 1000;

可以看到处理的数据量大幅降低,效率提示十分明显;

③仅使用limit:

SELECT UserID,Age
FROM hits_v1 
ORDER BY Age DESC LIMIT 1000;

处理的数据量虽然没有降低,但是效率提升也比较明显

避免构建虚拟列

什么是虚拟列:在select查询的字段中,非建表时指定的字段即为虚拟列

例如:

SELECT Income,Age,Income/Age as IncRate FROM datasets.hits_v1;

这里的Income/Age就是虚拟列,由于每次查一条语句都需要进行一次计算,所以十分影响性能

使用虚拟列:

没有虚拟列:

uniqCombined 替代 distinct

uniqCombined 底层采用类似 HyperLogLog 算法实现,能接收 2%左右的数据误差,可直接使用这种去重方式提升查询性能

而Count(distinct)会使用 uniqExact精确去重


示例:

explain syntax select count(distinct UserID) from hits_v1;


对UserID进行去重:

使用uniqCombined

SELECT uniqCombined(UserID) from datasets.hits_v1;

使用uniqExact

select count(distinct UserID) from hits_v1;

其他

查询熔断

为了避免因个别慢查询引起的服务雪崩的问题,除了可以为单个查询设置超时以外,还可以配置周期熔断,在一个查询周期内,如果用户频繁进行慢查询操作超出规定阈值后将无法继续进行查询操作

关闭虚拟内存

物理内存和虚拟内存的数据交换,会导致查询变慢,资源允许的情况下关闭虚拟内存

配置 join_use_nulls

为每一个账户添加 join_use_nulls 配置,左表中的一条记录在右表中不存在,右表的相应字段会返回该字段相应数据类型的默认值,而不是标准 SQL 中的 Null 值

批量写入时先排序

批量写入数据时,必须控制每个批次的数据中涉及到的分区的数量,在写入之前最好对需要导入的数据进行排序。无序的数据或者涉及的分区太多,会导致 ClickHouse 无法及时对新导入的数据进行合并,从而影响查询性能

关注 CPU

cpu 一般在 50%左右会出现查询波动,达到 70%会出现大范围的查询超时,cpu 是最关键的指标,要非常关注

使用top命令查看CPU使用情况

参数含义如下:

%us:表示用户空间程序的cpu使用率(没有通过nice调度)

%sy:表示系统空间的cpu使用率,主要是内核程序。

%ni:表示用户空间且通过nice调度过的程序的cpu使用率。

%id:空闲cpu

%wa:cpu运行时在等待io的时间

%hi:cpu处理硬中断的数量

%si:cpu处理软中断的数量

%st:被虚拟机偷走的cpu

注:99.0 id,表示空闲CPU,即CPU未使用率,100%-99.0%=1%,即系统的cpu使用率为1%。

多表关联

准备数据

由于clickhouse的join操作效率很低,因此根据官方提供的数据集准备两张小表

#创建小表CREATE TABLE visits_v2 
ENGINE = CollapsingMergeTree(Sign)
PARTITION BY toYYYYMM(StartDate)
ORDER BY (CounterID, StartDate, intHash32(UserID), VisitID)
SAMPLE BY intHash32(UserID)
SETTINGS index_granularity = 8192
as select * from visits_v1 limit 10000;#创建 join 结果表:避免控制台疯狂打印数据
CREATE TABLE hits_v2 
ENGINE = MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID)
SETTINGS index_granularity = 8192
as select * from hits_v1 where 1=0;

这里的visits_v2是一张小表,用于join测试

hits_v2是一张结果表,用于保存join之后的数据;

注意where 1=0这个条件,保留了hits_v1这张表的结构,但里面没有数据

用 IN 代替 JOIN

①使用IN:

insert into hits_v2
select a.* from hits_v1 a where a. CounterID in (select CounterID from visits_v1);

②使用JOIN:

insert into table hits_v2
select a.* from hits_v1 a left join visits_v1 b on a. CounterID=b. CounterID;

对比可知使用IN效率提升很多;

同时在执行join的时候观察CPU使用情况:

发现CPU的占用率接近80%

小表在右原则

多表 join 时要满足小表在右的原则,右表关联时被加载到内存中与左表进行比较,ClickHouse 中无论是 Left join 、Right join 还是 Inner join 永远都是拿着右表中的每一条记录到左表中查找该记录是否存在,所以右表必须是小表


对比以下:

①小表在右

insert into table hits_v2
select a.* from hits_v1 a left join visits_v2 b on a. CounterID=b. CounterID;

②大表在右

insert into table hits_v2
select a.* from visits_v2 b left join hits_v1 a on a. CounterID=b. CounterID;

执行没几秒就内存超限了:

谓词下推(join查询无法自动实现)

ClickHouse 在 join 查询时不会主动发起谓词下推的操作,需要每个子查询提前完成过滤操作

需要注意的是,是否执行谓词下推,对性能影响差别很大

子查询不提前过滤:

insert into hits_v2
select a.* from hits_v1 a left join visits_v2 b on a. CounterID=b. CounterID
where a.EventDate = '2014-03-17';

子查询提前过滤:

insert into hits_v2
select a.* from (select * from hits_v1 where EventDate = '2014-03-17'
) a left join visits_v2 b on a. CounterID=b. CounterID;

可以看出效率上有一定差别


所谓谓词下推,就是在join之前根据条件进行过滤,从而筛选出一部分数据来,减少进行join的数据量,从而提升join的效率

查看上面两个select语句的优化计划,分别如下:

可以看到,如果不手动通过子查询提前完成过滤,是不会自动进行谓词下推的

分布式表使用GLOBAL关键字

两张分布式表上的 IN 和 JOIN 之前必须加上 GLOBAL 关键字,这样的话,右表只会在接收查询请求的那个节点查询一次,并将其分发到其他节点上

如果不加 GLOBAL 关键字的话,每个节点都会单独发起一次对右表的查询,而右表又是分布式表,就导致右表一共会被查询 N²次(N是该分布式表的分片数量),这就是查询放大,会带来很大开销


官网相关描述:

详细用法可见:IN Operators | ClickHouse Docs

字典表

将一些需要关联分析的业务创建成字典表进行 join 操作,前提是字典表不宜太大,因为字典表会常驻内存

参考:Dictionaries | ClickHouse Docs

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android Studio真机运行时提示“安装失败”

用中兴手机真机运行没问题,用Vivo运行就提示安装失败。前提,手机已经打开了调试模式。 报错 Android Studio报错提示: Error running app The application could not be installed: INSTALL_FAILED_TEST_ONLY 手机报错提示: 修…

专访|OpenTiny 社区 Mr 栋:结合兴趣,明确定位,在开源中给自己一些技术性挑战

前言 OpenTiny 开源之夏项目终于迎来了圆满的结局。借此机会,我们采访了 TinyReact 的共建者 Mr 栋同学。 Mr 栋同学是一位热衷于前端技术的开发者,对前端开发充满了激情和热爱。同时他也是一位即将毕业的大四在校生。在 OpenTiny 开源项目中&#xff0…

Window安装MongoDB

三种NOSQL的一种,Redis MongoDB ES 应用场景: 1.社交场景:使用Mongodb存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人,地点等功能 2.游戏场景:使用Mongodb存储游戏用户信息,用户的装备,积分等直接以内嵌文档的形式存储,方便查询,高效率存储和访问…

软路由R4S+iStoreOS实现公网远程桌面局域网内电脑

软路由R4SiStoreOS实现公网远程桌面局域网内电脑 文章目录 软路由R4SiStoreOS实现公网远程桌面局域网内电脑简介 一、配置远程桌面公网地址配置隧道 二、家中使用永久固定地址 访问公司电脑具体操作方法是:2.1 登录页面2.2 再次配置隧道2.3 查看访问效果 简介 上篇…

EDA实验-----3-8译码器设计(QuartusII)

目录 一. 实验目的 二. 实验仪器 三. 实验原理及内容 1.实验原理 2.实验内容 四.实验步骤 五. 实验报告 六. 注意事项 七. 实验过程 1.创建Verilog文件,写代码 ​编辑 2.波形仿真 3.连接电路图 4.烧录操作 一. 实验目的 学会Verilog HDL的…

JVM如何运行,揭秘Java虚拟机运行时数据区

目录 一、概述 二、程序计数器 三、虚拟机栈 四、本地方法栈 五、本地方法接口 六、堆 (一)概述 (二)堆空间细分 七、方法区 一、概述 不同的JVM对于内存的划分方式和管理机制存在部分差异,后续针对HotSpot虚…

前端案例-css实现ul中对li进行换行

场景描述: 我想要实现,在展示的item个数少于4个的时候,则排成一行,并且均分(比如说有3个,则每个的宽度为33.3%),如果item 个数大于4,则进行换行。 效果如下&#xff1a…

网络运维Day14

监控概述 监控的目的 报告系统运行状况每一部分必须同时监控内容包括吞吐量、反应时间、使用率等提前发现问题进行服务器性能调整前,知道调整什么找出系统的瓶颈在什么地方 监控的资源类别 公开数据 Web、FTP、SSH、数据库等应用服务TCP或UDP端口 私有数据 CPU、内…

【Java 进阶篇】JQuery DOM操作:舞动网页的属性魔法

在前端的舞台上,属性操作是我们与HTML元素进行互动的关键步骤之一。而JQuery,这位前端开发的巫师,通过简洁而强大的语法,为我们提供了便捷的属性操作工具。在这篇博客中,我们将深入研究JQuery DOM操作中的属性操作&…

Android Rxjava架构原理与使用的详解解答

简单介绍 Rxjava这个名字,其中java代表java语言,而Rx是什么意思呢?Rx是Reactive Extensions的简写,翻译过来就是,响应式拓展。所以Rxjava的名字的含义就是,对java语言的拓展,让其可以实现对数据…

【论文精读】Pose-Free Neural Radiance Fields via Implicit Pose Regularization

今天读的是一篇发表在ICCV 2023上的文章,作者来自NTU。 文章地址:点击前往 文章目录 Abstract1 Intro2 Related Work3 Preliminary4 Proposed Method4.1 Overall Framework4.2 Scene Codebook Construction4.3 Pose-Guided View Reconstruction4.4 Train…

HTML设置标签栏的图标

添加此图标最简单的方法无需修改内容,只需按以下步骤操作即可: 1.准备一个 ico 格式的图标 2.将该图标命名为 favicon.ico 3.将图标文件置于index.html同级目录即可 为什么我的没有变化? 答曰:ShiftF5强制刷新一下网页就行了

Python实现WOA智能鲸鱼优化算法优化卷积神经网络回归模型(CNN回归算法)项目实战

说明:这是一个机器学习实战项目(附带数据代码文档视频讲解),如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 鲸鱼优化算法 (whale optimization algorithm,WOA)是 2016 年由澳大利亚格里菲斯大学的Mirjalili 等提…

ELK之Logstash解析时间相差8h的问题

一、问题描述 服务器当前时间为:2022年 06月 28日 星期二 11:24:22 CST 而logstash解析的时间为2022-06-28T03:15:25.545Z与实际时间相差8h 一、解决办法: 需改logstash的配置文件: 原理就是:定义一个中间变量timestamp&…

Linux系统编程——标准c库对文件操作

Linux、标准c库对文件操作的区别 1.来源 从来源的角度看,两者能很好的区分开,这也是两者最显而易见的区别: open是UNIX系统调用函数 (包括LINUX等) ,返回的是文件描述符 (File Descriptor),它是文件在文件描述符表里的索引。.f…

postswigger 靶场(CSRF)攻略-- 1.没有防御措施的 CSRF 漏洞

靶场地址: What is CSRF (Cross-site request forgery)? Tutorial & Examples | Web Security Academy (portswigger.net)https://portswigger.net/web-security/csrf 没有防御措施的 CSRF 漏洞 题目中已告知易受攻击的是电子邮件的更改功能,而目…

搜索引擎项目

认识搜索引擎 1、有一个主页、有搜索框。在搜索框中输入的内容 称为“查询词” 2、还有搜索结果页,包含了若干条搜索结果 3、针对每一个搜索结果,都会包含查询词或者查询词的一部分或者和查询词具有一定的相关性 4、每个搜索结果包含好几个部分&…

数据库恢复技术

事务 含义:用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位 地位:恢复和控制并发的基本单位 区分事务和程序,一个程序中包含多个事务 定义事务 事务的开始与结束…

景联文科技加入中国人工智能产业发展联盟(AIIA),与行业各方共促AI产业发展

近日,景联文科技加入中国人工智能产业发展联盟(AIIA),与行业各方共同挖掘人工智能数据的更多价值,破解中国人工智能AI数据短缺难题。 中国人工智能产业发展联盟(简称AIIA)是在国家发改委、科技部…

PostgreSQL 机器学习插件 MADlib 安装与使用

MADlib 一个可以在数据库上运行的开源机器学习库,支持 PostgreSQL 和 Greenplum 等数据库;并提供了丰富的分析模型,包括回归分析,决策树,随机森林,贝叶斯分类,向量机,风险模型&#…