暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

一文了解TiDB的执行计划绑定功能

数据库中SQL语句的执行计划可能会随着时间的推移而发生变化,数据量的增长、统计信息的更新等都可能会导致相同SQL语句在不同的时间节点其执行计划不一致,从而引发性能问题。在一个稳定运行的生产环境中,大部分情况下我们都希望同一个SQL语句的执行计划保持固定不变,数据库也提供了一些手段帮助用户来稳定执行计划。

比较通用的固化执行计划的方法就是使用HINTHINT最早是Oracle数据库中很有特色的一个功能,也是很多DBA优化中经常采用的一个手段。数据库的优化器是一个非常复杂的模块,即使是Oracle这样的数据库在一些场景下也难以保证执行计划是最优的,HINT提供了一种机制可以告诉优化器按照想要的方式生成执行计划,当然HINT也可以被用来固化语句的执行计划。HINT的典型用法就是select *+ (具体的Hint内容) */…,它通过一种特殊的注释方式来影响执行计划。

业内大多数数据库基本都有HINT的功能,只不过不同数据库可能有自己的HINT写法。笔者之前使用的一款数据库在表名后面增加<<+index index_name>>来让执行计划走索引,后来为了兼容Oracle,也支持了/*+ … */这样的写法。

TiDB也支持HINT功能,语法上兼容Oracle HINT,可参考官方文档 Optimizer Hints | PingCAP 文档中心。在生产系统中,HINT可以被用来解决即时SQL查询的性能问题;在应用系统开发阶段,为了保证SQL执行计划的后续稳定性,开发人员也会选择直接将HINT添加到应用代码中。然而如果系统上线后有些未指定HINT的语句因为某种原因发生了执行计划变化而导致性能突然下降,除了从应用代码添加HINT重新打版以外,还有什么更好的方法呢?TiDB的执行计划绑定功能就是专门解决生产环境执行计划变化而设计的功能,以下具体介绍相关内容。

一.怎么使用执行计划绑定?

1 创建SQL绑定(SQL Binding)

假如一条SQL语句最优的执行计划是索引扫描,但当前却错误的选择了全表扫描。在不修改SQL语句的情况下,我们可以通过创建执行计划绑定的方式来修改它的执行计划。创建绑定有两种方式,一种是根据SQL语句创建绑定,另一种是根据历史执行计划创建绑定。

  • 根据SQL创建绑定

