前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈 AnalyticDB SQL 优化「建议收藏」

浅谈 AnalyticDB SQL 优化「建议收藏」

作者头像
全栈程序员站长
发布2022-09-29 09:46:55
1.1K0
发布2022-09-29 09:46:55
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

浅谈 AnalyticDB SQL 优化

前言

数据库性能优化需要从多个方面进行综合考虑。 例如:系统资源是否充足、资源模型的设计(高性能 vs 大存储)、表的设计以及规划、SQL改写和优化等等,本文只要介绍adb sql的优化


ADB计算引擎

ADB目前支持两种计算引擎:COMPUTENODE Local/Merge(简称:Two-Stage)和MPP:

两阶段计算引擎

FULL MPP计算引擎

约束限制

事实表join要求同表组 join条件必须包含一级分区列,不支持非分区列distinct操作

对SQL写法基本无特殊要求,按性能考虑的话,按分区列join性能更好

性能

简单场景:单表查询+一级分区列查询

全sql场景:简单查询场景性能比两阶段有10%左右的差异

版本要求

所以版本都支持

2.4.2及以上版本 (2.6以上版本支持跟全面)

默认计算引擎切换: Local/Merge(简称LM):又称两阶段,优点是计算性能很好,并发能力强,缺点是对部分跨一级分区列的计算支持较差。 Full MPP Mode(简称MPP):支持更丰富的函数,SQL语法,数据量计算能力。 默认计算引擎,V2.8 版本之后默认 MPP,之前版本默认LM。可以按DB切换默认引擎 引擎SQL hint: /+engine=mpp/ select …. From …where … /+engine=COMPUTENODE/ select …. From …where …


ADB优化器

ADB查询优化器—数百优化规则 基础优化规则 裁剪规则:列裁剪、分区裁剪、子查询裁剪 下推/合并规则:谓词下推、函数下推、聚合下推、Limit下推 去重规则:Project去重、Exchange去重、Sort去重 常量折叠、谓词推导 探测优化规则 Joins:BroadcastHashJoin、RedistributedHashJoin、NestlooplndexJoin Aggregate:HashAggregate、SingleAggregate JoinReorder GroupBy下推、Exchange下推、Sort下推 高级优化规则 CTE

ADB优化器—SQL Rewrite SQL parser 通过rewrite一些bad SQL,解决SQL导致索引失效问题 表达式变换

代码语言:javascript
复制
优化前:select a,b from tab where b+1=100;
优化后:select a,b from tab where b=99;

比较条件组合

代码语言:javascript
复制
优化前1:SELECT * FROM t WHERE a > 3 OR a >= 2;
优化前2:SELECT * FROM t1 WHERE max_adate > '2022-07-22' AND max_adate != '2022-06-01';
优化后1:SELECT * FROM t WHERE a >= 2;
优化后2:SELECT * FROM t1 WHERE max_adate > DATE '2022-07-22';

IS NULL OR IS NOT NULL去重

代码语言:javascript
复制
优化前:SELECT * FROM t1 WHERE stat_date is null or stat_date is not null;
优化后:SELECT * FROM t1;

函数常量折叠

代码语言:javascript
复制
优化前:SELECT * FROM t1 t WHERE comm_week BETWEEN CAST( date_format( date_add('day' , -day_of_week('20180605'), date('20180605')), '%Y%m%d')AS bigint) AND CAST( date_format( date_add('day' , -day_of_week('20180605') , date('20180605')), '%Y%m%d')AS bigint);
优化后:SELECT * FROM t1 t WHERE comm_week BETWEEN 20180602 AND 20180602;

内部扫描 no-indexHint,可以使条件通过内部扫描执行

代码语言:javascript
复制
/* +no-index=[table.sl] */
select id,sl from table where id='001' and sl<>999;

ADB索引

为提高查询响应速度,满足高性能需求场景,AnalyticDB为每个分区建了下列索引: 倒排索引: 分区表的所有列(适用Bitmap索引的列除外)都建了倒排索引,key为排序的列值,value为对应的RowID list,所以对于任何列进行FILTER(WHERE key=value)或者JOIN查询都非常高效。 同时索引采用pForDelta压缩,拥有高压缩比(1:4~1:32)和解压速度(1GB/s)。 Bitmap索引: 对于值重复率高的列,建立Bitmap索引。 区间树索引: 为了加速范围查询,对于类型为数字的列同时建立了区间树索引。

行列混存的块索引–元数据

在这里插入图片描述
在这里插入图片描述

