TiDB SQL优化(二)

1.物理优化
物理优化是基于代价的优化,为上一阶段产生的逻辑执行计划制定物理执行计划。这一阶段中,优化器会为逻辑执行计划中的每个算子选择具体的物理实现。逻辑算子的不同物理实现有着不同的时间复杂度、资源消耗和物理属性等。在这个过程中,优化器会根据数据的统计信息来确定不同物理实现的代价,并选择整体代价最小的物理执行计划。
 

1.1索引的选择

从存储层读取数据是 SQL 计算过程中最为耗时的部分之一,TiDB 目前支持从不同的存储和不同的索引中读取数据,索引选择得是否合理将很大程度上决定一个查询的运行速度。
本章节将介绍 TiDB 如何选择索引去读入数据,以及相关的一些控制索引选择的方式。

读表

在介绍索引的选择之前,首先要了解 TiDB 有哪些读表的方式,这些方式的触发条件是什么,不同方式有什么区别,各有什么优劣。

读表算子

读表算子
触发条件
适用场景
说明
PointGet/BatchPointGet
读表的范围是一个或多个单点范围
任何场景
如果能被触发,通常被认为是最快的算子,因为其直接调用 kvget 的接口进行计算,不走 coprocessor
TableReader
任何场景
从 TiKV 端直接扫描表数据,一般被认为是效率最低的算子,除非在  _tidb_rowid  这一列上存在范围查询,或者无其他可以选择的读表算子时,才会选择这个算子
TableReader
表在 TiFlash 节点上存在副本
需要读取的列比较少,但是需要计算的行很多
TiFlash 是列式存储,如果需要对少量的列和大量的行进行计算,一般会选择这个算子
IndexReader
表有一个或多个索引,且计算所需的列被包含在索引里
存在较小的索引上的范围查询,或者对索引列有顺序需求的时候
当存在多个索引的时候,会根据估算代价选择合理的索引
IndexLookupReader
表有一个或多个索引,且计算所需的列 不完全 被包含在索引里
同 IndexReader
因为计算列不完全被包含在索引里,所以读完索引后需要回表,这里会比 IndexReader 多一些开销
注意:
TableReader 是基于  _tidb_rowid  的索引,TiFlash 是列存索引,所以索引的选择即是读表算子的选择。

索引的选择

TiDB 在选择索引时,会基于每个读表算子的代价估算,在此基础上提供了启发式规则 "Skyline-Pruning",以降低错误估算导致选错索引的概率。

Skyline-Pruning

Skyline-Pruning 是一个针对索引的启发式过滤规则,评判一个索引的好坏需要从以下三个维度进行衡量:
  • 选择该索引读表时,是否需要回表(即该索引生成的计划是 IndexReader 还是 IndexLookupReader)。不用回表的索引在这个维度上优于需要回表的索引。
  • 选择该索引是否能满足一定的顺序。因为索引的读取可以保证某些列集合的顺序,所以满足查询要求顺序的索引在这个维度上优于不满足的索引。
  • 索引的列涵盖了多少访问条件。“访问条件”指的是可以转化为某列范围的  where  条件,如果某个索引的列集合涵盖的访问条件越多,那么它在这个维度上更优。
对于这三种维度,如果某个索引  idx_a  在 三个维度上都不比  idx_b  ,且 有一个维度比  idx_b  ,那么就会优先选择  idx_a 。

基于代价选择

在使用 Skyline-Pruning 规则排除了不合适的索引之后,索引的选择完全基于代价估算,读表的代价估算需要考虑以下几个方面:
  • 索引的每行数据在存储层的平均长度。
  • 索引生成的查询范围的行数量。
  • 索引的回表代价。
  • 索引查询时的范围数量。
根据这些因子和代价模型,优化器会选择一个代价最低的索引进行读表。

