实战攻略 | ClickHouse优化之FINAL查询加速

【本文作者:擎创科技资深研发 禹鼎侯】

查询时为什么要加FINAL

我们在使用ClickHouse存储数据时,通常会有一些去重的需求,这时候我们可以使用ReplacingMergeTree引擎。这个引擎允许你存储重复数据,但是在merge的时候会根据order by的字段进行去重。

它的去重逻辑是使用新数据覆盖旧数据。

但是很多时候,merge并不是实时的,他可能会在数据插入后几分钟甚至十几分钟后才会进行,而我们对数据的查询却往往却是实时的。这个时候就必然出现一个问题:

那些还没有来得及merge的数据,查询出来会有重复。这与我们所期望的效果是背道而驰的。

事实上,即便是merge发生了,我们也不能保证数据一定没有重复。我们举个简单的例子:

如上图所示:p1~p4是四个原始part,发生合并之后数据进行了去重,最终合并成了p1_4, 对id去重之后有四个值,分别为1,2,3,4, 假设此时又有一个新part p5插入,此时去查询仍然能搜到两条id为2和3数据。

因此,即使发生了合并,我们也不能保证数据就一定是唯一的。

ClickHouse为了解决这个问题,提供了FINAL语法,从字面意义上理解,就是返回merge最终态的数据结果。它的效果与OPTIMIZE FINAL 是一致的。

不过,SELECT FINAL仅是在读时合并,并不会实际将底层数据合并,而OPTIMIZE FINAL则是实实在在的发生合并,这是二者的本质的区别。

FINAL查询会有什么问题

那么大家肯定也已经发现了。这种加了FINAL的查询,性能会有很大的问题!

因为FINAL相当于在查询时执行一次OPTIMIZE FINAL,而这个操作本质上是将同一个partition内的数据合并成一个part。在数据量不大的情况下还好,这个操作还能很快返回,如果数据集比较大,比如单个分区的数据已经达到了上千万级,甚至上亿级,那么一次FINAL可真是要了亲命了。

为了让大家直观的感受到这种性能差距,我们来举个例子。

我们使用ClickHouse官方提供的压测数据来进行举例:

https://clickhouse.com/docs/en/getting-started/example-datasets/opensky#validate-data

建表语句如下:

CREATE TABLE opensky(    `callsign` String,    `number` String,    `icao24` String,    `registration` String,    `typecode` String,    `origin` String,    `destination` String,    `firstseen` DateTime,    `lastseen` DateTime,    `day` DateTime,    `latitude_1` Float64,    `longitude_1` Float64,    `altitude_1` Float64,    `latitude_2` Float64,    `longitude_2` Float64,    `altitude_2` Float64)ENGINE = ReplacingMergeTreePARTITION BY toYYYYMMDD(day)ORDER BY (origin, destination, callsign)

我们将数据导入两遍,这样就一定会得到重复的数据:

ck94 :) select count() from opensky;
SELECT count()FROM opensky
Query id: 0d65affc-873e-4847-9ee0-ae749b091bd1
┌───count()─┐│ 127132244 │└───────────┘
1 row in set. Elapsed: 0.007 sec.

接下来我们来执行一个查询语句:

ck94 :) select avg(altitude_1) from opensky where day >= '2020-10-01 00:00:00' and typecode = 'B737';
SELECT avg(altitude_1)FROM openskyWHERE (day >= '2020-10-01 00:00:00') AND (typecode = 'B737')
Query id: bcb16bd6-91fc-4993-a4de-87e1731d7760
┌────avg(altitude_1)─┐│ 1458.7475907858743 │└────────────────────┘
1 row in set. Elapsed: 0.186 sec. Processed 34.74 million rows, 812.04 MB (186.88 million rows/s., 4.37 GB/s.)Peak memory usage: 14.39 MiB.

可以看到,当我们不使用final时,上面这个sql仅仅使用了0.186秒就返回了结果。

接下来我们看看加了final的效果:

ck94 :) select avg(altitude_1) from opensky final where day >= '2020-10-01 00:00:00' and typecode = 'B737';
SELECT avg(altitude_1)FROM openskyFINALWHERE (day >= '2020-10-01 00:00:00') AND (typecode = 'B737')
Query id: 63f760a6-9bca-4a40-a7b3-237eacae356a
┌────avg(altitude_1)─┐│ 1336.4280164372838 │└────────────────────┘
1 row in set. Elapsed: 12.784 sec. Processed 48.60 million rows, 3.06 GB (3.80 million rows/s., 239.37 MB/s.)Peak memory usage: 4.16 GiB.

上面这个SQL足足耗费了12.784秒!这可是近70倍的差距,我们当前的数据集还不算大,如果数据集再大一点,那么这个查询性能慢的问题将无限扩大化,最终影响到生产使用。

