PolarDB-X 1.0-用户指南-SQL调优指南—SQL调优方法与实战

本文涉及的产品
云原生数据库 PolarDB 分布式版,标准版 2核8GB
简介: 找出需调优的慢SQL后,先通过EXPLAIN查看执行计划,然后通过如下方法优化SQL:下推更多计算至存储层MySQL,适当增加索引,优化执行计划。

下推更多的计算

PolarDB-X会尽可能将更多的计算下推到存储层MySQL。下推计算能够减少数据传输,减少网络层和PolarDB-X层的开销,提升SQL语句的执行效率。PolarDB-X支持下推几乎所有算子,包括:

  • 过滤条件,如WHEREHAVING中的条件。
  • 聚合算子,如COUNTGROUP BY等,会分成两阶段进行聚合计算。
  • 排序算子,如ORDER BY
  • JOIN和子查询,两边JOIN Key分片方式必须一样,或其中一边为广播表。

如下示例讲解如何将更多的计算下推到MySQL来加速执行


> EXPLAIN select * from customer, nation where c_nationkey = n_nationkey and n_regionkey = 3;
Project(c_custkey="c_custkey", c_name="c_name", c_address="c_address", c_nationkey="c_nationkey", c_phone="c_phone", c_acctbal="c_acctbal", c_mktsegment="c_mktsegment", c_comment="c_comment", n_nationkey="n_nationkey", n_name="n_name", n_regionkey="n_regionkey", n_comment="n_comment")
  BKAJoin(condition="c_nationkey = n_nationkey", type="inner")
    Gather(concurrent=true)
      LogicalView(tables="nation", shardCount=2, sql="SELECT * FROM `nation` AS `nation` WHERE (`n_regionkey` = ?)")
    Gather(concurrent=true)
      LogicalView(tables="customer_[0-7]", shardCount=8, sql="SELECT * FROM `customer` AS `customer` WHERE (`c_nationkey` IN ('?'))")

若执行计划中出现了BKAJOIN,BKAJOIN每次从左表获取一批数据,就会拼成一个IN查询取出右表相关联的行,并在最后执行JOIN操作。由于左表数据量很大,需要取很多次才能完成查询,执行很慢。

无法下推JOIN的原因是:当前情况下,nation是按主键n_nationkey切分的,而本查询的JOIN Key是c_custkey,二者不同,所以下推失败。

考虑到nation (国家)表数据量并不大、且几乎没有修改操作,可以将其重建成如下广播表:


--- 修改后 ---
CREATE TABLE `nation` (
  `n_nationkey` int(11) NOT NULL,
  `n_name` varchar(25) NOT NULL,
  `n_regionkey` int(11) NOT NULL,
  `n_comment` varchar(152) DEFAULT NULL,
  PRIMARY KEY (`n_nationkey`)
) BROADCAST;  --- 声明为广播表

修改后,可以看到执行计划中不再出现JOIN,几乎所有计算都被下推到存储层MySQL执行了(LogicalView中),而上层仅仅是将结果收集并返回给用户(Gather算子),执行性能大大增强。


> EXPLAIN select * from customer, nation where c_nationkey = n_nationkey and n_regionkey = 3;
Gather(concurrent=true)
  LogicalView(tables="customer_[0-7],nation", shardCount=8, sql="SELECT * FROM `customer` AS `customer` INNER JOIN `nation` AS `nation` ON ((`nation`.`n_regionkey` = ?) AND (`customer`.`c_nationkey` = `nation`.`n_nationkey`))")

更多关于下推的原理和优化,请参见查询改写与下推

增加索引

如果下推SQL中出现(物理)慢SQL,可以给分表增加索引来解决,这里不再详述。

PolarDB-X自5.4.1 版本开始支持全局二级索引,可以通过增加GSI的方式使逻辑表拥有多个拆分维度。

下面以一个慢SQL作为例子来讲解如何通过GSI下推更多算子。


> EXPLAIN select o_orderkey, c_custkey, c_name from orders, customer
          where o_custkey = c_custkey and o_orderdate = '2019-11-11' and o_totalprice > 100;
Project(o_orderkey="o_orderkey", c_custkey="c_custkey", c_name="c_name")
  HashJoin(condition="o_custkey = c_custkey", type="inner")
    Gather(concurrent=true)
      LogicalView(tables="customer_[0-7]", shardCount=8, sql="SELECT `c_custkey`, `c_name` FROM `customer` AS `customer`")
    Gather(concurrent=true)
      LogicalView(tables="orders_[0-7]", shardCount=8, sql="SELECT `o_orderkey`, `o_custkey` FROM `orders` AS `orders` WHERE ((`o_orderdate` = ?) AND (`o_totalprice` > ?))")

执行计划中,orders按照o_orderkey拆分而customer按照c_custkey拆分,由于拆分维度不同JOIN算子不能下推。