代价选择调优的常见问题

  1. 估算的行数量不准确?
    一般是统计信息过期或者准确度不够造成的,可以重新执行  analyze table  或者修改  analyze table  的参数。
  2. 统计信息准确,为什么读 TiFlash 更快,而优化器选择了 TiKV?
    目前区别 TiFlash 和 TiKV 的代价模型还比较粗糙,可以调小  tidb_opt_seek_factor  的值,让优化器倾向于选择 TiFlash。
  3. 统计信息准确,某个索引要回表,但是它比另一个不用回表的索引实际执行更快,为什么选择了不用回表的索引?
    碰到这种情况,可能是代价估算时对于回表的代价计算得过大,可以调小  tidb_opt_network_factor ,降低回表的代价。

控制索引的选择

通过  Optimizer Hints  可以实现单条查询对索引选择的控制。
  • USE_INDEX / IGNORE_INDEX  可以强制优化器使用/不使用某些索引。
  • READ_FROM_STORAGE  可以强制优化器对于某些表选择 TiKV/TiFlash 的存储引擎进行查询。
 

1.2错误索引的解决方案

在观察到某个查询的执行速度达不到预期时,可能是它的索引使用有误,这时就需要通过一些手段来解决。通常可以先使用 表的健康度信息 来查看统计信息的健康度。根据健康度可以分为以下两种情况处理。

健康度较低

这意味着距离 TiDB 上次  ANALYZE  很久了。这时可以先使用  ANALYZE  命令对统计信息进行更新。更新之后如果索引的使用上还是错误的,可以查看下一小节。

健康度接近 100%

这时意味着刚刚结束  ANALYZE  命令或者结束后不久。这时可能和 TiDB 对行数的估算逻辑有关。
对于等值查询,错误索引可能是由  Count-Min Sketch  引起的。这时可以先检查是不是这种特殊情况,然后进行对应的处理。
如果经过检查发现不是上面的可能情况,可以使用  Optimizer Hints  中提到的  USE_INDEX  或者  use index  来强制选择索引。同时也可以使用 执行计划管理 中提到的方式来非侵入地更改查询的行为。

其他情况

除去上述情况外,也存在因为数据的更新导致现有所有索引都不再适合的情况。这时就需要对条件和数据分布进行分析,查看是否有新的索引可以加快查询速度,然后使用  ADD INDEX  命令增加新的索引。
 
 

1.3Distinct 优化

 

简单 DISTINCT

通常简单的  DISTINCT  会被优化成 GROUP BY 来执行。例如:
mysql > explain select DISTINCT a from t ; + --------------------------+---------+-----------+---------------+-------------------------------------------------------+ | id | estRows | task | access object | operator info | + --------------------------+---------+-----------+---------------+-------------------------------------------------------+ | HashAgg_6 | 2.40 | root | | group by :test . t . a , funcs:firstrow ( test . t . a ) - > test . t . a | | └─TableReader_11 | 3.00 | root | | data :TableFullScan_10 | | └─TableFullScan_10 | 3.00 | cop [ tikv ] | table :t | keep order : false , stats:pseudo | + --------------------------+---------+-----------+---------------+-------------------------------------------------------+ 3 rows in set ( 0.00 sec )

聚合函数 DISTINCT

通常来说,带有  DISTINCT  的聚合函数会单线程的在 TiDB 侧执行。 使用系统变量  tidb_opt_distinct_agg_push_down  或者 TiDB 的配置项  distinct-agg-push-down  控制优化器是否执行带有  DISTINCT  的聚合函数(比如  select count(distinct a) from t )下推到 Coprocessor 的优化操作。
在以下示例中, tidb_opt_distinct_agg_push_down  开启前,TiDB 需要从 TiKV 读取所有数据,并在 TiDB 侧执行  disctinct 。 tidb_opt_distinct_agg_push_down  开启后, distinct a  被下推到了 Coprocessor,在  HashAgg_5  里新增了一个  group by  列  test.t.a 。
mysql > desc select count ( distinct a ) from test . t ; + -------------------------+----------+-----------+---------------+------------------------------------------+ | id | estRows | task | access object | operator info | + -------------------------+----------+-----------+---------------+------------------------------------------+ | StreamAgg_6 | 1.00 | root | | funcs: count ( distinct test . t . a ) - > Column #4 | | └─TableReader_10 | 10000.00 | root | | data :TableFullScan_9 | | └─TableFullScan_9 | 10000.00 | cop [ tikv ] | table :t | keep order : false , stats:pseudo | + -------------------------+----------+-----------+---------------+------------------------------------------+ 3 rows in set ( 0.01 sec )
 
