1.Base基础/3.Icon图标/操作/search备份
1.Base基础/3.Icon图标/操作/search备份
EN
文档
关于AntDB
部署与升级
快速入门
使用教程
运维
工具和插件
高级服务
数据安全
参考
  • 文档首页 /
  • 调优 /
  • SQL调优

SQL调优

更新时间:2024-07-01 14:39:44

SQL(Structured Query Language:结构化查询语言)是用于管理关系数据库管理系统(RDBMS)。 SQL 的范围包括数据插入、查询、更新和删除,数据库模式创建和修改,以及数据访问控制。

很多公司的业务系统刚上线初期数据量都比较小,并且可能没有 DBA 这个岗位去对你开发项目中的 SQL 进行一个最终的审核,导致开发的时候写了一些慢 SQL,数据量小的时候可能还好,一旦数据量上来了,查询效能极低,并且请求次数过多的话很可能会因为这一个慢 SQL 把整个系统拖垮,不能正常对外提供服务。

SQL 优化的意思就在于此,按照关系型数据库的使用经验,我们总结了后续的一些 SQL 使用规范,在开发过程中遵循这些使用规范可以避免后续可能的 90% 以上 SQL 导致的性能问题产生。

捕获慢 SQL

SQL 优化的首要任务是识别和捕获慢 SQL,并获取到 SQL 执行时的必要绑定变量值

查询慢 SQL 语句分为以下两种情况:

  • 查看历史慢 SQL 语句:

    通过观察 pg_stat_statements 视图,执行时间较长的 SQL 语句。

    SELECT query,max_exec_time FROM pg_stat_statements ORDER BY max_exec_time DESC LIMIT 10;
    
  • 查看当前慢 SQL 语句。

    查询执行时间超过 1 秒的 SQL:

    SELECT * FROM pg_stat_activity WHERE state<>'idle' AND now()-query_start > interval '1 s' ORDER BY query_start ;
    

执行计划分析

通过使用 EXPLAIN 查询 SQL 计划,可分析 SQL 的快慢或资源占用情况。

获取执行计划

  • 获取执行计划,不执行 SQL :

    EXPLAIN SELECT * FROM table WHERE column = x; 
    
  • 获取真实执行的执行计划 :

    EXPLAIN analyze SELECT * FROM table WHERE column = x; 
    
  • 获取真实执行的详细执行计划 :

    EXPLAIN (analyze, verbose) SELECT * FROM table WHERE column = x; 
    

解析执行计划

执行计划的解读,按照从上往下,从右往左的原则进行。

定位异常的快速步骤:

  • 时间递减法: 从起始步骤逐级往下查找,每次查找下层最耗时步骤,直到最后一层,图中 红色路径
  • 关键步骤搜索: 直接在执行计划中搜索关键步骤,例如 Seq Scan/Removed

执行计划-定位

具体调优手段

SQL 逻辑改写

不同的 SQL 语句写法,对最终的执行计划生成有很大的影响,最终影响 SQL 的执行效率

  • With 改写 :可通过 with 将部分子查询结果缓存,并多次使用;同时简介干预表关联顺序
  • 过滤条件内推:通过将有效的过滤条件内推到最内层子查询,可降低表关联的数据规模
  • 函数计算外移:某些情况下,通过将函数计算移动到最外层,可减少函数计算次数
  • 移除冗余关联 :移除 SQL 中部分相同表的重复关联操作

通过 Hint 优化执行计划

AntDB 使用基于代价的优化器,优化路线使用统计数据而非固定的规则。对于一条 SQL 语句,优化器会去评估所有可能的代价并最终选择代价最低的去执行。优化器会尽力选择最好的执行计划,但由于其并不了解数据中可能存在的一些内在连接关系,导致这些执行计划可能并不完美。当判断数据库优化器生成的执行计划不是最优的时候,可以在不修改 SQL 的情况下(例如:等价改写、非等价改写)通过 pg_hint_plan 扩展插件,可以直接干预执行计划的生成,产生符合预期的执行计划。

加载插件:

  • 必须执行 create extension pg_hint_plan,不然找不到 hint_plan.hints 表。

  • 修改 postgresql.conf 中的 shared_preload_libraries 参数,修改完重启数据库。

    shared_preload_libraries = 'pg_hint_plan'
    

支持的 hint 提示类型介绍:

根据提示短语影响执行计划的方式,支持的提示类型包括扫描方法提示、连接方法提示、连接顺序提示、行数校正提示、并行执行提示和 GUC 参数设置提示。

扫描方法提示

扫描方法提示对目标表强制执行特定的扫描方法,pg_hint_plan 通过表的别名(如果存在的话)来识别目标表。扫描方法可能是序列扫描、索引扫描等。

扫描提示对普通表、继承表、无日志表、临时表和系统表有效。对外部表、表函数、常量值语句、通用表达式、视图和子查询无效。

示例命令如下:

/*+
    SeqScan(t1)
    IndexScan(t2 t2_pkey)
 */
 SELECT * FROM table1 t1 JOIN table table2 t2 ON (t1.key = t2.key);

连接方法提示

连接方法提示强制指定相关表聚合在一起的方法。对普通表、继承表、无日志表、临时表、外部表、系统表、表函数、常量值命令和通用表达式有效。对视图和子查询无效。

连接顺序提示

连接顺序提示指定两张或多张表的连接顺序。包括两种强制指定方法:

  • 强制执行特定的连接顺序,但不限制每个连接级别的方向。
  • 强制连接方向。

