ClickHouse SQL 查询优化

单表查询

1.1 Prewhere替代where

Prewherewhere语句的作用相同用来过滤数据。不同之处在于prewhere只支持 *MergeTree 族系列引擎的表,首先会读取指定的列数据,来判断数据过滤,等待数据过滤之后再读取select 声明的列字段来补全其余属性。

当查询列明显多于筛选列时使用Prewhere可十倍提升查询性能,Prewhere会自动优化执行过滤阶段的数据读取方式,降低io操作。

在某些场合下,prewhere语句比where语句处理的数据量更少性能更高。

#关闭where自动转prewhere(默认情况下, where条件会自动优化成prewhere)
set optimize_move_to_prewhere=0; 
# 使用where
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';# 使用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 prewhere UserID='3198390223272470366';

默认情况,我们肯定不会关闭where自动优化成prewhere,但是某些场景即使开启优化,也不会自动转换成prewhere,需要手动指定prewhere:

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

1.2 数据采样

通过采样运算可极大提升数据分析的性能

SELECT Title,count(*) AS PageViews 
FROM hits_v1
SAMPLE 0.1         #代表采样10%的数据,也可以是具体的条数
WHERE CounterID =57
GROUP BY Title
ORDER BY PageViews DESC LIMIT 1000

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

1.3 列裁剪与分区裁剪

数据量太大时应避免使用select * 操作,查询的性能会与查询的字段大小和数量成线性表换,字段越少,消耗的io资源越少,性能就会越高。

反例:
select * from datasets.hits_v1;
正例:
select WatchID, 
    JavaEnable, Title, 
    GoodEvent, 
    EventTime, 
    EventDate, 
    CounterID, 
    ClientIP, 
    ClientIP6, 
    RegionID, 
    UserID
from datasets.hits_v1;

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

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

1.4 orderby 结合 where、limit

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

#正例:
SELECT UserID,Age
FROM hits_v1        
WHERE CounterID=57
ORDER BY Age DESC LIMIT 1000#反例:
SELECT UserID,Age
FROM hits_v1        
ORDER BY Age DESC

1.5 避免构建虚拟列

如非必须,不要在结果集上构建虚拟列,虚拟列非常消耗资源浪费性能,可以考虑在前端进行处理,或者在表中构造实际字段进行额外存储。

反例:
SELECT Income,Age,Income/Age as IncRate FROM datasets.hits_v1;
正例:拿到Income和Age后,考虑在前端进行处理,或者在表中构造实际字段进行额外存储
SELECT Income,Age FROM datasets.hits_v1;

1.6 uniqCombined替代distinct

性能可提升10倍以上,uniqCombined底层采用类似HyperLogLog算法实现能接收2%左右的数据误差可直接使用这种去重方式提升查询性能。Count(distinct )会使用uniqExact精确去重。

不建议在千万级不同数据上执行distinct去重查询,改为近似去重uniqCombined

反例:
select count(distinct rand()) from hits_v1;
正例:
SELECT uniqCombined(rand()) from  datasets.hits_v1

1.7 使用物化视图

参考第6章。

1.8 其他注意事项

(1)查询熔断

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

(2)关闭虚拟内存

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

(3)配置join_use_nulls

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

(4)批量写入时先排序

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

(5)关注CPU

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

多表关联

2.1 准备表和数据

#创建小表
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;

2.2 用 IN 代替 JOIN

当多表联查时查询的数据仅从其中一张表出时可考虑用 IN 操作而不是JOIN

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;

2.3 大小表JOIN

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

(1)小表在右

insert into table hits_v2

select a.* from hits_v1 a left join visits_v2 b on a. CounterID=b. CounterID;

2大表在右

insert into table hits_v2

select a.* from visits_v2 b left join hits_v1 a on a. CounterID=b. CounterID;

2.4 注意谓词下推(版本差异)

ClickHouse在join查询时不会主动发起谓词下推的操作,需要每个子查询提前完成过滤操作,需要注意的是,是否执行谓词下推,对性能影响差别很大(新版本中已经不存在此问题,但是需要注意谓词的位置的不同依然有性能的差异)