考虑到2019-11-11当天总价高于100的订单非常多,跨分片JOIN耗时很高,需要在orders表上创建一个GSI来使得JOIN算子可以下推。

查询中使用到了orders表的o_orderkey, o_custkey, o_orderdate, o_totalprice四列,其中o_orderkey, o_custkey分别是主表和索引表的拆分键,o_orderdate, o_totalprice作为覆盖列包含在索引中用于避免回表。


> create global index i_o_custkey on orders(`o_custkey`) covering(`o_orderdate`, `o_totalprice`)
        DBPARTITION BY HASH(`o_custkey`) TBPARTITION BY HASH(`o_custkey`) TBPARTITIONS 4;

增加GSI并通过force index(i_o_custkey)强制使用索引后,跨分片JOIN变为MySQL上的局部JOIN (IndexScan中),并且通过覆盖列避免了回表操作,查询性能得到提升。


> EXPLAIN select o_orderkey, c_custkey, c_name from orders force index(i_o_custkey), customer
          where o_custkey = c_custkey and o_orderdate = '2019-11-11' and o_totalprice > 100;
Gather(concurrent=true)
  IndexScan(tables="i_o_custkey_[0-7],customer_[0-7]", shardCount=8, sql="SELECT `i_o_custkey`.`o_orderkey`, `customer`.`c_custkey`, `customer`.`c_name` FROM `i_o_custkey` AS `i_o_custkey` INNER JOIN `customer` AS `customer` ON (((`i_o_custkey`.`o_orderdate` = ?) AND (`i_o_custkey`.`o_custkey` = `customer`.`c_custkey`)) AND (`i_o_custkey`.`o_totalprice` > ?))")

更多关于全局二级索引的使用细节,请参见使用全局二级索引

执行计划调优


说明 以下内容适用于PolarDB-X5.3.12或以上版本。

大多数情况下,PolarDB-X的查询优化器可以自动产生最佳的执行计划。但是,少数情况下,可能因为统计信息存在缺失、误差等,导致生成的执行计划不够好,这时,可以通过Hint来干预优化器行为,使之生成更好的执行计划。

如下示例将讲解执行计划的调优。

下面的查询,PolarDB-X查询优化器综合了JOIN两边的代价。


> EXPLAIN select o_orderkey, c_custkey, c_name from orders, customer
          where o_custkey = c_custkey and o_orderdate = '2019-11-15' and o_totalprice < 10;
Project(o_orderkey="o_orderkey", c_custkey="c_custkey", c_name="c_name")
  HashJoin(condition="o_custkey = c_custkey", type="inner")
    Gather(concurrent=true)
      LogicalView(tables="customer_[0-7]", shardCount=8, sql="SELECT `c_custkey`, `c_name` FROM `customer` AS `customer`")
    Gather(concurrent=true)
      LogicalView(tables="orders_[0-7]", shardCount=8, sql="SELECT `o_orderkey`, `o_custkey` FROM `orders` AS `orders` WHERE ((`o_orderdate` = ?) AND (`o_totalprice` < ?))")