明确需要绑定的SQL语句,也明确应该使用什么HINT来指定执行计划时,可以使用这种方式。比如下面的语句默认采用全表扫描,而使用索引扫描效率更高,就可以直接对语句创建绑定。

    mysql>  explain analyze SELECT * from test1 where b like 'user-1%';


    +-------------------------+-----------+---------+-----------+---------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------+----------+------+


    | id | estRows | actRows | task | access object | execution info | operator info | memory | disk |


    +-------------------------+-----------+---------+-----------+---------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------+----------+------+


    | TableReader_7 | 11315.28 | 11112 | root | | time:236.6ms, loops:14, RU:168.359374, cop_task: {num: 6, max: 102.6ms, min: 20ms, avg: 39.3ms, p95: 102.6ms, max_proc_keys: 82752, p95_proc_keys: 82752, tot_proc: 227.5ms, tot_wait: 323.5µs, rpc_num: 6, rpc_time: 235.9ms, copr_cache_hit_ratio: 0.00, build_task_duration: 13.6µs, max_distsql_concurrency: 1} | data:Selection_6 | 249.9 KB | N/A |


    | └─Selection_6 | 11315.28 | 11112 | cop[tikv] | | tikv_task:{proc max:100ms, min:19ms, avg: 37.5ms, p80:34ms, p95:100ms, iters:122, tasks:6}, scan_detail: {total_process_keys: 100000, total_process_keys_size: 5877790, total_keys: 100006, get_snapshot_time: 116.9µs, rocksdb: {delete_skipped_count: 543136, key_skipped_count: 643136, block: {cache_hit_count: 1582}}} | like(test.test1.b, "user-1%", 92) | N/A | N/A |


    | └─TableFullScan_5 | 100000.00 | 100000 | cop[tikv] | table:test1 | tikv_task:{proc max:76ms, min:18ms, avg: 30ms, p80:27ms, p95:76ms, iters:122, tasks:6} | keep order:false | N/A | N/A |


    +-------------------------+-----------+---------+-----------+---------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------+----------+------+


    3 rows in set (0.24 sec)


    mysql> explain analyze SELECT /*+ use_index(test1, idx1) */ * from test1 where b like 'user-1%';


    +-------------------------------+----------+---------+-----------+----------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+----------+------+


    | id | estRows | actRows | task | access object | execution info | operator info | memory | disk |


    +-------------------------------+----------+---------+-----------+----------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+----------+------+


    | IndexLookUp_7 | 11315.28 | 11112 | root | | time:26.9ms, loops:12, RU:36.470042, index_task: {total_time: 16.9ms, fetch_handle: 16.9ms, build: 3.56µs, wait: 15.3µs}, table_task: {total_time: 24.3ms, num: 4, concurrency: 5}, next: {wait_index: 7.4ms, wait_table_lookup_build: 1.93ms, wait_table_lookup_resp: 16.7ms} | | 752.7 KB | N/A |


    | ├─IndexRangeScan_5(Build) | 11315.28 | 11112 | cop[tikv] | table:test1, index:idx1(b) | time:15.9ms, loops:14, cop_task: {num: 7, max: 5.39ms, min: 1.2ms, avg: 2.56ms, p95: 5.39ms, max_proc_keys: 4360, p95_proc_keys: 4360, tot_proc: 11.5ms, tot_wait: 1.65ms, rpc_num: 7, rpc_time: 17.7ms, copr_cache_hit_ratio: 0.00, build_task_duration: 49.2µs, max_distsql_concurrency: 2}, tikv_task:{proc max:4ms, min:1ms, avg: 1.86ms, p80:3ms, p95:4ms, iters:35, tasks:7}, scan_detail: {total_process_keys: 11112, total_process_keys_size: 820956, total_keys: 11119, get_snapshot_time: 1.42ms, rocksdb: {key_skipped_count: 11112, block: {cache_hit_count: 26, read_count: 27, read_byte: 56.1 KB, read_time: 166µs}}} | range:["user-1","user-2"), keep order:false | N/A | N/A |


    | └─TableRowIDScan_6(Probe) | 11315.28 | 11112 | cop[tikv] | table:test1 | time:20.6ms, loops:16, cop_task: {num: 4, max: 7.81ms, min: 2.69ms, avg: 4.95ms, p95: 7.81ms, max_proc_keys: 5056, p95_proc_keys: 5056, tot_proc: 14.7ms, tot_wait: 234.9µs, rpc_num: 4, rpc_time: 19.7ms, copr_cache_hit_ratio: 0.00, build_task_duration: 87.4µs, max_distsql_concurrency: 1}, tikv_task:{proc max:6ms, min:1ms, avg: 3.5ms, p80:6ms, p95:6ms, iters:29, tasks:4}, scan_detail: {total_process_keys: 11112, total_process_keys_size: 653142, total_keys: 11127, get_snapshot_time: 52.3µs, rocksdb: {delete_skipped_count: 11109, key_skipped_count: 22218, block: {cache_hit_count: 143}}} | keep order:false | N/A | N/A |


    +-------------------------------+----------+---------+-----------+----------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+----------+------+


    3 rows in set (0.03 sec)


    mysql> create global binding for SELECT * from test1 where b like ? using SELECT /*+ use_index(test1, idx1) */ * from test1 where b like ?;


    Query OK, 0 rows affected (0.01 sec)
    复制

    上述语句创建一个global级别的绑定,我们登录另外一个会话时便可以使用show global bindings查看到对应的绑定,同时查看原始语句的执行计划发现已经更改为索引扫描,证明执行计划绑定已生效。

      mysql> show  global bindings;


      +-------------------------------------------------+-------------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+--------+------------------------------------------------------------------+-------------+


      | Original_sql | Bind_sql | Default_db | Status | Create_time | Update_time | Charset | Collation | Source | Sql_digest | Plan_digest |


      +-------------------------------------------------+-------------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+--------+------------------------------------------------------------------+-------------+


      | select * from `test` . `test1` where `b` like ? | SELECT /*+ use_index(`test1` `idx1`)*/ * FROM `test`.`test1` WHERE `b` LIKE ? | test | enabled | 2024-03-08 18:01:47.938 | 2024-03-08 18:01:47.938 | utf8mb4 | utf8mb4_0900_ai_ci | manual | eb338909e651eac55ccb5cb60dd0f3d6962e69257ecec55381013a9517244a42 | |


      +-------------------------------------------------+-------------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+--------+------------------------------------------------------------------+-------------+


      1 row in set (0.00 sec)


      mysql> explain SELECT * from test1 where b like 'user-1%';


      +-------------------------------+----------+-----------+----------------------------+---------------------------------------------+


      | id | estRows | task | access object | operator info |


      +-------------------------------+----------+-----------+----------------------------+---------------------------------------------+


      | IndexLookUp_7 | 11315.28 | root | | |


      | ├─IndexRangeScan_5(Build) | 11315.28 | cop[tikv] | table:test1, index:idx1(b) | range:["user-1","user-2"), keep order:false |


      | └─TableRowIDScan_6(Probe) | 11315.28 | cop[tikv] | table:test1 | keep order:false |


      +-------------------------------+----------+-----------+----------------------------+---------------------------------------------+


      3 rows in set (0.00 sec)
      复制
      • 根据历史执行计划创建绑定

      TiDB中有一个参数tidb_enable_stmt_summary,参数打开表示将SQL耗时等执行信息记录到系统表information_schema.STATEMENTS_SUMMARY中用于定位和排查性能问题。statement_summary用于保存SQL监控指标聚合后的结果,默认每半小时清空一次。

        mysql> select  * from information_schema.statements_summary where QUERY_SAMPLE_TEXT like  '%SELECT *+ use_index(test1, idx1) */ * from test1%' \G


        *************************** 1. row ***************************


        SUMMARY_BEGIN_TIME: 2024-03-11 11:00:00


        SUMMARY_END_TIME: 2024-03-11 11:30:00


        STMT_TYPE: Select


        SCHEMA_NAME: test


        DIGEST: 6c3daca0b3e0fdb874a7fc28883d1a5d55cbf68a38289ea99215bbd6d64d157e


        DIGEST_TEXT: select * from `test1` where `b` like ?


        TABLE_NAMES: test.test1


        INDEX_NAMES: test1:idx1


        SAMPLE_USER: root


        EXEC_COUNT: 1


        SUM_ERRORS: 0


        SUM_WARNINGS: 0


        SUM_LATENCY: 21295823


        MAX_LATENCY: 21295823


        MIN_LATENCY: 21295823


        AVG_LATENCY: 21295823


        AVG_PARSE_LATENCY: 253050


        MAX_PARSE_LATENCY: 253050


        AVG_COMPILE_LATENCY: 577573


        MAX_COMPILE_LATENCY: 577573


        SUM_COP_TASK_NUM: 10


        MAX_COP_PROCESS_TIME: 3395404


        MAX_COP_PROCESS_ADDRESS: 172.20.12.52:20161


        MAX_COP_WAIT_TIME: 87124


        MAX_COP_WAIT_ADDRESS: 172.20.12.52:20161


        AVG_PROCESS_TIME: 17488281


        MAX_PROCESS_TIME: 17488281


        AVG_WAIT_TIME: 549676


        MAX_WAIT_TIME: 549676


        AVG_BACKOFF_TIME: 0


        MAX_BACKOFF_TIME: 0


        AVG_TOTAL_KEYS: 17185


        MAX_TOTAL_KEYS: 17185


        AVG_PROCESSED_KEYS: 17168


        MAX_PROCESSED_KEYS: 17168


        AVG_ROCKSDB_DELETE_SKIPPED_COUNT: 2.9906e-320


        MAX_ROCKSDB_DELETE_SKIPPED_COUNT: 6053


        AVG_ROCKSDB_KEY_SKIPPED_COUNT: 1.1471e-319


        MAX_ROCKSDB_KEY_SKIPPED_COUNT: 23218


        AVG_ROCKSDB_BLOCK_CACHE_HIT_COUNT: 7.07e-322


        MAX_ROCKSDB_BLOCK_CACHE_HIT_COUNT: 143


        AVG_ROCKSDB_BLOCK_READ_COUNT: 0


        MAX_ROCKSDB_BLOCK_READ_COUNT: 0


        AVG_ROCKSDB_BLOCK_READ_BYTE: 0


        MAX_ROCKSDB_BLOCK_READ_BYTE: 0


        AVG_PREWRITE_TIME: 0


        MAX_PREWRITE_TIME: 0


        AVG_COMMIT_TIME: 0


        MAX_COMMIT_TIME: 0


        AVG_GET_COMMIT_TS_TIME: 0


        MAX_GET_COMMIT_TS_TIME: 0


        AVG_COMMIT_BACKOFF_TIME: 0


        MAX_COMMIT_BACKOFF_TIME: 0


        AVG_RESOLVE_LOCK_TIME: 0


        MAX_RESOLVE_LOCK_TIME: 0


        AVG_LOCAL_LATCH_WAIT_TIME: 0


        MAX_LOCAL_LATCH_WAIT_TIME: 0


        AVG_WRITE_KEYS: 0


        MAX_WRITE_KEYS: 0


        AVG_WRITE_SIZE: 0


        MAX_WRITE_SIZE: 0


        AVG_PREWRITE_REGIONS: 0


        MAX_PREWRITE_REGIONS: 0


        AVG_TXN_RETRY: 0


        MAX_TXN_RETRY: 0


        SUM_EXEC_RETRY: 0


        SUM_EXEC_RETRY_TIME: 0


        SUM_BACKOFF_TIMES: 0


        BACKOFF_TYPES: NULL


        AVG_MEM: 620848


        MAX_MEM: 620848


        AVG_DISK: 0


        MAX_DISK: 0


        AVG_KV_TIME: 0


        AVG_PD_TIME: 0


        AVG_BACKOFF_TOTAL_TIME: 0


        AVG_WRITE_SQL_RESP_TIME: 0


        MAX_RESULT_ROWS: 11112


        MIN_RESULT_ROWS: 11112


        AVG_RESULT_ROWS: 11112


        PREPARED: 0


        AVG_AFFECTED_ROWS: 0


        FIRST_SEEN: 2024-03-11 11:07:29


        LAST_SEEN: 2024-03-11 11:07:29


        PLAN_IN_CACHE: 0


        PLAN_CACHE_HITS: 0


        PLAN_IN_BINDING: 0


        QUERY_SAMPLE_TEXT: SELECT *+ use_index(test1, idx1) */ * from test1 where b like 'user-1%'


        PREV_SAMPLE_TEXT:


        PLAN_DIGEST: 41e285a19ae862234757fd639e67b908bb2043638f5e74f83e08247080412f6b


        PLAN: id task estRows operator info actRows execution info memory disk


        IndexLookUp_7 root 11315.28 11112 time:18.1ms, loops:12, index_task: {total_time: 14.2ms, fetch_handle: 14.1ms, build: 3.7µs, wait: 14.7µs}, table_task: {total_time: 16.7ms, num: 4, concurrency: 5}, next: {wait_index: 5.05ms, wait_table_lookup_build: 1.54ms, wait_table_lookup_resp: 10.7ms} 606.3 KB N/A


        ├─IndexRangeScan_5(Build) cop[tikv] 11315.28 table:test1, index:idx1(b), range:["user-1","user-2"), keep order:false 11112 time:13.2ms, loops:14, cop_task: {num: 6, max: 4.22ms, min: 994.3µs, avg: 2.25ms, p95: 4.22ms, max_proc_keys: 4360, p95_proc_keys: 4360, tot_proc: 9.18ms, tot_wait: 319.8µs, rpc_num: 6, rpc_time: 13.3ms, copr_cache_hit_ratio: 0.00, build_task_duration: 33µs, max_distsql_concurrency: 1}, tikv_task:{proc max:4ms, min:0s, avg: 1.5ms, p80:2ms, p95:4ms, iters:34, tasks:6}, scan_detail: {total_process_keys: 11112, total_process_keys_size: 820956, total_keys: 11118, get_snapshot_time: 112.7µs, rocksdb: {key_skipped_count: 11112, block: {cache_hit_count: 49}}} N/A N/A


        └─TableRowIDScan_6(Probe) cop[tikv] 11315.28 table:test1, keep order:false 11112 time:13.2ms, loops:16, cop_task: {num: 4, max: 4.8ms, min: 343.6µs, avg: 3.05ms, p95: 4.8ms, max_proc_keys: 2688, p95_proc_keys: 2688, tot_proc: 8.31ms, tot_wait: 229.8µs, rpc_num: 4, rpc_time: 12.1ms, copr_cache_hit_ratio: 0.25, build_task_duration: 148.6µs, max_distsql_concurrency: 1}, tikv_task:{proc max:6ms, min:2ms, avg: 3.5ms, p80:6ms, p95:6ms, iters:29, tasks:4}, scan_detail: {total_process_keys: 6056, total_process_keys_size: 355952, total_keys: 6067, get_snapshot_time: 67.2µs, rocksdb: {delete_skipped_count: 6053, key_skipped_count: 12106, block: {cache_hit_count: 94}}} N/A N/A


        BINARY_PLAN: wg1YCr0NCg1JbmRleExvb2tVcF83EsMFChAFEshSYW5nZVNjYW5fNRoBASEXw5QbeXpBQSnOWm9YpBnGQDDoVjgCQAJKGgoYCgR0ZXN0EgUBBjgxGgkKBGlkeDESAWJSK3IBRSg6WyJ1c2VyLTEiLAkJ8EkyIiksIGtlZXAgb3JkZXI6ZmFsc2VaFXRpbWU6MTMuMm1zLCBsb29wczoxNGKKAmNvcF90YXNrOiB7bnVtOiA2LCBtYXg6IDQuMgUrgG1pbjogOTk0LjPCtXMsIGF2ZzogMi4yNW1zLCBwOTU6IBUpRGF4X3Byb2Nfa2V5czogNDM2MAUiRhUACHRvdAUVFDogOS4xOAFJARIod2FpdDogMzE5LjgFaihycGNfbnVtOiA2LAUMBcEQIDEzLjMBMqBjb3ByX2NhY2hlX2hpdF9yYXRpbzogMC4wMCwgYnVpbGRfdGFza19kdQUaDG46IDMJwAGmdGRpc3RzcWxfY29uY3VycmVuY3k6IDF9ao8CdGlrdgU5BDp7AcsBLgQ6NAFvIQYAMBH/BDEuCf4IODA6JSMhBwUmJGl0ZXJzOjM0LCAhUmBzOjZ9LCBzY2FuX2RldGFpbDoge3RvdGFsBf0IZXNzLSoUMTExMTIsIQQ6GwAwX3NpemU6IDgyMDk1NhEhKV4BNDw4LCBnZXRfc25hcHNob3RfLSEMMTIuNylBaG9ja3NkYjoge2tleV9za2lwcGVkX2NvdW50OgVBKDIsIGJsb2NrOiB7OUoNIBg0OX19fXD/EQEEAXgRCkD/ARK4BQoQVGFibGVSb3dJREXGMDYaAQIhCX6Q2eeAQ0FCxgIQDwoNCgRBwE3GBFIQlqACDDZiiwI+oAIENCwllgggNC5FOiGZFCAzNDMuNgX0RZ8IMy4wKaFJnwkoAGEyngIMMjY4OEKeAgkVQYwh/RQ6IDguMzEh/QESSZ4EMjJCngIANDKeAgQyLgUyAGNangIEMjVangIQMTQ4LjYFwgGpXqECAKlSoQIANgGkIQhFjS0CKQEEODAJHiEKBSdJogQyOVGiADSSogIINjA1VYAhG1G8AF9JoRAzNTU5NVXCSaEMNjA2N1KgAgw2Ny4yBelZnxRkZWxldGU+ogIMNjA1M4HJRrsCDDIxMDZquwIEOTRiuwIcIUjI1Zo4GHU2owIMAUABWq0gADgpwAhsb2+hIBAyYusBaaHBiRRxv21pADSpTDxmZXRjaF9oYW5kbGU6IDE0CUKFhAg6IDNtjEk/BDE0BQ8AfSF+CGJsZVJWABg2LjdtcywghfoINCwgMqQEKDV9LCBuZXh0OiB7AVEAXwWcCDogNU3wBRQAdAVaGGxvb2t1cF8NhggxLjWFoEYhABxyZXNwOiAxMAF1RH1wsPIleP///////////wEYAQ==


        CHARSET: utf8mb4


        COLLATION: utf8mb4_0900_ai_ci


        PLAN_HINT: use_index(@`sel_1` `test`.`test1` `idx1`), no_order_index(@`sel_1` `test`.`test1` `idx1`)


        MAX_REQUEST_UNIT_READ: 28.53761791796875


        AVG_REQUEST_UNIT_READ: 28.53761791796875


        MAX_REQUEST_UNIT_WRITE: 0


        AVG_REQUEST_UNIT_WRITE: 0


        MAX_QUEUED_RC_TIME: 0


        AVG_QUEUED_RC_TIME: 0


        RESOURCE_GROUP: default


        1 row in set (0.03 sec)
        复制

        假如某条最近执行的SQL语句执行计划符合预期,从statements_summary表中找到对应记录后并记录字段plan_digest,之后我们便可以根据这个plan_digest来创建绑定。

          mysql> create  global binding from history using plan digest  '41e285a19ae862234757fd639e67b908bb2043638f5e74f83e08247080412f6b';


          Query OK, 0 rows affected (0.10 sec)


          mysql> show global bindings;


          +-------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+---------+------------------------------------------------------------------+------------------------------------------------------------------+


          | Original_sql | Bind_sql | Default_db | Status | Create_time | Update_time | Charset | Collation | Source | Sql_digest | Plan_digest |


          +-------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+---------+------------------------------------------------------------------+------------------------------------------------------------------+


          | select * from `test` . `test1` where `b` like ? | SELECT *+ use_index(@`sel_1` `test`.`test1` `idx1`) no_order_index(@`sel_1` `test`.`test1` `idx1`)*/ * FROM `test`.`test1` WHERE `b` LIKE 'user-1%' | test | enabled | 2024-03-11 11:17:42.220 | 2024-03-11 11:17:42.220 | utf8mb4 | utf8mb4_0900_ai_ci | history | eb338909e651eac55ccb5cb60dd0f3d6962e69257ecec55381013a9517244a42 | 41e285a19ae862234757fd639e67b908bb2043638f5e74f83e08247080412f6b |


          +-------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+---------+------------------------------------------------------------------+------------------------------------------------------------------+


          1 row in set (0.00 sec)
          复制

          2 删除SQL绑定

          假如某个SQL绑定不想使用了,可以将其删除。删除绑定可以使用SQL语句来删除,也可以根据plan digest来删除。

          • 根据SQL删除绑定

            mysql> show  global bindings;


            +-------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+---------+------------------------------------------------------------------+------------------------------------------------------------------+


            | Original_sql | Bind_sql | Default_db | Status | Create_time | Update_time | Charset | Collation | Source | Sql_digest | Plan_digest |


            +-------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+---------+------------------------------------------------------------------+------------------------------------------------------------------+


            | select * from `test` . `test1` where `b` like ? | SELECT /*+ use_index(@`sel_1` `test`.`test1` `idx1`) no_order_index(@`sel_1` `test`.`test1` `idx1`)*/ * FROM `test`.`test1` WHERE `b` LIKE 'user-1%' | test | enabled | 2024-03-11 11:17:42.220 | 2024-03-11 11:17:42.220 | utf8mb4 | utf8mb4_0900_ai_ci | history | eb338909e651eac55ccb5cb60dd0f3d6962e69257ecec55381013a9517244a42 | 41e285a19ae862234757fd639e67b908bb2043638f5e74f83e08247080412f6b |


            +-------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+---------+------------------------------------------------------------------+------------------------------------------------------------------+


            1 row in set (0.00 sec)


            mysql> drop global binding for select * from `test` . `test1` where `b` like ?;


            Query OK, 1 row affected (0.02 sec)


            mysql> show global bindings;


            Empty set (0.00 sec)
            复制
            • 根据plan digest删除绑定

              mysql> show  global bindings;


              +-------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+---------+------------------------------------------------------------------+------------------------------------------------------------------+


              | Original_sql | Bind_sql | Default_db | Status | Create_time | Update_time | Charset | Collation | Source | Sql_digest | Plan_digest |


              +-------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+---------+------------------------------------------------------------------+------------------------------------------------------------------+


              | select * from `test` . `test1` where `b` like ? | SELECT /*+ use_index(@`sel_1` `test`.`test1` `idx1`) no_order_index(@`sel_1` `test`.`test1` `idx1`)*/ * FROM `test`.`test1` WHERE `b` LIKE 'user-1%' | test | enabled | 2024-03-11 11:43:15.119 | 2024-03-11 11:43:15.119 | utf8mb4 | utf8mb4_0900_ai_ci | history | eb338909e651eac55ccb5cb60dd0f3d6962e69257ecec55381013a9517244a42 | 41e285a19ae862234757fd639e67b908bb2043638f5e74f83e08247080412f6b |


              +-------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+---------+------------------------------------------------------------------+------------------------------------------------------------------+


              1 row in set (0.01 sec)


              mysql> drop global binding for sql digest 'eb338909e651eac55ccb5cb60dd0f3d6962e69257ecec55381013a9517244a42';


              Query OK, 1 row affected (0.01 sec)


              mysql> show global bindings;


              Empty set (0.00 sec)
              复制

              3 查看SQL绑定是否缓存

              SQL绑定可以缓存在TiDB Server中,这样可以提升获取执行计划的效率。用于缓存SQL绑定的内存大小由参数tidb_mem_quota_binding_cache决定,默认为64MB

                mysql> select  @@tidb_mem_quota_binding_cache;


                +--------------------------------+


                | @@tidb_mem_quota_binding_cache |


                +--------------------------------+


                | 67108864 |


                +--------------------------------+


                1 row in set (0.00 sec)





                mysql> show binding_cache status;


                +-------------------+-------------------+--------------+--------------+


                | bindings_in_cache | bindings_in_table | memory_usage | memory_quota |


                +-------------------+-------------------+--------------+--------------+


                | 1 | 1 | 400 Bytes | 64 MB |


                +-------------------+-------------------+--------------+--------------+


                1 row in set (0.00 sec)
                复制

                二.如何使用自动捕获绑定来应对升级造成的执行计划不一致?

                有时候我们可能会发现,当系统进行版本升级后某些SQL语句由于执行计划变化而导致突然变慢的情况,这可能是由于不同版本在优化器选择方面的成本计算不同导致。那么我们有没有一种方法可以将升级前语句的执行计划进行绑定保存呢?

                TiDB提供了自动捕获执行计划绑定功能,这个功能由参数tidb_capture_plan_baselines控制,默认是关闭的。当我们需要进行版本升级之前,可以在现有版本中打开这个参数并运行一段时间,数据库会默认每3秒遍历一次statement summary中的历史SQL语句,并为至少出现两次的SQL语句自动捕获绑定。

                当然,这种自动捕获的方式也有一定局限性,所以满足条件的SQL语句都会被捕获。如果我们想过滤某些SQL语句,可以将过滤规则插入到系统表mysql.capture_plan_baselines_blacklist中,可以按表名、执行频率、用户名进行过滤。

                本文简单的聊聊TiDB中的执行计划绑定功能,描述如何使用执行计划绑定以及如何利用自动捕获执行计划的能力来应对版本升级导致的执行计划变更问题。关于执行计划管理的更多细节,请参考官网执行计划管理 (SPM) | PingCAP 文档中心 介绍。

                文章转载自数据源的技术后花园,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                评论