元数据: 上面介绍了一个分区的数据存储格式,相应的元数据包括: 分区元数据 列元数据 列Block元数据。 其中分区元数据包含该分区总行数,单个block中的列行数等信息; 列元数据包括该列值类型,整列的MAX/MIN值,NULL值数目,直方图信息,用于加速查询; 列block元数据也包含该列的MAX/MIN/SUM, 总条目数(COUNT)等信息,同样用于加速查询 多维组合索引的优化

ADB索引设计和使用 ADB默认为表所有列创建索引,无需create index 取消index – disableIndex 参考原则: 只会出现在select子句中,不会在where子句中使用情况

代码语言:javascript
复制
CREATE TABLE  f_fskt_orderown (
  id varchar COMMENT '',
  cu_id varchar COMMENT '',goods_id bigint COMMENT '',
  numbers bigint disableIndex true COMMENT '',
  total_price double disableIndex true COMMENT '',    
  order_date bigint COMMENT '',
  PRIMARY KEY (order_id,cu_id,order_date)
)
PARTITION BY HASH KEY (id) PARTITION NUM 16
SUBPARTITION BY LIST KEY (order_date)
SUBPARTITION OPTIONS (available_partition_num = 90)
TABLEGROUP ads_demo
OPTIONS (UPDATETYPE='realtime')
COMMENT '';

ADB SQL开发与表分区设计

ADB 的数据分布对查询性能有着直接的影响:

  • 数据分布要均匀,避免数据倾斜
  • 典型查询要能够基于“一级分区键”
  • 多表JOIN要能够基于“一级分区键”
  • 利用维度表避免数据在分区键Shuffle
  • 利用二级分区和聚簇列减少I/O消耗

本地加速关联—分布式计算local join: 在设计表的一级分区方案务必根据查询SQL的特点来确定,分布式计算平台下,实现多表join关联查询加速,需要优先考虑local join。 ADB对local join有如下前提要求: 1.事实表 join 维度表 维度表记录数尽量不要超过千万,特殊情况极限小于2 千万 事实表 join 维度表,不限制关联条件 2.事实表 join 事实表 join条件必须包含一级分区列 同时要求join的表的一级分区数一致

ADB SQL开发的性能指南

SQL开发原则概况—如何获取更高性能

ADB是一个分布式、列存数据库,极速计算内核设计:实时计算,高QPS SQL编写原则: 追求简单 大部分情况下性能随 SQL复杂度下降,比如:单表查询 (冗余设计)优于 表关联查询。 SQL优化核心方法:减少IO 索引扫描,尽可能少的列扫描,返回最小量数据量,减少IO同时也减少内存开销。 分布式计算:本地计算&并行计算 大数据计算情况,本地计算避免数据跨节点,充分利用分布式多计算资源的能力。 高QPS:分区裁剪 业务系统要求高QPS,毫秒级RT,请记住一定要将表和SQL设计为分区裁剪模式。 SQL开发规范: ADB SQL开发规范

  • 多表JOIN要能够基于“一级分区键”
  • 所有的LEFT JOIN 要放在INNER JOIN之后
  • 尽可能添加足够的过滤条件
  • 尽量避免子查询导致数据shuffle
  • 利用维度表避免数据在分区键Shuffle
  • 尽量避免LEFT JOIN
  • 避免含有聚合运算的子查询
  • 避免在列上添加函数导致索引失效: 索引和扫描选择 默认查询都走索引,但是走索引检索在下面的几种情况下,性能较差。 1.范围查询(或等值查询)筛选能力差 2.不等于条件查询(不包括 not null) 3.中缀或后缀查询,例如 like ‘%abc’ 或 like ‘%abc%’ 4.AND 条件中某一条件具有高筛选能力,其他条件走索引性能比扫描性 能差 示例:
代码语言:javascript
复制
select * from table1 where x= 3 and time between 0 and 10000000000 ;

对于这条查询 sql ,我们可以认为 x=3 筛选后的结果集肯定是比较小了,因 为是一个精确匹配。如果 select count(*) from table1 where x= 3 出来的结 果比较小的话,time 列再去走索引效果反而差。 所以,对于这种 query,增加 hint no-index:

代码语言:javascript
复制
/*+ no-index=[table1.time]*/ select * from table1 where x= 3 and time between 0 and 9999999999;

上述语句表示强制条件 time between 0 and 10000 走扫描。计算引擎首先检 索列 x 的索引,得出满足条件 x=3 的行集合,然后读取每行所对应的 time 列 数据,如果满足 time between 0 and 9999999999,则将该行数据加入返回结果。 SQL开发规范与示例–表关联性能最佳SQL示例 表join:保证:Local Join 一级分区键join 一级分区数一致