mysql > set session tidb_opt_distinct_agg_push_down = 1 ;
Query OK , 0 rows affected ( 0.00 sec )
 
mysql > desc select count ( distinct a ) from test . t ; + ---------------------------+----------+-----------+---------------+------------------------------------------+ | id | estRows | task | access object | operator info | + ---------------------------+----------+-----------+---------------+------------------------------------------+ | HashAgg_8 | 1.00 | root | | funcs: count ( distinct test . t . a ) - > Column #3 | | └─TableReader_9 | 1.00 | root | | data :HashAgg_5 | | └─HashAgg_5 | 1.00 | cop [ tikv ] | | group by :test . t . a , | | └─TableFullScan_7 | 10000.00 | cop [ tikv ] | table :t | keep order : false , stats:pseudo | + ---------------------------+----------+-----------+---------------+------------------------------------------+ 4 rows in set ( 0.00 sec )
 
2.控制执行计划
2.1 Optimizer Hints
iDB 支持 Optimizer Hints 语法,它基于 MySQL 5.7 中介绍的类似 comment 的语法,例如  /*+ HINT_NAME(t1, t2) */ 。当 TiDB 优化器选择的不是最优查询计划时,建议使用 Optimizer Hints。
注意:
MySQL 命令行客户端在 5.7.7 版本之前默认清除了 Optimizer Hints。如果需要在这些早期版本的客户端中使用  Hint  语法,需要在启动客户端时加上  --comments  选项,例如  mysql -h 127.0.0.1 -P 4000 -uroot --comments 。

语法

Optimizer Hints 不区分大小写,通过  /*+ ... */  注释的形式跟在  SELECT 、 UPDATE  或  DELETE  关键字的后面。 INSERT  关键字后不支持 Optimizer Hints。
多个不同的 Hint 之间需用逗号隔开,例如:
SELECT /*+ USE_INDEX(t1, idx1), HASH_AGG(), HASH_JOIN(t1) */ count ( * ) FROM t t1 , t t2 WHERE t1 . a = t2 . b ;
可以通过  Explain  /  Explain Analyze  语句的输出,来查看 Optimizer Hints 对查询执行计划的影响。
如果 Optimizer Hints 包含语法错误或不完整,查询语句不会报错,而是按照没有 Optimizer Hints 的情况执行。如果 Hint 不适用于当前语句,TiDB 会返回 Warning,用户可以在查询结束后通过  Show Warnings  命令查看具体信息。
注意:
如果注释不是跟在指定的关键字后,会被当作是普通的 MySQL comment,注释不会生效,且不会上报 warning。
TiDB 目前支持的 Optimizer Hints 根据生效范围的不同可以划分为两类:第一类是在查询块范围生效的 Hint,例如  /*+ HASH_AGG() */ ;第二类是在整个查询范围生效的 Hint,例如  /*+ MEMORY_QUOTA(1024 MB)*/
每条语句中每一个查询和子查询都对应着一个不同的查询块,每个查询块有自己对应的名字。以下面这条语句为例:
SELECT * FROM ( SELECT * FROM t ) t1 , ( SELECT * FROM t ) t2 ;
该查询语句有 3 个查询块,最外面一层  SELECT  所在的查询块的名字为  sel_1 ,两个  SELECT  子查询的名字依次为  sel_2  和  sel_3 。其中数字序号根据  SELECT  出现的位置从左到右计数。如果分别用  DELETE  和  UPDATE  查询替代第一个  SELECT  查询,则对应的查询块名字分别为  del_1  和  upd_1 。
 
 