示例命令如下:

 /*+
    NestLoop(t1 t2)
    MergeJoin(t1 t2 t3)
    Leading(t1 t2 t3)
  */
 SELECT * FROM table1 t1
     JOIN table table2 t2 ON (t1.key = t2.key)
     JOIN table table3 t3 ON (t2.key = t3.key);

说明:

  • NestLoop(t1 t2):指定表 t1 和 t2 的连接方法。
  • MergeJoin(t1 t2 t3):指定表 t1、t2 和 t3 之间的连接方法。
  • Leading(t1 t2 t3):指定三张表的连接顺序。

行数校正提示

行数校正提示会校正由于查询优化器限制而导致的行数错误。

 /*+ Rows(a b #10) */ SELECT... ; # 设置连接结果的行数为10
 /*+ Rows(a b +10) */ SELECT... ; # 行数增加10
 /*+ Rows(a b -10) */ SELECT... ; # 行数减去10
 /*+ Rows(a b *10) */ SELECT... ; # 行数增大10倍

并行执行提示

并行执行提示会指定并行的执行计划。

并行级别提示对普通的表、继承表、无日志表和系统表有效。对外部表、常量从句、通用表达式、视图和子查询无效。视图的内部表可以通过其真实名称或别名指定目标对象。

下面两个示例说明在每张表上执行查询的方式不同:

  • 方式一:指定表 c1 的并行度为 3,表 c2 的并行度为 5。

    EXPLAIN /*+ Parallel(c1 3 hard) Parallel(c2 5 hard) */
         SELECT c2.a FROM c1 JOIN c2 ON (c1.a = c2.a);
    

    返回结果如下:

                                    QUERY PLAN
    -------------------------------------------------------------------------------
    Hash Join  (cost=2.86..11406.38 rows=101 width=4)
     Hash Cond: (c1.a = c2.a)
     ->  Gather  (cost=0.00..7652.13 rows=1000101 width=4)
           Workers Planned: 3
           ->  Parallel Seq Scan on c1  (cost=0.00..7652.13 rows=322613 width=4)
     ->  Hash  (cost=1.59..1.59 rows=101 width=4)
           ->  Gather  (cost=0.00..1.59 rows=101 width=4)
                 Workers Planned: 5
                 ->  Parallel Seq Scan on c2  (cost=0.00..1.59 rows=59 width=4)
    
    
  • 方式二:指定表 t1 的并行度为 5。

    EXPLAIN /*+ Parallel(tl 5 hard) */ SELECT sum(a) FROM tl;
    

    返回结果如下:

                                      QUERY PLAN
    -----------------------------------------------------------------------------------
    Finalize Aggregate  (cost=693.02..693.03 rows=1 width=8)
     ->  Gather  (cost=693.00..693.01 rows=5 width=8)
           Workers Planned: 5
           ->  Partial Aggregate  (cost=693.00..693.01 rows=1 width=8)
                 ->  Parallel Seq Scan on tl  (cost=0.00..643.00 rows=20000 width=4)
    
  • GUC 参数设置提示

    在执行查询的过程中改变 GUC 参数的值。此值仅在执行器生成查询计划期间有效,并且不会对其它语句产生影响。如果对同一个GUC 参数进行多次设置,则以最后一个为准。

    示例命令如下:

    /*+ Set(random_page_cost 2.0) */
    SELECT * FROM table1 t1 WHERE key = 'value';
    

索引优化

索引可以有效的降低数据访问的规模,不同的场景需要不同类型的索引进行优化。

索引类别

AntDB 中,常用的索引类型如下:

  • B-Tree 索引 : 最常使用的数据库索引,也是数据库默认的索引类型,广泛用于通用的大批量数据过滤。包括: 一般 B-Tree 索引,唯一索引,函数索引,部分索引,复合索引等
  • 哈希索引 : 只在等值查询中使用,提供等值查询记录的快速访问,适用于字段值较长不适合 B-Tree 的部分场景
  • Gist 索引 : 通用搜索树索引,可作为索引模板实现任意的索引模式,通常用于多维数据类型或集合类型中
  • Gin 索引 : 通用倒排索引,通常用于全文搜索,数组元素索引,同样也可扩展到其他类型或自定义类型
  • Brin 索引 : 基于数据块级别的范围索引,记录一批数据块的的统计信息(最大,最小,Count, Sum 等),空间占用极小

使用场景

B-Tree 索引是绝大部分情况下都适用的索引,其可使用场景分类如下:

  • 等值查询: 对索引列的等值查询,例如: col = 123
  • 范围查询: 对索引列的范围查询,例如: col between 1 and 10, col < 10
  • 模糊查询: 部分右侧扩展的模糊查询,例如: col like 'test%'

注意事项

对于 B-Tree 索引,在使用过程中需要注意以下事项,不当使用可能导致无法正常使用索引:

  • 不等于条件 : 使用 != 过滤条件,无法使用索引
  • 在索引列上计算 : 包括使用函数计算和其他类型的计算转换,其中函数计算可通过函数索引解决
  • 使用 or 过滤条件 :在过滤条件中,多个条件使用 or 组合,可能导致无法使用索引
  • not in 条件 : 基本等同于不等于条件,也无法使用索引
  • in 条件 : 若 in 列表过长,可能无法使用索引
  • like 条件 : 在 like 条件中,若关键词前后均有 % 通配符,则无法使用索引
  • 选择性差 : 优化器在判断是否使用索引的过程中,会通过统计信息,判断过滤条件的选择性,选择性太差可能无法使用索引
  • 统计信息不足 : 执行计划的生成,严重依赖于表和索引的统计信息,若统计信息不足,则可能导致无法使用索引

注意: 索引的使用与否,最终要取决于优化器在当前数据库环境下的综合评估

问题反馈