优化器HINT 一节中已介绍通过在SQL中添加HINT可以控制优化器按HINT指定的行为进行计划生成,直接在SQL语句中添加HINT适用于在系统上线前,将HINT加入到SQL中。但对于已上线的业务,如果出现优化器选择的计划不够优时,则需要在线进行计划绑定,即无需业务进行SQL更改,而是通过DDL操作将一组HINT加入到SQL中,从而使得优化器根据指定的一组HINT,对该SQL生成的新更优计划。我们将该组HINT称为OUTLINE,通过对某条SQL创建OUTLINT可达到计划绑定的目的。

    创建OUTLINE

    1. OceanBase支持通过两种方式创建OUTLINE,一种是通过SQL\_TEXT(用户执行的带参数的原始语句),另一种是通过SQL\_ID创建。

    说明

    注意: 创建OUTLINE需要进入对应database下执行。

    使用SQL_TEXT创建

    使用SQL_TEXT创建OUTLINE后,会生成一个key-value对存储在map中,其中key为绑定的SQL参数化后的问题(具体参数化原则可参考快速参数化约束条件部分),value为绑定的HINT。

    • 语法:
    1. CREATE [OR REPLACE] OUTLINE outline_name ON stmt [ TO target_stmt ]
    • 说明:

    • 指定OR REPLACE后,可以对已经存在执行计划进行replace。

    • 其中stmt一般为一个带有HINT和原始参数的DML语句。
    • 如果不指定TO target_stmt, 则表示如果数据库接受的SQL参数化后与stmt去掉HINT参数化文本相同,则将该SQL绑定stmt中HINT生成执行计划。
    • 如果期望对含有HINT的语句进行固定计划,则需要TO target_stmt来指明原始的SQL。

    说明

    注意: 在使用target_stmt时,严格要求stmt与target_stmt在去掉hint后完全匹配。

    • 举例
    1. CREATE TABLE t1 (c1 int primary key, c2 int, c3 int, index idx_c2(c2));
    2. INSERT INTO t1 values(1, 1, 1), (2, 2, 2), (3, 3, 3);
    3. SELECT * FROM t1 WHERE c2 = 1\G;
    4. Query Plan:
    5. ===================================
    6. |ID|OPERATOR |NAME|EST. ROWS|COST|
    7. -----------------------------------
    8. |0 |TABLE SCAN|t1 |1 |37 |
    9. ===================================

    优化器选择了走主键扫描;而如果数据量很增大后,走索引idx_c2该SQL会更优,此时可以通过创建OUTLINE将该SQL绑定走索引计划。

    创建OUTLINE:

    1. CREATE OUTLINE otl_idx_c2
    2. ON SELECT/*+ index(t1 idx_c2)*/ * FROM t1 WHERE c2 = 1;

    使用SQL_ID创建

    • 语法:CREATE OUTLINE outline_name ON sql_id USING HINT hint;

    • 说明

    sql_id为需要绑定的SQL对应的SQL_ID; SQL_ID可通过以下几种方式获取

    • 查询gv$plan_cache_plan_stat表获取。
    • 查询gv$sql_audit表获取。
    • 通过参数化的原始SQL,使用MD5生成,具体可使用下面这个脚本生成对应SQL的SQL_ID。
    1. import hashlib
    2. sql_text='SELECT * FROM t1 WHERE c2 = ?'
    3. sql_id=hashlib.md5(sql_text.encode('utf-8')).hexdigest().upper()
    4. print(sql_id)
    • hint格式为/*+ xxx */,具体HINT说明可参优化器HINT
    • 使用SQL_TEXT方式创建的OUTLINE会覆盖sql_id方式创建的OUTLINE。SQL_TEXT方式创建的优先级高

    • 如果sql_id对应的SQL语句已经有hint,则创建OUTLINE指定的hint会覆盖原始语句中所有hint

    • 举例为上面SQL_A使用SQL_ID绑定OUTLINE:

    1. CREATE OUTLINE otl_idx_c2
    2. ON "ED570339F2C856BA96008A29EDF04C74"
    3. USING HINT /*+ index(t1 idx_c2)*/ ;

    删除OUTLINE

    • 语法:
    1. DROP OUTLINE outline_name;
    • 说明:删除OUTLINE后,对应SQL重新生成计划时将不再依据绑定的outline生成

    说明

    注意: 删除OUTLINE需要outline_name中指定database名,或者use database后执行。

    OUTLINE视图-gv$outline

    字段名称

    类型

    描述

    tenant_id

    bigint(20)

    租户id

    database_id

    bigint(20)

    数据库 id

    outline_id

    bigint(20)

    outline id

    database_name

    varchar(128)

    数据库名称

    outline_name

    varchar(128)

    outline 名称

    visible_signature

    varchar(32768)

    signature的反序列化结果,为了便于查看signature的信息

    sql_text

    varchar(32768)

    创建outline时,在on clause中指定的sql

    outline_target

    varchar(32768)

    创建outline时,在to clause中指定的sql

    outline_sql

    varchar(32768)

    具有完整outline信息的sql

    确定创建生效

    确定创建的OUTLINE是否成功且符合预期,需要进行如下三步的验证:

    1. 确定是否创建OUTLINE成功。

    通过查看gv$outline中表,确认是否成功创建对应的outline名称的OUTLINE。

    1. select * from oceanbase.gv$outline where outline_name = 'otl_idx_c2'\G;
    2. *************************** 1. row ***************************
    3. tenant_id: 1001
    4. database_id: 1100611139404776
    5. outline_id: 1100611139404777
    6. database_name: test
    7. outline_name: otl_idx_c2
    8. visible_signature: SELECT * FROM t1 WHERE c2 = ?
    9. sql_text: SELECT/*+ index(t1 idx_c2)*/ * FROM t1 WHERE c2 = 1
    10. outline_target:
    11. outline_sql: SELECT /*+ BEGIN_OUTLINE_DATA INDEX(@"SEL$1" "test.t1"@"SEL$1" "idx_c2") END_OUTLINE_DATA*/* FROM t1 WHERE c2 = 1
    1. 确定新的SQL执行是否通过绑定的outline生成了新计划。

    当绑定OUTLINE的SQL有新的流量查询后,查询(g)v$plan_cache_plan_stat表中该SQL对应的计划信息中outline_id,如果outline_id是在gv$outline中查到的outline_id则表示该计划是岸绑定的outline生成的执行计划,否则不是。

    1. select sql_id, plan_id, statement, outline_id, outline_data
    2. from oceanbase.gv$plan_cache_plan_stat
    3. where statement like '%SELECT * FROM t1 WHERE c2 =%'\G
    4. *************************** 1. row ***************************
    5. sql_id: ED570339F2C856BA96008A29EDF04C74
    6. plan_id: 17225
    7. statement: SELECT * FROM t1 WHERE c2 = ?
    8. outline_id: 1100611139404777
    9. outline_data: /*+ BEGIN_OUTLINE_DATA INDEX(@"SEL$1" "test.t1"@"SEL$1" "idx_c2") END_OUTLINE_DATA*/
    1. 确定生成的执行计划是否符合预期。

    确定是通过绑定的outline生成的计划后,需要确定生成的计划是否符合预期,可以通过查询gv$plan_cache_plan_stat表查看plan_cache中缓存的执行计划形状, 具体查看方式可参考实时执行计划展示的介绍。

    1. select OPERATOR, NAME
    2. from oceanbase.gv$plan_cache_plan_explain
    3. where tenant_id = 1001 and ip = '10.101.163.87'
    4. and port = 30474 and plan_id = 17225;
    5. +--------------------+------------+
    6. | OPERATOR | NAME |
    7. +--------------------+------------+
    8. | PHY_ROOT_TRANSMIT | NULL |
    9. | PHY_TABLE_SCAN | t1(idx_c2) |
    10. +--------------------+------------+

    计划绑定与执行计划缓存关系

    1. 使用SQL_TEXT创建OUTLINE后, SQL请求生成新计划查找OUTLINE使用的key与计划缓存使用的key相同,均是SQL参数化后的文本串,

    2. 当创建和删除OUTLINE后,对应SQL有新的请求时,会触发执行计划缓存中对应执行计划失效,更新为按绑定的OUTLINE生成的执行计划。