SQL开发规范与示例—一级分区裁剪 当要求高QPS查询业务时,需要从表的设计和SQL上利用分区裁剪能力。

SQL开发规范与示例—二级分区裁剪 包含二级分区情况,SQL中增加二级分区条件,减少二级分区扫描

多表关联–尽量的充分的过滤条件 多表关联查询,where条件中,需要显示的写明每一个表的过滤条件。通常我们习惯在传统数据库中,都是通过索引字段关联来快速检索数据。如下SQL:

子查询使用 对于子查询,ADB会首先执行子查询,并将子查询的结果保存在内存中,然后将该子查询作为一个逻辑表,执行条件筛选。由于子查询没有索引,所有条件筛选走扫描。因此如果子查询结果较大时,性能比较差;反之当子查询结果集较小时,扫描性能反而超过索引查询。 对于join查询,由于AnalyticDB默认采用hash join算法,如果其中一张表结果集(条件筛选后)较大时,扫描性能会比索引差很多,因此尽量不要采用子查询。 例如以下SQL:

代码语言:javascript
复制
Select A.id from table1 A join (select table2.id from table2 where table2.y = 6) B on A.id= B.id where A.x=5 ;

当满足条件x=5 和y=6的条数较多时,应改成:

代码语言:javascript
复制
Select A.id from table1 A join table2 B on A.id = B.id where B.y = 6 and A.x=5 ;

ADB慢SQL的定位和常见原因

SQL问题定位及优化方法导图

Top N Slow SQL FN access.log 日志文件,如果多个Fn需要每个FN都搜索下 cat access.log | awk -F’Total_time=’ {‘print $2’} | sort -n | tail -10 获取FN 日志步骤

  1. who am i获取当前FN的IP:port 或者通过 gallardo ui找到FN
  2. 登录FN,su admin
  3. netstat -ntpld ${port} 输出: tcp 0 0 0.0.0.0:9999 0.0.0.0:* LISTEN 205213/java
  4. pwdx ${进程ID} 如205213 返回FN进程的路径
  5. cd ${进程路径}/logs 性能问题定位 — 系统 — GC grep stop gc.log |grep -v “ed:0” 短时间内有大量超过1s的GC

问题排查&原因

解决方案

二级分区数过多导致GCcd ${workdir}/tmp/; tree -L 5 | wc -l如果结果超过10000,则表明二级分区数过多

缩减二级分区个数,或者二级分区周期调整为周/月扩容

实时表数据量太多cd /${workdir}/tmp/find . -name “922*”|xargs du -sh * |egrep “[0-9]G”如果有超过1GB的增量数据,则该数据的version(倒数第三层目录)对应时间超过1天,通常表示上次基线合并有问题。否则有可能当天实时增量数据写入过大

如果基线合并问题,则需要排查并解决如果当天写入实时数据过大,需要对大表进行optimize table $table_name如果系统不能恢复,建议将DB级别配置delayPullRTData设置为60000 (每分钟pull一次实时数据),减少实时数据对系统的压力,如果GC过于严重,基本不能服务,则影响上线,建议重启CN

复杂SQL导致GCCN查询exception.log,是否有异常超时SQL,并判断该SQL计算数据量(如全表group by ,全表order by等操作)egrep -B 2 “mhm=[0-9]{9,}” analysis.log识别是否有消耗内存超过1GB的SQL

找到问题SQL,并优化SQL

性能问题定位 — 系统 —网络层面问题 CN CPU负载过高,导致网路线程无响应/超时 查询不稳定

问题排查&原因

解决方案

FN analysis.log显示某一个或者少数CN节点返回超时或者出错CN 如下现象:CPU load非常高,并且根据jstack显示load较高的线程为epollwaitnetstat -anp结果中有部分连接的Rec-Q或 Send-Q非常大(超过1000)netstat -anp连接数非常多,或有大量CLOSE_WAIT状态的连接grep packet log.log有大量超时或者失败的日志

临时方案:按副本重启CN及FN排查CPU负载过高问题并解决

性能问题定位 — 系统 —SSD磁盘Util过高

问题排查&原因

解决方案

tsar -I 1 -l, SSD盘的util接近100%排查方法:jstack ${pid}

如果是下载线程导致,则可以减少下载线程数,修改/gloable/config/taskThreadCount为较小值(5)如果是写入导致的,一般是由于主键无序导致的,建议优化主键,尽量保证有序。同时可以减少实时线程数,配置:/global/config/pullMQThreadCount为较小的值如果是查询线程,则需优化SQL

