摘要: GaussDB(for influx)提供灵活的子查询能力,满足海量数据场景下的高性能查询需求。

本文分享自华为云社区《华为云GaussDB(for Influx)揭秘第五期:最佳实践之子查询》,作者: GaussDB 数据库 。

"告警了!告警了!"。

"什么告警了?"正在睡梦中迷糊的小王突然被运维同事的一个电话叫醒,顿时一脸惊愕。

"慢查询!客户报障了!赶紧起来处理啊!"

小王赶紧打开便携,远程连上环境查找问题所在,最终发现该慢查询是一条子查询。"不对啊,相同语句昨天还没报慢查询啊?"

不过小王很快得出了原因。这条慢查询的问题在于:子查询的内部查询本来可以将数据汇聚后再输出到外部查询,但由于没有做汇聚,因此当数据量大的时候,就会很慢!

找到症结所在,小王立马将优化后的sql语句通过运维同事转给客户,该告警终于得以解决。

"看来得好好整理下子查询了!",趁着思路清晰,小王开始整理了起来......

01 什么是子查询?

子查询是嵌套在另一个查询中的查询,在InfluxQL语法中一般放在from语句中,以增强代码的灵活性。子查询主要有以下几大分类:

  • 标量子查询(scalarsubquery):返回1行1列一个值
  • 行子查询(rowsubquery):返回的结果集是 1 行 N 列
  • 列子查询(columnsubquery):返回的结果集是 N 行 1列
  • 表子查询(tablesubquery):返回的结果集是 N 行 N 列

例如在查询语句:

select first(sum_f1)/first(sum_f2) from (select sum(f1) as sum_f1 from mst), (select sum(f2) as sum_f2 from mst)

使用了两个子查询,分别是从表mst中求得f1和f2两列之和,并将结果sum_f1和sum_f2作为外部查询的源,供外层查询语句使用。

GaussDB(for Influx)子查询的一般语法为SELECT_clause FROM (SELECT_statement ) [...]。在处理子查询时的逻辑如下图所示。

系统会首先处理子查询语句,子查询的结果被缓存起来,作为外查询的数据源,最终由外层查询处理完成后将结果返回给客户。

02 子查询的使用场景

子查询用在一次简单查询无法处理的情况下,或者是基于一个查询的数据做进一步的处理,比如想找出每个分组的最小值中最大的三个:

SELECT top (v,3)
FROM (
SELECT min (value) AS v
FROM mst
GROUP BY tag1
)

子查询为我们带来了很大的灵活性,但是原则上不推荐使用子查询。原因很简单,相比于普通的查询来说,子查询有更深的函数调用和更大的数据量,消耗的资源和时延都会增加。

03 案例剖析

我们在使用GaussDB(for Influx)开发的过程中,常常会面临一些子查询方面的困扰,例如:

1. 什么时候使用子查询?

2. 面对一个复杂场景,如何分解成为子查询来解决?

3. 写好的子查询是最优的么?可不可以再优化?

接下来我们结合一个具体案例来简单分析下如何高效的使用子查询和分析思路。

华为云某用户使用GaussDB(for Influx),每天写入约5.4亿个点,时间线100w+,业务中有时空查询,请求成功率查询,topN查询。

以下面脱敏数据作为sample数据做案例分析和实践:

案例1 什么时候使用子查询?

用户使用了子查询做时空分组并且作为外查询的源,外查询将时空分组的结果做聚合。查询语句为:

SELECT SUM(req_nums)
FROM(
SELECT requestNum AS req_nums
FROM req_table
WHERE statement=’SUCCESS’ AND time >= 1629129600000000000
AND time<=1629129611000000000 )
WHERE time>=1629129600000000000 AND time<=1629129611000000000
AND req_nums < 50
GROUP BY time(1s), group
ORDER BY time ASC

产生的问题:

用户的使用场景下,可以发现该查询子查询内部仅仅实现了条件过滤和列名更改,因此内部查询等同于SELECT requestNum AS req_nums + 过滤, 非聚合场景的查询由于需要捞出大量原始数据导致查询速度较慢,因此该查询效率达不到用户的要求。