Explain syntax
select a.* from hits_v1 a left join visits_v2 b on a. CounterID=b. CounterID
having a.EventDate = '2014-03-17';Explain syntax
select a.* from hits_v1 a left join visits_v2 b on a. CounterID=b. CounterID
having b.StartDate = '2014-03-17';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;

2.5 分布式表使用GLOBAL

两张分布式表上的IN和JOIN之前必须加上GLOBAL关键字,右表只会在接收查询请求的那个节点查询一次,并将其分发到其他节点上。如果不加GLOBAL关键字的话,每个节点都会单独发起一次对右表的查询,而右表又是分布式表,就导致右表一共会被查询N²次(N是该分布式表的分片数量),这就是查询放大,会带来很大开销。

2.6 使用字典表

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

2.7 提前过滤

通过增加逻辑过滤可以减少数据扫描,达到提高执行速度及降低内存消耗的目的

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

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

相关文章

自动驾驶学习笔记(九)——车辆控制

#Apollo开发者# 学习课程的传送门如下,当您也准备学习自动驾驶时,可以和我一同前往: 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo Beta宣讲和线下沙龙》免费报名—>传送门 文章目录 前言 控制器设计 比例积分微分控制 线性…

在 Linux 环境下的简单调试技巧

在 Linux 环境下的简单调试技巧 GDB(GNU调试器)是一个强大的命令行调试工具,用于调试C、C等程序。下面是使用GDB的一些基本步骤: 编译程序时包含调试信息 确保在编译程序时使用 -g 选项来包含调试信息。例如: gcc -g …

Kotlin 知识体系

Kotlin 知识体系 1、Kotlin 文档2、Kotlin 基础3、桌面应用程序4、Android 与 iOS 应用程序 1、Kotlin 文档 Kotlin 是一门现代但已成熟的编程语言,旨在让开发人员更幸福快乐。 它简洁、安全、可与 Java 及其他语言互操作,并提供了多种方式在多个平台间复…

『亚马逊云科技产品测评』活动征文|借助AWS EC2搭建服务器群组运维系统Zabbix+spug

授权声明:本篇文章授权活动官方亚马逊云科技文章转发、改写权,包括不限于在 Developer Centre, 知乎,自媒体平台,第三方开发者媒体等亚马逊云科技官方渠道。 本文基于以下软硬件工具: aws ec2 frp-0.52.3 zabbix 6…

LRU最近最少使用算法

LRU(LeastRecentlyUsed)“最近最少使用”算法: 1.当缓存空间已满耗用时,淘汰最近最少使用数据的缓存对象以释放更多的缓存空间(用于历史缓存对象的维护)。 2. 哈希表:快速查找缓存对象;双向链表:维护 历史数据所在的节点顺序。 步骤&#xff…

掌握深度学习利器——TensorFlow 2.x实战应用与进阶

掌握深度学习利器——TensorFlow 2.x实战应用与进阶 摘要:随着人工智能技术的飞速发展,深度学习已成为当下最热门的领域之一。作为深度学习领域的重要工具,TensorFlow 2.x 备受关注。本文将通过介绍TensorFlow 2.x的基本概念和特性&#xff…

在 Linux 上搭建 Java Web 项目环境(最简单的进行搭建)

要在 Linux 上安装的程序有 1.JDK (要想运行 java 程序 JDK 是必不可少的) 2.Tomcat (HTTP 服务器,是管理 Web 项目的常用工具) 3. mysql (数据库) 一.安装 JDK 博主使用的 Linux 发行版是 centos ,cen…

母婴服务预约小程序的效果如何

二胎家庭增速明显,占比较大,成为市场各母婴品牌的目标,而随着行业发展及市场变化,线上互联网深入人们生活,各家母婴品牌开始向“数字化”靠拢。 目前母婴门店商家主要面临服务/产品线上曝光不足、宣传度不够或扩圈无门…

【Kingbase FlySync】命令模式:安装部署同步软件,实现KES到KES实现同步

【Kingbase FlySync】命令模式:安装部署同步软件,实现KES到KES实现同步迁移 概述准备环境目标资源1.测试虚拟机下载地址包含node1,node22.同步工具下载地址3.临时授权下载地址4.ruby工具下载地址5.EXAMv0.11.sql下载地址 实操:同步软件安装部署1.node1准…