这让老夫如何是好

那么,这个问题如此明显,有没有什么好的优化手段呢?

自然是有的。

接下来就来介绍两种方法来做final查询的优化。

优化手段1:使用PREWHERE

所谓PREWHERE,就是在查询之前,先将数据过滤掉一部分,这样,目标数据集的规模小了,执行FINAL自然会快了。

我们使用EXPLAIN  SYNTAX 执行计划来看一下为啥第一条不加FINAL的SQL快得离谱:

EXPLAIN SYNTAXSELECT avg(altitude_1)FROM openskyWHERE (day >= '2020-10-01 00:00:00') AND (typecode = 'B737')
Query id: 046a721e-7690-4d0b-9457-20e825d0e152
┌─explain─────────────────────────────────────────────────────────┐│ SELECT avg(altitude_1)                                          ││ FROM opensky                                                    ││ PREWHERE (day >= '2020-10-01 00:00:00') AND (typecode = 'B737') │└─────────────────────────────────────────────────────────────────┘
3 rows in set. Elapsed: 0.003 sec. 

可以看到,上面这条SQL,实际上clickhouse优化器已经对其进行改写成了PREWHERE。

我们再看看加了FINAL的执行计划:

EXPLAIN SYNTAXSELECT avg(altitude_1)FROM openskyFINALWHERE (day >= '2020-10-01 00:00:00') AND (typecode = 'B737')
Query id: c4ae0a63-7a55-4216-815a-25ff44ee538e
┌─explain──────────────────────────────────────────────────────┐│ SELECT avg(altitude_1)                                       ││ FROM opensky                                                 ││ FINAL                                                        ││ WHERE (day >= '2020-10-01 00:00:00') AND (typecode = 'B737') │└──────────────────────────────────────────────────────────────┘

那就离了个大谱了!

为神马就加了个FINAL,他就不能使用PREWHERE优化了?

按照官方的说法: 如果查询条件里带有主键字段,clickhouse会默认会使用PREWHERE进行优化,可以提前减少数据集的大小,一来避免过多内存造成OOM,二来自然是可以加速查询了。

然而十分悲催的是,这个优化对加了FINAL的查询不会生效。

那么我们怎么能让它使用到PREWHERE优化呢?

这里提供两种方法:

一种是显式指定。

ck94 :) select avg(altitude_1) from opensky final prewhere day >= '2020-10-01 00:00:00' and typecode = 'B737';
SELECT avg(altitude_1)FROM openskyFINALPREWHERE (day >= '2020-10-01 00:00:00') AND (typecode = 'B737')
Query id: ce72c7a9-38a1-4ae8-957e-d951e0dd9d0b
┌────avg(altitude_1)─┐│ 1424.1051732278174 │└────────────────────┘
1 row in set. Elapsed: 1.429 sec. Processed 48.60 million rows, 3.06 GB (34.01 million rows/s., 2.14 GB/s.)Peak memory usage: 1.19 GiB.

可以看到,我们改成显式使用PREWHERE之后,查询性能立马减少到了1.429秒,差不多有9倍左右的提升,可见这个提升是巨大的。

第二是利用子查询命中PREWHERE。

那么,有木有什么办法不显式指定PREWHERE,但是让其有这个优化呢?而且我们并不能保证所有的条件都会带上主键索引。

前面介绍过,当查询条件带主键,且不加final的时候,clickhouse会默认使用PREWHERE进行优化,那么我们是不是可以先用子查询命中PREWHERE,然后再final呢?

我们将SQL改成如下样子:

ck94 :) select avg(altitude_1) from opensky where (origin, destination, callsign) in (select origin, destination, callsign from opensky where day >= '2020-10-01 00:00:00' and typecode = 'B737') and day >= '2020-10-01 00:00:00' and typecode = 'B737';
SELECT avg(altitude_1)FROM openskyWHERE ((origin, destination, callsign) IN (    SELECT        origin,        destination,        callsign    FROM opensky    WHERE (day >= '2020-10-01 00:00:00') AND (typecode = 'B737'))) AND (day >= '2020-10-01 00:00:00') AND (typecode = 'B737')
Query id: 61ae0590-de1a-44b1-866f-0c54a919c762
┌────avg(altitude_1)─┐│ 1458.7475907858748 │└────────────────────┘
1 row in set. Elapsed: 0.768 sec. Processed 69.47 million rows, 4.09 GB (90.43 million rows/s., 5.32 GB/s.)Peak memory usage: 69.15 MiB.

耗时降到了0.768秒,这已经非常接近不加final的裸查询了。牛逼class!

优化手段2:禁用final查询跨分区merge