解决思路:

通过分析查询语句可知,用户的需求是将符合条件(statement=’SUCCESS’ AND requestNum < 50)的数据做一个时空聚合(GROUPBY TAG, time(5m)),明确了查询目标之后能写出更加清晰高效的查询语句:将所有的过滤条件放在一起,直接做时空聚合。

语法改进:

SELECT SUM(requestNum)
FORM req_table
WHERE statement=’SUCCESS’ AND requestNum < 50
AND time>=1629129600000000000 AND time<=1629129611000000000
GROUP BY time(1s), group
ORDER BY time ASC

案例2 使用子查询解决复杂问题

用户的业务场景中需要计算请求成功率,即按照不同的过滤条件对某一列数据进行筛选和计数,最后求出比例。GaussDB(for Influx)不支持case when语句,因此如何根据不同case过滤出同一列的不同数据是一个难点。很多开发者碰到这样的问题时,便没了思路。

解决思路:

第一步:利用子查询+多表特性,根据过滤条件将同一列数据变为两列:

SELECT * FROM
(SELECT requestNum AS success_requestNum FROM req_table WHERE statement=’SUCCESS’ AND time>=1629129600000000000 AND time<=1629129611000000000),
(SELECT requestNum AS total_requestNum FROM req_table WHERE time>=1629129600000000000 AND time<=1629129611000000000)

第二步:对查询出来的数据进行计数:

SELECT SUM(success_requestNum) AS total_success_reqNum, SUM(total_requestNum) AS total_requestNum
FROM
(SELECT requestNum AS success_requestNum FROM req_table WHERE statement=’SUCCESS’ AND time>=1629129600000000000 AND time<=1629129611000000000),
(SELECT requestNum AS total_requestNum FROM req_table WHERE time>=1629129600000000000 AND time<=1629129611000000000)
GROUP BY time ASC

第三步:写出最终求成功率的查询语句:

SELECT SUM(success_requestNum)/SUM(total_requestNum) AS success_ratio
FROM
(SELECT requestNum AS success_requestNum FROM req_table WHERE statement=’SUCCESS’ AND time>=1629129600000000000 AND time<=1629129611000000000),
(SELECT requestNum AS total_requestNum FROM req_table WHERE time>=1629129600000000000 AND time<=1629129611000000000)
GROUP BY time ASC

案例3 如何优化子查询语句?

基于案例2,我们得到了求成功率的方法,查询语句如下:

SELECT SUM(success_requestNum)/SUM(total_requestNum) AS success_ratio
FROM
(SELECT requestNum AS success_requestNum FROM req_table WHERE statement=’SUCCESS’ AND time>=1629129600000000000 AND time<=1629129611000000000),
(SELECT requestNum AS total_requestNum FROM req_table WHERE time>=1629129600000000000 AND time<=1629129611000000000)
GROUP BY time ASC

产生的问题:

用户所写查询语句查询时长高于120s不满足业务需求,需要进一步优化。

语法改进:

根据前面所述的子查询原则和解决方法,应当把聚合查询放到子查询内部来减少数据量加快查询速度,优化后的查询语句如下:

SELECT SUM(success_requestNum)/SUM(total_requestNum) AS success_ratio
FROM
(SELECT SUM(requestNum) AS success_requestNum FROM req_table WHERE statement=’SUCCESS’ AND time>=1629129600000000000 AND time<=1629129611000000000),
(SELECT SUM(requestNum) AS total_requestNum FROM req_table WHERE time>=1629129600000000000 AND time<=1629129611000000000)
GROUP BY time ASC

查询结果一致:

优化效果:

未优化查询耗时126s,优化后查询耗时2.7s,性能提升47倍。

注意

使用SUM(success_requestNum),SUM(total_requestNum)的目的是为了让数据对齐。直接使用SELECTsuccess_requestNum / total_requestNum,会因为相同时间数据无法对齐而出现结果不正确的情况:

SELECT *
FROM
(SELECT SUM(requestNum) AS success_requestNum FROM req_table WHERE statement=’SUCCESS’ AND time>=1629129600000000000 AND time<=1629129611000000000),
(SELECT SUM(requestNum) AS total_requestNum FROM req_table WHERE time>=1629129600000000000 AND time<=1629129611000000000)
GROUP BY time ASC

查询的总数据量与查询速度正相关,越大的数据查询量意味着越慢的查询速度,因此无论是书写子查询还是非子查询的查询语句,第一原则是尽量在查询中减少数据量,也就意味着聚合查询(典型减少数据量的查询)应当尽可能放到子查询内部。

04 灵活的子查询和高性能

GaussDB(for Influx)不但提供灵活的子查询能力,同时还使用了向量化、内存复用等技术不断提升查询的效率,满足了用户海量数据场景下的查询性能需求。

向量化查询:GaussDB(for Influx) 使用了SIMD指令集,提高数据处理的并行化程度。与此同时,采用向量化数据模型,一次迭代可以处理一批次的点,极大减少了计算迭代次数,加快了计算速度。

内存复用:在查询过程中尽可能减少GC对内存的回收和分配,申请的内存单独管理,解决了查询过程中内存膨胀导致GC频繁降低查询速度的问题。

05 总结

GaussDB(for Influx)支持子查询功能给我们处理问题带来了很大的灵活性,同时对使用者也有很高的要求,不合理的子查询往往会导致查询时延高,资源消耗大等问题,因此在使用GaussDB(for Influx)子查询时应该注意以下几点:

1. 理解子查询适用的业务逻辑,子查询适用于对查询出来的数据做二次(多次)处理的场景;
2. 能不使用子查询的场景下尽量避免使用子查询;
3. 必须使用子查询的场景尽量将减少数据量的查询放到子查询内部以减少整体的查询数据量从而加快查询速度。

06 结束

本文作者:华为云数据库创新Lab & 华为云时空数据库团队
欢迎加入我们!
云数据库创新Lab(成都、北京)简历投递邮箱:xiangyu9@huawei.com
华为云时空数据库团队(西安、深圳)简历投递邮箱:yujiandong@huawei.com

点击关注,第一时间了解华为云新鲜技术~