git rebase 和 git merge的区别?以及你对它们的理解?

文章目录 前言是什么分析区别后言 前言 hello world欢迎来到前端的新世界 😜当前文章系列专栏:git操作相关 🐱‍👓博主在前端领域还有很多知识和技术需要掌握,正在不断努力填补技术短板。(如果出现错误,感谢…

【论文解读】FFHQ-UV:用于3D面部重建的归一化面部UV纹理数据集

【论文解读】FFHQ-UV 论文地址:https://arxiv.org/pdf/2211.13874.pdf 0. 摘要 我们提出了一个大规模的面部UV纹理数据集,其中包含超过50,000张高质量的纹理UV贴图,这些贴图具有均匀的照明、中性的表情和清洁的面部区域,这些都是…

mybatis动态sql语法

<?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace"com.qvfan.mybatistest.mapper.Emp…

基于深度学习的恶意软件检测

恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞&#xff0c;例如可以被劫持的合法软件&#xff08;例如浏览器或 Web 应用程序插件&#xff09;中的错误。 恶意软件渗透可能会造成灾难性的后果&#xff0c;包括数据被盗、勒索或网…

sqli-labs关卡18(基于http头部报错盲注)通关思路

文章目录 前言一、靶场通关需要了解的知识点1、什么是http请求头2、为什么http头部可以进行注入 二、靶场第十八关通关思路1、判断注入点2、爆数据库名3、爆数据库表4、爆数据库列5、爆数据库关键信息 总结 前言 此文章只用于学习和反思巩固sql注入知识&#xff0c;禁止用于做…

LV.12 D18 中断处理 学习笔记

一、ARM的异常处理机制及工程代码结构 1.1异常概念 处理器在正常执行程序的过程中可能会遇到一些不正常的事件发生 这时处理器就要将当前的程序暂停下来转而去处理这个异常的事件 异常事件处理完成之后再返回到被异常打断的点继续执行程序。 1.2异常处理机制 不同的处…

【Python】解析CPP类定义代码,获取UML类图信息

参考 & 鸣谢 CppHeaderParser - 官方文档Python解析C头文件win10直接获得文件绝对路径的方法总结 目的 解析CPP头文件中的类定义&#xff0c;获取UML中的属性。用于画UML类图。如下所示格式&#xff0c;图片来源-链接 即获取&#xff0c;类名&#xff0c;成员函数&#x…

H110主板搭配魔改QNCW升级小记

最近搬家完毕&#xff0c;翻出来一块闲置已久的qncw&#xff0c;隐约记得是买的主板套装&#xff0c;现在主板早已不知踪影&#xff0c;剩下孤零零一个CPU&#xff0c;一起翻出来一个G3900T亮机CPU&#xff0c;应该是同时代的产物。 qncw百度上一搜&#xff0c;发现参数还行&am…

【ES6标准入门】JavaScript中的模块Module语法的使用细节:export命令和imprt命令详细使用,超级详细!!!

&#x1f601; 作者简介&#xff1a;一名大四的学生&#xff0c;致力学习前端开发技术 ⭐️个人主页&#xff1a;夜宵饽饽的主页 ❔ 系列专栏&#xff1a;JavaScript进阶指南 &#x1f450;学习格言&#xff1a;成功不是终点&#xff0c;失败也并非末日&#xff0c;最重要的是继…

如何将vscode和Linux远程链接:

如何将vscode和Linux远程链接&#xff1a; Remote - SSH - 远程登录Linux 安装Remote - SSH 我们下载完后&#xff0c;就会出现这些图标 这里点一下号 查看一下我们的主机名&#xff0c;并复制 输入ssh 用户名主机名 这里是要将ssh这个文件要放在主机下的哪个路径下&#xff…

Android 10.0 系统修改usb连接电脑mtp和PTP的显示名称

1.前言 在10.0的产品定制化开发中,在usb模块otg连接电脑,调整为mtp文件传输模式的时候,这时可以在电脑看到手机的内部存储 显示在电脑的盘符中,会有一个mtp名称做盘符,所以为了统一这个名称,就需要修改这个名称,接下来分析下处理的 方法来解决这个问题 2.系统修改usb连…