查询块范围生效的 Hint

这类 Hint 可以跟在查询语句中 任意  SELECT 、 UPDATE  或  DELETE  关键字的后面。通过在 Hint 中使用查询块名字可以控制 Hint 的生效范围,以及准确标识查询中的每一个表(有可能表的名字或者别名相同),方便明确 Hint 的参数指向。若不显式地在 Hint 中指定查询块,Hint 默认作用于当前查询块。以如下查询为例:
SELECT /*+ HASH_JOIN(@sel_1 t1@sel_1, t3) */ * FROM ( SELECT t1 . a , t1 . b FROM t t1 , t t2 WHERE t1 . a = t2 . a ) t1 , t t3 WHERE t1 . b = t3 . b ;
该 Hint 在  sel_1  这个查询块中生效,参数分别为  sel_1  中的  t1  表( sel_2  中也有一个  t1  表)和  t3  表。
如上例所述,在 Hint 中使用查询块名字的方式有两种:第一种是作为 Hint 的第一个参数,与其他参数用空格隔开。除  QB_NAME  外,本节所列的所有 Hint 除自身明确列出的参数外都有一个隐藏的可选参数  @QB_NAME ,通过使用这个参数可以指定该 Hint 的生效范围;第二种在 Hint 中使用查询块名字的方式是在参数中的某一个表名后面加  @QB_NAME ,用以明确指出该参数是哪个查询块中的表。
注意:
Hint 声明的位置必须在指定生效的查询块之中或之前,不能是在之后的查询块中,否则无法生效。
 

QB_NAME

当查询语句是包含多层嵌套子查询的复杂语句时,识别某个查询块的序号和名字很可能会出错,Hint  QB_NAME  可以方便我们使用查询块。 QB_NAME  是 Query Block Name 的缩写,用于为某个查询块指定新的名字,同时查询块原本默认的名字依然有效。例如:
SELECT /*+ QB_NAME(QB1) */ * FROM ( SELECT * FROM t ) t1 , ( SELECT * FROM t ) t2 ;
这条 Hint 将最外层  SELECT  查询块的命名为  QB1 ,此时  QB1  和默认名称  sel_1  对于这个查询块来说都是有效的。
注意:
上述例子中,如果指定的  QB_NAME  为  sel_2 ,并且不给原本  sel_2  对应的第二个查询块指定新的  QB_NAME ,则第二个查询块的默认名字  sel_2  会失效。
 

MERGE_JOIN(t1_name [, tl_name ...])

MERGE_JOIN(t1_name [, tl_name ...])  提示优化器对指定表使用 Sort Merge Join 算法。这个算法通常会占用更少的内存,但执行时间会更久。当数据量太大,或系统内存不足时,建议尝试使用。例如:
SELECT /*+ MERGE_JOIN(t1, t2) */ * FROM t1,t2 WHERE t1 . id = t2 . id ;
注意:
MERGE_JOIN  的别名是  TIDB_SMJ ,在 3.0.x 及之前版本仅支持使用该别名;之后的版本同时支持使用这两种名称,但推荐使用  MERGE_JOIN 。
 

INL_JOIN(t1_name [, tl_name ...])