性能问题定位 — 系统 —CPU负载过高

问题排查&原因

解决方案

登录CN,jstack ${pid}|grep localnode ,通常对应stack trace的线程名称为当前运行SQL使用top -Hpxi er ${pid},查看是否存在某写线程一直占有超过50%的CPU,将线程Id转换为16进制(小写),在jstack中查找该值,找到对应的stack,通常线程name为正在运行的SQL

识别导致CPU过高的SQL,并优化

SQL执行开销日志 analysis.log—udf_sys_log 通过 udf_sys_log() 获取CN analysis.log日志

代码语言:javascript
复制
select udf_sys_log() from ( SQL Statement) ;

udf_sys_log()返回信息

FN日志找CN节点

ADB慢SQL优化实例

查询优化 – 索引失效: 避免过滤条件带有针对列的函数计算:

代码语言:javascript
复制
例如:select * from table where year(date_test) >= 2018;
应该改为:select * from table where date_test >= '2018-01-01';

避免多表链接时基于函数关联:

代码语言:javascript
复制
例如:select t1.id from t1 inner join t2 on year(t1.birthday) = year(t2.birthday)
应该在t1、t2表中增加yyyy列,改为:select t1.id from t1 inner join t2 on t1.yyyy = t2.yyyy;

避免类型转换:

  • 应当在表设计之初就要充分考虑类型的统一
  • 此类问题经常出现在 date/timestamp/varchar 数据类型的转换

查询优化 – 列的类型选择 原理

  • ADB 处理数值类型的性能远好于处理字符串类型
  • 建议尽可能使用 数值类型、日期型、时间戳
  • 基于标签的查询推荐使用 多值列(multivalue)

常见将字符串转换为数值类型方法

  • 包含字符前缀或后缀,例如E12345,E12346等。可以直接去掉前缀或者将前缀映射为数字
  • 该列只有少数几个值,例如国家名。可以对每个国家编码,每个国家对应一个唯一数字

主键优化

  • 设置主键的原理
  • 主键必须包括分区键,二级分区键
  • 主键尽可能少,短
  • 主键尽可能递增或递减

SQL优化技巧 – localJoin:

原理:

  • 使用 localJoin 时,计算可以在节点内完成,避免数据Shuffle
  • 通常情况下,localJoin 会大幅提升RT和并发度

在多表关联查询时:

  • 要含有 一级分区键 的等值链接
  • 或者确保其中的一张表的链接键是一级分区键
  • 如果两表链接无法基于一级分区键,可以考虑把其中的一张表转换为维度表
  • 驱动表的数据量应当尽量的少

实例:

  • 表A 和 表B 链接时 没有基于一级分区键,查询耗时 4.2sec
  • 经过业务确认,在增加一级分区键的等值链接后,查询耗时 0.37sec ,性能提升10倍

优化前:

优化后:

SQL优化技巧 – hashJoin: 原理:

  • 使用 hashJoin 时,计算在内存中完成,可以充分利用分布式的计算能力
  • 通常情况下,hashJoin 更加适合大结果集的运算

在多表关联查询时:

  • 要含有 一级分区键 的等值链接
  • 或者确保其中的一张表的链接键是一级分区键

实例:

  • 测试1:按照“商家ID”做一级分区键,任何基于商家的统计可在单独的分区内完成,但导致分布不均,计算存在热点
  • 测试2:按照 “订单ID”做一级分区键,数据分布均匀,但任何基于商家的统计需要在所有节点上进行并行计算
  • 测试结果表明:按照“订单ID”查询更快,且数据量越大越明显


谢谢观看!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/192925.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年9月16日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 浅谈 AnalyticDB SQL 优化
  • 前言
    • ADB计算引擎
      • ADB优化器
        • ADB索引
          • ADB SQL开发与表分区设计
            • ADB SQL开发的性能指南
              • ADB慢SQL的定位和常见原因
                • ADB慢SQL优化实例
                相关产品与服务
                GPU 云服务器
                GPU 云服务器(Cloud GPU Service,GPU)是提供 GPU 算力的弹性计算服务,具有超强的并行计算能力,作为 IaaS 层的尖兵利器,服务于生成式AI,自动驾驶,深度学习训练、科学计算、图形图像处理、视频编解码等场景。腾讯云随时提供触手可得的算力,有效缓解您的计算压力,提升业务效率与竞争力。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档