但是,实际上2019-11-15这一天总价低于10元的订单数量很小,只有几条,这时候用BKAJOIN是比Hash JOIN更好的选择(关于BKAJOIN和Hash JOIN的介绍,请参见JOIN与子查询的优化和执行

通过如下/*+TDDL:BKA_JOIN(orders, customer)*/Hint强制优化器使用BKAJOIN(LookupJOIN):


> EXPLAIN /*+TDDL:BKA_JOIN(orders, customer)*/ select o_orderkey, c_custkey, c_name from orders, customer
          where o_custkey = c_custkey and o_orderdate = '2019-11-15' and o_totalprice < 10;
Project(o_orderkey="o_orderkey", c_custkey="c_custkey", c_name="c_name")
  BKAJoin(condition="o_custkey = c_custkey", type="inner")
    Gather(concurrent=true)
      LogicalView(tables="orders_[0-7]", shardCount=8, sql="SELECT `o_orderkey`, `o_custkey` FROM `orders` AS `orders` WHERE ((`o_orderdate` = ?) AND (`o_totalprice` < ?))")
    Gather(concurrent=true)
      LogicalView(tables="customer_[0-7]", shardCount=8, sql="SELECT `c_custkey`, `c_name` FROM `customer` AS `customer` WHERE (`c_custkey` IN ('?'))")

可以选择执行加如下Hint的查询:


/*+TDDL:BKA_JOIN(orders, customer)*/ select o_orderkey, c_custkey, c_name from orders, customer where o_custkey = c_custkey and o_orderdate = '2019-11-15' and o_totalprice < 10;

以上操作加快了SQL查询速度。为了让Hint发挥作用,可以将应用中的SQL加上Hint,或者更方便的方式是使用执行计划管理(Plan Management)功能对该SQL固定执行计划。具体操作如下:


BASELINE FIX SQL /*+TDDL:BKA_JOIN(orders, customer)*/ select o_orderkey, c_custkey, c_name from orders, customer where o_custkey = c_custkey and o_orderdate = '2019-11-15';

这样一来,对于这条SQL(参数可以不同),PolarDB-X都会采用如上固定的执行计划。

更多关于执行计划管理的信息,请参见执行计划管理

相关实践学习
快速体验PolarDB开源数据库
本实验环境已内置PostgreSQL数据库以及PolarDB开源数据库:PolarDB PostgreSQL版和PolarDB分布式版,支持一键拉起使用,方便各位开发者学习使用。
相关文章
|
23天前
|
SQL 数据采集 关系型数据库
实现MySQL与SQL Server之间数据迁移的有效方法
总的来说,从MySQL到SQL Server的数据迁移是一个涉及到很多步骤的过程,可能会遇到各种问题和挑战。但只要精心规划、仔细执行,这个任务是完全可以完成的。
85 18
|
1月前
|
存储 关系型数据库 分布式数据库
PolarDB开源进阶篇:深度解析与实战优化指南
PolarDB是阿里云开源的云原生数据库,采用计算-存储分离架构,结合高性能共享存储与Parallel Raft多副本一致性协议,实现微秒级延迟和卓越性能。本文深入解析其架构设计,涵盖智能调度层、性能优化技巧(如查询优化器调优和分布式事务提升)、高可用与容灾配置、扩展功能开发指南以及监控运维体系。同时,通过电商平台优化案例展示实际应用效果,并展望未来演进方向,包括AI结合、多模数据库支持及Serverless架构发展。作为云原生数据库代表,PolarDB为开发者提供了强大支持和广阔前景。
132 16
|
1月前
|
SQL Java 数据库连接
Java中实现SQL分页的方法
无论何种情况,选择适合自己的,理解了背后的工作原理,并能根据实际需求灵活变通的方式才是最重要的。
61 9
|
2月前
|
SQL 关系型数据库 MySQL
【MySQL】SQL分析的几种方法
以上就是SQL分析的几种方法。需要注意的是,这些方法并不是孤立的,而是相互关联的。在实际的SQL分析中,我们通常需要结合使用这些方法,才能找出最佳的优化策略。同时,SQL分析也需要对数据库管理系统,数据,业务需求有深入的理解,这需要时间和经验的积累。
89 12
|
2月前
|
SQL 关系型数据库 MySQL
大数据新视界--大数据大厂之MySQL数据库课程设计:MySQL 数据库 SQL 语句调优方法详解(2-1)
本文深入介绍 MySQL 数据库 SQL 语句调优方法。涵盖分析查询执行计划,如使用 EXPLAIN 命令及理解关键指标;优化查询语句结构,包括避免子查询、减少函数使用、合理用索引列及避免 “OR”。还介绍了索引类型知识,如 B 树索引、哈希索引等。结合与 MySQL 数据库课程设计相关文章,强调 SQL 语句调优重要性。为提升数据库性能提供实用方法,适合数据库管理员和开发人员。
|
3月前
|
SQL 数据库连接 Linux
数据库编程:在PHP环境下使用SQL Server的方法。
看看你吧,就像一个调皮的小丑鱼在一片广阔的数据库海洋中游弋,一路上吞下大小数据如同海中的珍珠。不管有多少难关,只要记住这个流程,剩下的就只是探索未知的乐趣,沉浸在这个充满挑战的数据库海洋中。
87 16
|
7月前
|
SQL 监控 安全
Flask 框架防止 SQL 注入攻击的方法
通过综合运用以上多种措施,Flask 框架可以有效地降低 SQL 注入攻击的风险,保障应用的安全稳定运行。同时,持续的安全评估和改进也是确保应用长期安全的重要环节。
311 71
|
7月前
|
SQL 安全 PHP
PHP开发中防止SQL注入的方法,包括使用参数化查询、对用户输入进行过滤和验证、使用安全的框架和库等,旨在帮助开发者有效应对SQL注入这一常见安全威胁,保障应用安全
本文深入探讨了PHP开发中防止SQL注入的方法,包括使用参数化查询、对用户输入进行过滤和验证、使用安全的框架和库等,旨在帮助开发者有效应对SQL注入这一常见安全威胁,保障应用安全。
225 4
|
7月前
|
SQL BI 数据库
SQL操作的一些基本方法
【10月更文挑战第27天】SQL操作的一些基本方法
106 3
|
7月前
|
SQL 监控 固态存储
SQL优化有哪些方法?
【10月更文挑战第27天】SQL优化有哪些方法?
142 3

相关产品

  • 云原生分布式数据库 PolarDB-X