INL_JOIN(t1_name [, tl_name ...])  提示优化器对指定表使用 Index Nested Loop Join 算法。这个算法可能会在某些场景更快,消耗更少系统资源,有的场景会更慢,消耗更多系统资源。对于外表经过 WHERE 条件过滤后结果集较小(小于 1 万行)的场景,可以尝试使用。例如:
SELECT /*+ INL_JOIN(t1, t2) */ * FROM t1,t2 WHERE t1 . id = t2 . id ;
INL_JOIN()  中的参数是建立查询计划时内表的候选表,比如  INL_JOIN(t1)  只会考虑使用 t1 作为内表构建查询计划。表如果指定了别名,就只能使用表的别名作为  INL_JOIN()  的参数;如果没有指定别名,则用表的本名作为其参数。比如在  SELECT /*+ INL_JOIN(t1) */ * FROM t t1, t t2 WHERE t1.a = t2.b;  中, INL_JOIN()  的参数只能使用 t 的别名 t1 或 t2,不能用 t。
注意:
INL_JOIN  的别名是  TIDB_INLJ ,在 3.0.x 及之前版本仅支持使用该别名;之后的版本同时支持使用这两种名称,但推荐使用  INL_JOIN 。
 

INL_HASH_JOIN

INL_HASH_JOIN(t1_name [, tl_name])  提示优化器使用 Index Nested Loop Hash Join 算法。该算法与 Index Nested Loop Join 使用条件完全一样,两者的区别是  INL_JOIN  会在连接的内表上建哈希表,而  INL_HASH_JOIN  会在连接的外表上建哈希表,后者对于内存的使用是有固定上限的,而前者使用的内存使用取决于内表匹配到的行数。

INL_MERGE_JOIN

INL_MERGE_JOIN(t1_name [, tl_name])  提示优化器使用 Index Nested Loop Merge Join 算法。这个 Hint 的适用场景和  INL_JOIN  一致,相比于  INL_JOIN  和  INL_HASH_JOIN  会更节省内存,但使用条件会更苛刻:join keys 中的内表列集合是内表使用的索引的前缀,或内表使用的索引是 join keys 中的内表列集合的前缀。

HASH_JOIN(t1_name [, tl_name ...])

HASH_JOIN(t1_name [, tl_name ...])  提示优化器对指定表使用 Hash Join 算法。这个算法多线程并发执行,执行速度较快,但会消耗较多内存。例如:
SELECT /*+ HASH_JOIN(t1, t2) */ * FROM t1,t2 WHERE t1 . id = t2 . id ;
注意:
HASH_JOIN  的别名是  TIDB_HJ ,在 3.0.x 及之前版本仅支持使用该别名;之后的版本同时支持使用这两种名称,推荐使用  HASH_JOIN 。

HASH_AGG()

HASH_AGG()  提示优化器对指定查询块中所有聚合函数使用 Hash Aggregation 算法。这个算法多线程并发执行,执行速度较快,但会消耗较多内存。例如:
SELECT /*+ HASH_AGG() */ count ( * ) FROM t1,t2 WHERE t1 . a > 10 GROUP BY t1 . id ;

STREAM_AGG()

STREAM_AGG()  提示优化器对指定查询块中所有聚合函数使用 Stream Aggregation 算法。这个算法通常会占用更少的内存,但执行时间会更久。数据量太大,或系统内存不足时,建议尝试使用。例如:
SELECT /*+ STREAM_AGG() */ count ( * ) FROM t1,t2 WHERE t1 . a > 10 GROUP BY t1 . id ;

USE_INDEX(t1_name, idx1_name [, idx2_name ...])

USE_INDEX(t1_name, idx1_name [, idx2_name ...])  提示优化器对指定表仅使用给出的索引。
下面例子的效果等价于  SELECT * FROM t t1 use index(idx1, idx2); :
SELECT /*+ USE_INDEX(t1, idx1, idx2) */ * FROM t1 ;
注意:
当该 Hint 中只指定表名,不指定索引名时,表示不考虑使用任何索引,而是选择全表扫
 

IGNORE_INDEX(t1_name, idx1_name [, idx2_name ...])

IGNORE_INDEX(t1_name, idx1_name [, idx2_name ...])  提示优化器对指定表忽略给出的索引。
下面例子的效果等价于  SELECT * FROM t t1 ignore index(idx1, idx2); :
SELECT /*+ IGNORE_INDEX(t1, idx1, idx2) */ * FROM t t1 ;