ClickHouse本身是允许跨分区进行数据替换的,这无疑复杂化了FINAL查询的逻辑。但事实上,只要我们的数据分区合理,这种情况完全可以规避掉。这时候我们可以使用do_not_merge_across_partitions_select_final=1这个配置来禁用跨final查询时分区合并。这意味着clickhouse仅在本分区内进行merge去重。

我们来看看效果:

ck94 :) select avg(altitude_1) from opensky final where day >= '2020-10-01 00:00:00' and typecode = 'B737' SETTINGS do_not_merge_across_partitions_select_final=1;
SELECT avg(altitude_1)FROM openskyFINALWHERE (day >= '2020-10-01 00:00:00') AND (typecode = 'B737')SETTINGS do_not_merge_across_partitions_select_final = 1
Query id: 41aa4fc9-5933-43e1-86bf-a374eab15dd9
┌────avg(altitude_1)─┐│ 1458.6043678101414 │└────────────────────┘
1 row in set. Elapsed: 0.732 sec. Processed 34.74 million rows, 2.18 GB (47.44 million rows/s., 2.98 GB/s.)Peak memory usage: 2.86 GiB.
仅耗时0.732秒,同样牛的一批。

事实上,我们建议在部署集群时,将这个配置作为默认配置进行设置,在知名项目clickhouse-operator 中,这个配置就是默认打开的。

那么,综合上面两种优化手段,这就是最终形态了:

ck94 :) select avg(altitude_1) from opensky where (origin, destination, callsign) in (select origin, destination, callsign from opensky where day >= '2020-10-01 00:00:00' and typecode = 'B737') and day >= '2020-10-01 00:00:00' and typecode = 'B737' SETTINGS do_not_merge_across_partitions_select_final = 1;
SELECT avg(altitude_1)FROM openskyWHERE ((origin, destination, callsign) IN (    SELECT        origin,        destination,        callsign    FROM opensky    WHERE (day >= '2020-10-01 00:00:00') AND (typecode = 'B737'))) AND (day >= '2020-10-01 00:00:00') AND (typecode = 'B737')SETTINGS do_not_merge_across_partitions_select_final = 1
Query id: 298c3d92-67da-4379-8e1d-43f85c99cc2c
┌────avg(altitude_1)─┐│ 1458.7475907858745 │└────────────────────┘
1 row in set. Elapsed: 0.678 sec. Processed 69.47 million rows, 4.09 GB (102.45 million rows/s., 6.03 GB/s.)Peak memory usage: 69.89 MiB.

虽然还是比不加final的要慢一丢丢,但总体上已经到了可以接受的程度了。

———— THE END ————

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

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

相关文章

加入GitHub Spark需要申请

目录 加入GitHub Spark需要申请 GitHub Spark 一、产品定位与特点 二、核心组件与功能 三、支持的AI模型 四、应用场景与示例 五、未来展望 六、申请体验 加入GitHub Spark需要申请 GitHub Spark 是微软旗下GitHub在2024年10月30日的GitHub Universe大会上推出的一款革…

鸿蒙与团结引擎c#与ts简单交互

目录 团结中调用ts代码 鸿蒙中调用团结代码 首先在团结创建代码,需要将代码添加到场景物体中 devecoStudio端编写ts代码 在index页面添加一个测试按钮 团结中调用ts代码 团结引擎 - 手册: Call TypeScript plug-in code from C# scripts 注册函数要跟文件名一致 在u…

Qt QCustomplot 在采集信号领域的应用

文章目录 一、常用的几种开源库:1、QCustomPlot:2、QChart:3、Qwt:QCustomplot 在采集信号领域的应用1、应用实例时域分析频谱分析2.数据筛选和处理其他参考自然界中的物理过程、传感器和传感器网络、电路和电子设备、通信系统等都是模拟信号的来源。通过可视化模拟信号,可以…

世优科技携手人民中科打造AI数字人智能体助力智慧校园

近日,世优科技与人民中科携手,为中国劳动关系学院开发了一款AI数字人助手,不仅在校园内部承担日常问询、交互工作,还在学校的展厅中担任讲解员的角色,为师生们提供生动详尽的导览服务。 中国劳动关系学院作为中华全国总…

7.2、实验二:被动接口和单播更新

源文件链接: 7.2、实验二:被动接口和单播更新: https://url02.ctfile.com/d/61945102-63671890-6af6ec?p2707 (访问密码: 2707) 一、被动接口 1.介绍 定义: 在路由协议的配置中,一个被动接口指的是一个接口不发送路由更新包的配置方式&a…

达梦8-达梦数据实时同步软件(DMHS)配置-Oracle-DM8

