跳至主要內容

Klustron大约 5 分钟

图文实录|HTAP数据库能力系列分享:执行计划异常检测及优化

本文金句

一个错误的执行计划可能拖垮一个库,提前发现这类隐患是一个数据库系统的自诊断能力的重要指标。

今天我们来聊执行计划。执行计划的是在数据库的优化器阶段生成的,并在执行器阶段用于指导语句执行。

优化器选择执行计划的原则是选择最小的代价。

其中重要的两个决策点,分别是索引选择和算法选择。

我们先用一个单表上的查询语句来举例。

建表语句如下:

create table t(
id int primary key, 
c int, 
...
Other fields,
...
index(c))

这个表有一个主键和一个索引c, 假设要执行select id from t order by id.

用过MySQL的同学都能一眼看出这个查询大多数时候会选择主键id。

不过对于优化器来说,“评估”和“选择”是必不可少的动作。

这个查询的语义很简单,执行语句的代价包括“扫描数据”和“排序”。

表里有两个索引,就有两种可选方案:

  1. 使用主键id。由于InnoDB是聚簇索引,选择主键id时,需要读出每行数据的所有列。由于主键id这棵索引树,本身就是按照id递增的顺序组织的,因此不需要再做排序,取得每一行的ID值返回即可。
  2. 使用索引c。c这个索引树上只有c和id两列,因此每行读的数据量少。但由于这里数据是按照c值递增组织的,按照语句的order by id这个语义,需要对拿到的id值做排序。

优化器会评估这两个方案的代价,选择最小的那个代价来生产这个语句的执行计划。

大多数时候排序的代价比扫描的代价大,因此会选择方案1。极端情况就是这个表的单行数据量很大时,也就是“other fields”字段很多,可能会因为读行数据成本太高而选择索引c。

我们用一个简单例子介绍优化器做执行计划时的逻辑,当涉及到对表join时,还需要考虑驱动表的选择和join算法等,然后对代价做总体评估。

有时间我们会听到用户说,这个执行计划“不够好”,那么执行计划好坏的评估标准是什么呢?

从优化器角度看,算出来代价最低的执行计划就是最优的;而从用户角度看,最终指标是语句的执行时间。

如果一个语句使用执行计划A,但是实际上使用执行计划B的耗时更短,那么我们就可以说,这时候优化器做了一个“坏”的执行计划。

但问题是,只看单个语句的单次执行,是无法作出这种对比判断的,那么问题来了:怎么判断一个执行计划的好坏?

以下是一些常用的方法:

  1. Explain. 这个命令是MySQL自带的功能,有经验的DBA基本可以通过explain的输出就估算语句是否还有优化空间。但explain的问题就是需要事前介入, 而且是根据经验做评估,无法提前发现执行计划走错的情况。
  2. slowlog。一个语句被记入慢查询日志时,会记录扫描行数。一个语句如果扫描行数过多,就可能是索引没有建好,或者执行计划错误。slowlog是语句真实的执行结果,是一个事后数据。但是慢查询已经发生了,对于线上系统来说,可能故障已经出现。有没有能够在对生产造成影响之前就提前发现的方法?
  3. 全量审计日志。这是指在系统里记录所有SQL行为的方法。包括查询和更新。语句执行过程中记录相关信息,在语句执行的最后阶段,将信息写入日志中。记录的信息包括执行起止时间(微秒单位)、扫描行数、更新行数等等。虽然审计日志也是语句执行完成以后才能记入,但因为记录的是全量日志,就有机会在出现慢查询之前被发现扫描行数异常等情况,提示DBA来确认。
  4. 对照系统。实际上“执行计划错误”这样的bug发生概率并不高,而且通常PG的优化器准确度会比MySQL高一些。所以如果有两套系统同时跑相同的数据和语句,当某个语句在两个系统的执行计划不同时,就可以认为其中一个执行计划可能不是最优的,此时人工介入。但是维护一套对照系统本身是需要不少成本的。这个方案在KluStron刚好可行,因为Klustron的计算节点基于PG,而存储节点基于MySQL,在一个语句执行流程里可以通过记录在两层的执行计划对比,来提前发现可能存在的优化器BUG。

这里介绍了一些发现执行计划错误的方法,如果还有其他的方案,欢迎在Klustron技术交流群讨论。

END