AGG_TO_COP()

AGG_TO_COP()  提示优化器将指定查询块中的聚合函数下推到 coprocessor。如果优化器没有下推某些适合下推的聚合函数,建议尝试使用。例如:
SELECT /*+ AGG_TO_COP() */ sum ( t1 . a ) FROM t t1 ;

READ_FROM_STORAGE(TIFLASH[t1_name [, tl_name ...]], TIKV[t2_name [, tl_name ...]])

READ_FROM_STORAGE(TIFLASH[t1_name [, tl_name ...]], TIKV[t2_name [, tl_name ...]])  提示优化器从指定的存储引擎来读取指定的表,目前支持的存储引擎参数有  TIKV  和  TIFLASH 。例如:
SELECT /*+ READ_FROM_STORAGE(TIFLASH[t1], TIKV[t2]) */ t1 . a FROM t t1 , t t2 WHERE t1 . a = t2 . a ;
注意:
如果需要提示优化器使用的表不在同一个数据库内,需要显式指定数据库名。例如  SELECT /*+ READ_FROM_STORAGE(TIFLASH[test1.t1,test2.t2]) */ t1.a FROM test1.t t1, test2.t t2 WHERE t1.a = t2.a; 。
 

IGNORE_INDEX(t1_name, idx1_name [, idx2_name ...])

IGNORE_INDEX(t1_name, idx1_name [, idx2_name ...])  提示优化器对指定表忽略给出的索引。
下面例子的效果等价于  SELECT * FROM t t1 ignore index(idx1, idx2); :
SELECT /*+ IGNORE_INDEX(t1, idx1, idx2) */ * FROM t t1 ;

AGG_TO_COP()

AGG_TO_COP()  提示优化器将指定查询块中的聚合函数下推到 coprocessor。如果优化器没有下推某些适合下推的聚合函数,建议尝试使用。例如:
SELECT /*+ AGG_TO_COP() */ sum ( t1 . a ) FROM t t1 ;

READ_FROM_STORAGE(TIFLASH[t1_name [, tl_name ...]], TIKV[t2_name [, tl_name ...]])

READ_FROM_STORAGE(TIFLASH[t1_name [, tl_name ...]], TIKV[t2_name [, tl_name ...]])  提示优化器从指定的存储引擎来读取指定的表,目前支持的存储引擎参数有  TIKV  和  TIFLASH 。例如:
SELECT /*+ READ_FROM_STORAGE(TIFLASH[t1], TIKV[t2]) */ t1 . a FROM t t1 , t t2 WHERE t1 . a = t2 . a ;
注意:
如果需要提示优化器使用的表不在同一个数据库内,需要显式指定数据库名。例如  SELECT /*+ READ_FROM_STORAGE(TIFLASH[test1.t1,test2.t2]) */ t1.a FROM test1.t t1, test2.t t2 WHERE t1.a = t2.a; 。

USE_INDEX_MERGE(t1_name, idx1_name [, idx2_name ...])

USE_INDEX_MERGE(t1_name, idx1_name [, idx2_name ...])  提示优化器通过 index merge 的方式来访问指定的表,其中索引列表为可选参数。若显式地指出索引列表,会尝试在索引列表中选取索引来构建 index merge。若不给出索引列表,会尝试在所有可用的索引中选取索引来构建 index merge。例如:
SELECT /*+ USE_INDEX_MERGE(t1, idx_a, idx_b, idx_c) */ * FROM t1 WHERE t1 . a > 10 OR t1 . b > 10 ;
当对同一张表有多个  USE_INDEX_MERGE  Hint 时,优化器会从这些 Hint 指定的索引列表的并集中尝试选取索引。
注意:
USE_INDEX_MERGE  的参数是索引名,而不是列名。对于主键索引,索引名为  primary 。
目前该 Hint 生效的条件较为苛刻,包括:
  • 如果查询有除了全表扫以外的单索引扫描方式可以选择,优化器不会选择 index merge;
  • 如果查询在显式事务里,且该条查询之前的语句已经涉及写入,优化器不会选择 index merge;
 