华为云GaussDB(for Influx)揭秘第五期:最佳实践之子查询的更多相关文章

  1. 揭秘华为云GaussDB(for Influx)最佳实践:hint查询

    摘要:GaussDB(for Influx)通过提供hint功能,在单时间线的查询场景下,性能有大幅度的提升,能有效满足客户某些特定场景的查询需求. 本文分享自华为云社区<华为云GaussDB( ...

  2. 揭秘华为云GaussDB(for Influx):数据直方图

    摘要:本文带您了解直方图在不同产品中的实现,以及GaussDB(for Influx)中直方图的使用方法. 本文分享自华为云社区<华为云GaussDB(for Influx)揭秘第九期:最佳实践 ...

  3. 华为云PB级数据库GaussDB(for Redis)揭秘第八期:用高斯 Redis 进行计数

    摘要:高斯Redis,计数的最佳选择! 一.背景 当我们打开手机刷微博时,就要开始和各种各样的计数器打交道了.我们注册一个帐号后,微博就会给我们记录一组数据:关注数.粉丝数.动态数-:我们刷帖时,关注 ...

  4. 华为云PB级数据库GaussDB(for Redis)揭秘第七期:高斯Redis与强一致

    摘要:在KV数据库领域,"强一致性"不仅是一个技术名词,它更是业务与运维的重要需求. 清明刚过,五一假期就要来了.大好春光,不如去婺源看油菜花吧!小云迅速打开APP刷出余票2张,赶 ...

  5. 升级的华为云“GaussDB”还能战否?

    摘要:芯片.操作系统.数据库是现代信息技术领域的三大核心基础,做数据库,不仅需要技术和投入,对华为这种做通讯起家的企业,更需要的是一种并非玩票性质的态度. GaussDB,不仅蕴含着华为对数学和科学的 ...

  6. 华为云GaussDB(DWS)内存知识点,你知道吗?

    前言 在日常数据库的使用中,难免会遇到一些内存问题.此次博文主要向大家分享一些华为云数仓GaussDB(DWS)内存的基本框架以及基本视图的使用,以便遇到内存问题后可以有一个基本的判断. 注意,本篇博 ...

  7. 华为云 MRS 基于 Apache Hudi 极致查询优化的探索实践

    背景 湖仓一体(LakeHouse)是一种新的开放式架构,它结合了数据湖和数据仓库的最佳元素,是当下大数据领域的重要发展方向. 华为云早在2020年就开始着手相关技术的预研,并落地在华为云 Fusio ...

  8. TOP100summit:【分享实录-华为】微服务场景下的性能提升最佳实践

    本篇文章内容来自2016年TOP100summit华为架构部资深架构师王启军的案例分享.编辑:Cynthia 王启军:华为架构部资深架构师.负责华为的云化.微服务架构推进落地,前后参与了华为手机祥云4 ...

  9. 阿里云RDS for SQL Server使用的一些最佳实践

    了解RDS的概念 这也是第一条,也是最重要的一条,在使用某项产品和服务之前,首先要了解该产品或服务的功能与限制,就像你买一个冰箱或洗衣机,通常也只有在阅读完说明书之后才能利用起来它们的所以功能,以及使 ...

  10. 轻量ORM-SqlRepoEx (十五)最佳实践之数据映射(Map)

    简介:SqlRepoEx是 .Net平台下兼容.NET Standard 2.0人一个轻型的ORM.解决了Lambda转Sql语句这一难题,SqlRepoEx使用的是Lambda表达式,所以,对c#程 ...

随机推荐

  1. 让物体动起来,Unity的几种移动方式

    一.前言 在大部分的Unity游戏开发中,移动是极其重要的一部分,移动的手感决定着游戏的成败,一个优秀的移动手感无疑可以给游戏带来非常舒服的体验.而Unity中有多种移动方法,使用Transform, ...

  2. QT(1)- QString

    QT(1)- QString 1 简介 在Qt中表示字符串的类是QString类,它存储字符串是采用的Unicode码,编码方式是使用UTF-16来进行编码的,也就是一个字符(两个字节),一个中文汉字 ...

  3. HTML-8

    (一)引用数据类型 object function array object JavaScript对象用花括号来书写 对象属性是name:value由逗号分隔 var x={firstname:&qu ...

  4. Transformers 中原生支持的量化方案概述

    本文旨在对 transformers 支持的各种量化方案及其优缺点作一个清晰的概述,以助于读者进行方案选择. 目前,量化模型有两个主要的用途: 在较小的设备上进行大模型推理 对量化模型进行适配器微调 ...

  5. Basic concepts of complex number

    目录 虚数的引入 复数和虚数的关系 Example - 分辨一个数 判断两个复数是否相等的条件 共轭复数 复数的几何意义.复平面的认识 求复数的模 Reference 虚数的引入 假设有一个数,可以叫 ...

  6. 浅谈仓储UI自动化之路

    1 分层测试 分层测试:就是不同的时间段,不同的团队或团队使用不同的测试用例对产品不同的关注点进行测试.一个系统/产品我们最先看到的是UI层,也就是外观或者说整体,这些是最上层,最上层依赖下面的服务层 ...

  7. ThreadPoolExecutor线程池内部处理浅析

    我们知道如果程序中并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束时,会因为频繁创建线程而大大降低系统的效率,因此出现了线程池的使用方式,它可以提前创建好线程来执行任务.本文主要通过j ...

  8. ubuntu安装opencv的正确方法

    本文介绍的是如何安装ubuntu下C++接口的opencv 1.安装准备: 1.1安装cmake sudo apt-get install cmake 1.2依赖环境 sudo apt-get ins ...

  9. C++ Qt开发:自定义Dialog对话框组件

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍自定义Dial ...

  10. Scrapy框架架构

    ENGINE:引擎,负责各个组件的管理. SPIDERS:各个爬虫文件类.(我们一般要写的代码就是这个). SCHEDULER:调度器,ENGINE将爬虫任务分发给该组件,由该组件调度爬虫任务. DO ...