1、安装环境 源端目的端IP地址192.168.6.111192.168.6.110系统版本Red Hat 6.4Kylin v10数据库版本Oracle11g达梦 v8系统用户Oracledmdba字符集MERICAN_AMERICA.AL32UTF8UTF-8端口15215236实例名PRODDMSERVER数据库软件目录/u01/app/oracle/opt/dmdbmsDMHS安装目录/u01/dmhs/o…

多个NVR同时管理EasyNVR多品牌NVR管理工具/设备:IP常见问题解决方案

随着视频监控技术的不断发展,NVR(网络视频录像机)已经成为现代安防系统的重要组成部分。而为了更高效地管理多个品牌的NVR设备,EasyNVR这一多品牌NVR管理工具应运而生。然而,在实际使用过程中,尤其是在多个…

新160个crackme - 094-TheBigMan-crackme6

运行分析 需破解Name和Serial PE分析 LCC win32程序,32位,无壳 静态分析&动态调试 ida搜索字符串,进入关键函数 ida动调,发现关键判断函数func_1 进入后,发现Name长度需满足一定要求,且func_2返回值不能…

软考系统分析师知识点三七:今日考试

前言 今年报考了11月份的软考高级:系统分析师。 考试时间:11月9日。 今日考试。 今日考试 上午:选择题、案例题,注意记一下选择题和案例题中出现的知识点。 中午:再次整理强记一下论文框架、论文知识点 下午&…

u8g2操作流程和相关问题总结

1、下载官方源码实例 GitHub - olikraus/u8g2: U8glib library for monochrome displays, version 2 2、将目录中的csrc拷贝到工程文件夹里 3、裁剪代码 将u8x8_d_XXX.c 的文件,是自己硬件的保留,其他的都可以删掉。 4、裁剪文件的代码

Github 2024-11-05 Python开源项目日报Top10

根据Github Trendings的统计,今日(2024-11-05统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目10HTML项目1TypeScript项目1系统设计指南 创建周期:2507 天开发语言:Python协议类型:OtherStar数量:241693 个Fork数量:42010 次…

Oracle视频基础1.4.4练习

1.4.4 [dbs] 删干净上次创建的bbk ll rm -f *dbf ll rm -f spfilebbk.ora clear ll创建bbk的pfile,准备对应的目录 ll strings spfilewilson.ora | more strings spfilewilson.ora > initbbk.ora :%s/wilson/bbk :%s/*\.//g :wq ll vi initbbk.ora####### 创…

跨境电商独立站怎么建?如何收款?

独立站是相对于平台电商以及近年迅猛发展的社交电商而言的。 平台站就是在亚马逊、Lazada、速卖通、Temu等电商平台上开设店铺的站点,社交电商则是依托社交媒体的流量衍生的电商平台,TikTok Shop便是典型代表。 一、什么是独立站 独立站,简…

毕设 深度学习遮挡下的人脸识别(源码+论文)

文章目录 0 前言1 项目运行效果2 设计概要4 最后 0 前言 🔥这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师…

Charles简单压力测试

1.接口请求次数,并发量,请求延迟时间均可配置 1.1选中需要进行测试的接口,鼠标右键选中【repeat advance】 2.设置并发参数 下面的图中,选择了1个接口,每次迭代中1个接口同时请求,迭代1000次(…

UE5.4 PCG 获取地形Layer

使用AttributeFilter:属性过滤器 节点 设置地形Layer名称和权重 效果:

isc-dhcp-server

#dhcps #安装 apt install isc-dhcp-server #topo # 创建命名空间 ip netns add ns #创建veth接口 ip link add ns-veth0 type veth peer name h-veth0 #ns-veth0放入命名空间 ip link set ns-veth0 netns ns #配置ns的接口 ip -netns ns link set ns-veth0 up ip -netns ns …

网络安全技术及其在企业中的应用

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 网络安全技术及其在企业中的应用 网络安全技术及其在企业中的应用 网络安全技术及其在企业中的应用 引言 网络安全技术概述 定义…

《深度学习神经网络:颠覆生活的魔法科技与未来发展新航向》

深度学习神经网络对我们生活的影响 一、医疗领域 深度学习神经网络在医疗领域的应用可谓意义重大。在疾病诊断方面,它能够精准分析医疗影像,如通过对大量的 CT、MRI 图像进行深度学习,快速准确地识别出微小的肿瘤病变,为医生提供…

【综合案例】使用React编写B站评论案例

一、效果展示 默认效果,一开始默认按照最热进行排序 发布了一条评论 按照最新进行排序 按照最新进行排序 二、效果说明 页面上默认有3条评论,且一开始进入页面的时候是按照点赞数量进行倒序排列展示,可以点击【最热 、最新】进行排序的切换。…