查询范围生效的 Hint

这类 Hint 只能跟在语句中 第一个  SELECT 、 UPDATE  或  DELETE  关键字的后面,等同于在当前这条查询运行时对指定的系统变量进行修改,其优先级高于现有系统变量的值。
注意:
这类 Hint 虽然也有隐藏的可选变量  @QB_NAME ,但就算指定了该值,Hint 还是会在整个查询范围生效。

NO_INDEX_MERGE()

NO_INDEX_MERGE()  会关闭优化器的 index merge 功能。
下面的例子不会使用 index merge:
SELECT /*+ NO_INDEX_MERGE() */ * FROM t WHERE t . a > 0 or t . b > 0 ;
除了 Hint 外,系统变量  tidb_enable_index_merge  也能决定是否开启该功能。
注意:
NO_INDEX_MERGE  优先级高于  USE_INDEX_MERGE ,当这两类 Hint 同时存在时, USE_INDEX_MERGE  不会生效。

USE_TOJA(boolean_value)

参数  boolean_value  可以是  TRUE  或者  FALSE 。 USE_TOJA(TRUE)  会开启优化器尝试将 in (subquery) 条件转换为 join 和 aggregation 的功能。相对地, USE_TOJA(FALSE)  会关闭该功能。
下面的例子会将  in (SELECT t2.a FROM t2) subq  转换为等价的 join 和 aggregation:
SELECT /*+ USE_TOJA(TRUE) */ t1 . a , t1 . b FROM t1 WHERE t1 . a in ( SELECT t2 . a FROM t2 ) subq ;
除了 Hint 外,系统变量  tidb_opt_insubq_to_join_and_agg  也能决定是否开启该功能。

MAX_EXECUTION_TIME(N)

MAX_EXECUTION_TIME(N)  把语句的执行时间限制在  N  毫秒以内,超时后服务器会终止这条语句的执行。
下面的 Hint 设置了 1000 毫秒(即 1 秒)超时:
SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM t1 inner join t2 WHERE t1 . id = t2 . id ;
除了 Hint 之外,系统变量  global.max_execution_time  也能对语句执行时间进行限制。

MEMORY_QUOTA(N)

MEMORY_QUOTA(N)  用于限制语句执行时的内存使用。该 Hint 支持 MB 和 GB 两种单位。内存使用超过该限制时会根据当前设置的内存超限行为来打出一条 log 或者终止语句的执行。
下面的 Hint 设置了 1024 MB 的内存限制:
SELECT /*+ MEMORY_QUOTA(1024 MB) */ * FROM t ;
除了 Hint 外,系统变量  tidb_mem_quota_query  也能限制语句执行的内存使用。

READ_CONSISTENT_REPLICA()

READ_CONSISTENT_REPLICA()  会开启从数据一致的 TiKV follower 节点读取数据的特性。
下面的例子会从 follower 节点读取数据:
SELECT /*+ READ_CONSISTENT_REPLICA() */ * FROM t ;
除了 Hint 外,环境变量  tidb_replica_read  设为  'follower'  或者  'leader'  也能决定是否开启该特性。

IGNORE_PLAN_CACHE()

IGNORE_PLAN_CACHE()  提示优化器在处理当前  prepare  语句时不使用 plan cache。
该 Hint 用于在  prepare-plan-cache  开启的场景下临时对某类查询禁用 plan cache。
以下示例强制该  prepare  语句不使用 plan cache:
prepare stmt FROM 'SELECT /*+ IGNORE_PLAN_CACHE() */ * FROM t WHERE t.id = ?' ;
 
 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值