- Docs Home
- About TiDB
- Quick Start
- Develop
- Overview
- Quick Start
- Build a TiDB Cluster in TiDB Cloud (Developer Tier)
- CRUD SQL in TiDB
- Build a Simple CRUD App with TiDB
- Example Applications
- Connect to TiDB
- Design Database Schema
- Write Data
- Read Data
- Transaction
- Optimize
- Troubleshoot
- Reference
- Cloud Native Development Environment
- Third-party Support
- Deploy
- Software and Hardware Requirements
- Environment Configuration Checklist
- Plan Cluster Topology
- Install and Start
- Verify Cluster Status
- Test Cluster Performance
- Migrate
- Overview
- Migration Tools
- Migration Scenarios
- Migrate from Aurora
- Migrate MySQL of Small Datasets
- Migrate MySQL of Large Datasets
- Migrate and Merge MySQL Shards of Small Datasets
- Migrate and Merge MySQL Shards of Large Datasets
- Migrate from CSV Files
- Migrate from SQL Files
- Migrate from One TiDB Cluster to Another TiDB Cluster
- Migrate from TiDB to MySQL-compatible Databases
- Advanced Migration
- Integrate
- Overview
- Integration Scenarios
- Maintain
- Monitor and Alert
- Troubleshoot
- TiDB Troubleshooting Map
- Identify Slow Queries
- Analyze Slow Queries
- SQL Diagnostics
- Identify Expensive Queries Using Top SQL
- Identify Expensive Queries Using Logs
- Statement Summary Tables
- Troubleshoot Hotspot Issues
- Troubleshoot Increased Read and Write Latency
- Save and Restore the On-Site Information of a Cluster
- Troubleshoot Cluster Setup
- Troubleshoot High Disk I/O Usage
- Troubleshoot Lock Conflicts
- Troubleshoot TiFlash
- Troubleshoot Write Conflicts in Optimistic Transactions
- Troubleshoot Inconsistency Between Data and Indexes
- Performance Tuning
- Tuning Guide
- Configuration Tuning
- System Tuning
- Software Tuning
- SQL Tuning
- Overview
- Understanding the Query Execution Plan
- SQL Optimization Process
- Overview
- Logic Optimization
- Physical Optimization
- Prepare Execution Plan Cache
- Control Execution Plans
- Tutorials
- TiDB Tools
- Overview
- Use Cases
- Download
- TiUP
- Documentation Map
- Overview
- Terminology and Concepts
- Manage TiUP Components
- FAQ
- Troubleshooting Guide
- Command Reference
- Overview
- TiUP Commands
- TiUP Cluster Commands
- Overview
- tiup cluster audit
- tiup cluster check
- tiup cluster clean
- tiup cluster deploy
- tiup cluster destroy
- tiup cluster disable
- tiup cluster display
- tiup cluster edit-config
- tiup cluster enable
- tiup cluster help
- tiup cluster import
- tiup cluster list
- tiup cluster patch
- tiup cluster prune
- tiup cluster reload
- tiup cluster rename
- tiup cluster replay
- tiup cluster restart
- tiup cluster scale-in
- tiup cluster scale-out
- tiup cluster start
- tiup cluster stop
- tiup cluster template
- tiup cluster upgrade
- TiUP DM Commands
- Overview
- tiup dm audit
- tiup dm deploy
- tiup dm destroy
- tiup dm disable
- tiup dm display
- tiup dm edit-config
- tiup dm enable
- tiup dm help
- tiup dm import
- tiup dm list
- tiup dm patch
- tiup dm prune
- tiup dm reload
- tiup dm replay
- tiup dm restart
- tiup dm scale-in
- tiup dm scale-out
- tiup dm start
- tiup dm stop
- tiup dm template
- tiup dm upgrade
- TiDB Cluster Topology Reference
- DM Cluster Topology Reference
- Mirror Reference Guide
- TiUP Components
- PingCAP Clinic Diagnostic Service
- TiDB Operator
- Dumpling
- TiDB Lightning
- TiDB Data Migration
- About TiDB Data Migration
- Architecture
- Quick Start
- Deploy a DM cluster
- Tutorials
- Advanced Tutorials
- Maintain
- Cluster Upgrade
- Tools
- Performance Tuning
- Manage Data Sources
- Manage Tasks
- Export and Import Data Sources and Task Configurations of Clusters
- Handle Alerts
- Daily Check
- Reference
- Architecture
- Command Line
- Configuration Files
- OpenAPI
- Compatibility Catalog
- Secure
- Monitoring and Alerts
- Error Codes
- Glossary
- Example
- Troubleshoot
- Release Notes
- Backup & Restore (BR)
- Point-in-Time Recovery
- TiDB Binlog
- TiCDC
- Dumpling
- sync-diff-inspector
- TiSpark
- Reference
- Cluster Architecture
- Key Monitoring Metrics
- Secure
- Privileges
- SQL
- SQL Language Structure and Syntax
- SQL Statements
ADD COLUMN
ADD INDEX
ADMIN
ADMIN CANCEL DDL
ADMIN CHECKSUM TABLE
ADMIN CHECK [TABLE|INDEX]
ADMIN SHOW DDL [JOBS|QUERIES]
ADMIN SHOW TELEMETRY
ALTER DATABASE
ALTER INDEX
ALTER INSTANCE
ALTER PLACEMENT POLICY
ALTER TABLE
ALTER TABLE COMPACT
ALTER TABLE SET TIFLASH MODE
ALTER USER
ANALYZE TABLE
BACKUP
BATCH
BEGIN
CHANGE COLUMN
COMMIT
CHANGE DRAINER
CHANGE PUMP
CREATE [GLOBAL|SESSION] BINDING
CREATE DATABASE
CREATE INDEX
CREATE PLACEMENT POLICY
CREATE ROLE
CREATE SEQUENCE
CREATE TABLE LIKE
CREATE TABLE
CREATE USER
CREATE VIEW
DEALLOCATE
DELETE
DESC
DESCRIBE
DO
DROP [GLOBAL|SESSION] BINDING
DROP COLUMN
DROP DATABASE
DROP INDEX
DROP PLACEMENT POLICY
DROP ROLE
DROP SEQUENCE
DROP STATS
DROP TABLE
DROP USER
DROP VIEW
EXECUTE
EXPLAIN ANALYZE
EXPLAIN
FLASHBACK TABLE
FLUSH PRIVILEGES
FLUSH STATUS
FLUSH TABLES
GRANT <privileges>
GRANT <role>
INSERT
KILL [TIDB]
LOAD DATA
LOAD STATS
MODIFY COLUMN
PREPARE
RECOVER TABLE
RENAME INDEX
RENAME TABLE
REPLACE
RESTORE
REVOKE <privileges>
REVOKE <role>
ROLLBACK
SAVEPOINT
SELECT
SET DEFAULT ROLE
SET [NAMES|CHARACTER SET]
SET PASSWORD
SET ROLE
SET TRANSACTION
SET [GLOBAL|SESSION] <variable>
SHOW ANALYZE STATUS
SHOW [BACKUPS|RESTORES]
SHOW [GLOBAL|SESSION] BINDINGS
SHOW BUILTINS
SHOW CHARACTER SET
SHOW COLLATION
SHOW [FULL] COLUMNS FROM
SHOW CONFIG
SHOW CREATE PLACEMENT POLICY
SHOW CREATE SEQUENCE
SHOW CREATE TABLE
SHOW CREATE USER
SHOW DATABASES
SHOW DRAINER STATUS
SHOW ENGINES
SHOW ERRORS
SHOW [FULL] FIELDS FROM
SHOW GRANTS
SHOW INDEX [FROM|IN]
SHOW INDEXES [FROM|IN]
SHOW KEYS [FROM|IN]
SHOW MASTER STATUS
SHOW PLACEMENT
SHOW PLACEMENT FOR
SHOW PLACEMENT LABELS
SHOW PLUGINS
SHOW PRIVILEGES
SHOW [FULL] PROCESSSLIST
SHOW PROFILES
SHOW PUMP STATUS
SHOW SCHEMAS
SHOW STATS_HEALTHY
SHOW STATS_HISTOGRAMS
SHOW STATS_META
SHOW STATUS
SHOW TABLE NEXT_ROW_ID
SHOW TABLE REGIONS
SHOW TABLE STATUS
SHOW [FULL] TABLES
SHOW [GLOBAL|SESSION] VARIABLES
SHOW WARNINGS
SHUTDOWN
SPLIT REGION
START TRANSACTION
TABLE
TRACE
TRUNCATE
UPDATE
USE
WITH
- Data Types
- Functions and Operators
- Overview
- Type Conversion in Expression Evaluation
- Operators
- Control Flow Functions
- String Functions
- Numeric Functions and Operators
- Date and Time Functions
- Bit Functions and Operators
- Cast Functions and Operators
- Encryption and Compression Functions
- Locking Functions
- Information Functions
- JSON Functions
- Aggregate (GROUP BY) Functions
- Window Functions
- Miscellaneous Functions
- Precision Math
- Set Operations
- List of Expressions for Pushdown
- TiDB Specific Functions
- Clustered Indexes
- Constraints
- Generated Columns
- SQL Mode
- Table Attributes
- Transactions
- Garbage Collection (GC)
- Views
- Partitioning
- Temporary Tables
- Cached Tables
- Character Set and Collation
- Placement Rules in SQL
- System Tables
mysql
- INFORMATION_SCHEMA
- Overview
ANALYZE_STATUS
CLIENT_ERRORS_SUMMARY_BY_HOST
CLIENT_ERRORS_SUMMARY_BY_USER
CLIENT_ERRORS_SUMMARY_GLOBAL
CHARACTER_SETS
CLUSTER_CONFIG
CLUSTER_HARDWARE
CLUSTER_INFO
CLUSTER_LOAD
CLUSTER_LOG
CLUSTER_SYSTEMINFO
COLLATIONS
COLLATION_CHARACTER_SET_APPLICABILITY
COLUMNS
DATA_LOCK_WAITS
DDL_JOBS
DEADLOCKS
ENGINES
INSPECTION_RESULT
INSPECTION_RULES
INSPECTION_SUMMARY
KEY_COLUMN_USAGE
METRICS_SUMMARY
METRICS_TABLES
PARTITIONS
PLACEMENT_POLICIES
PROCESSLIST
REFERENTIAL_CONSTRAINTS
SCHEMATA
SEQUENCES
SESSION_VARIABLES
SLOW_QUERY
STATISTICS
TABLES
TABLE_CONSTRAINTS
TABLE_STORAGE_STATS
TIDB_HOT_REGIONS
TIDB_HOT_REGIONS_HISTORY
TIDB_INDEXES
TIDB_SERVERS_INFO
TIDB_TRX
TIFLASH_REPLICA
TIKV_REGION_PEERS
TIKV_REGION_STATUS
TIKV_STORE_STATUS
USER_PRIVILEGES
VARIABLES_INFO
VIEWS
METRICS_SCHEMA
- UI
- TiDB Dashboard
- Overview
- Maintain
- Access
- Overview Page
- Cluster Info Page
- Top SQL Page
- Key Visualizer Page
- Metrics Relation Graph
- SQL Statements Analysis
- Slow Queries Page
- Cluster Diagnostics
- Monitoring Page
- Search Logs Page
- Instance Profiling
- Session Management and Configuration
- FAQ
- CLI
- Command Line Flags
- Configuration File Parameters
- System Variables
- Storage Engines
- Telemetry
- Errors Codes
- Table Filter
- Schedule Replicas by Topology Labels
- FAQs
- Release Notes
- All Releases
- Release Timeline
- TiDB Versioning
- TiDB Installation Packages
- v6.2
- v6.1
- v6.0
- v5.4
- v5.3
- v5.2
- v5.1
- v5.0
- v4.0
- v3.1
- v3.0
- v2.1
- v2.0
- v1.0
- Glossary
Optimizer Hints
TiDB supports optimizer hints, which are based on the comment-like syntax introduced in MySQL 5.7. For example, one of the common syntaxes is /*+ HINT_NAME([t1_name [, t2_name] ...]) */
. Use of optimizer hints is recommended in cases where the TiDB optimizer selects a less optimal query plan.
MySQL command-line clients earlier than 5.7.7 strip optimizer hints by default. If you want to use the Hint
syntax in these earlier versions, add the --comments
option when starting the client. For example: mysql -h 127.0.0.1 -P 4000 -uroot --comments
.
Syntax
Optimizer hints are case insensitive and specified within /*+ ... */
comments following the SELECT
, UPDATE
or DELETE
keyword in a SQL statement. Optimizer hints are not currently supported for INSERT
statements.
Multiple hints can be specified by separating with commas. For example, the following query uses three different hints:
SELECT /*+ USE_INDEX(t1, idx1), HASH_AGG(), HASH_JOIN(t1) */ count(*) FROM t t1, t t2 WHERE t1.a = t2.b;
How optimizer hints affect query execution plans can be observed in the output of EXPLAIN
and EXPLAIN ANALYZE
.
An incorrect or incomplete hint will not result in a statement error. This is because hints are intended to have only a hint (suggestion) semantic to query execution. Similarly, TiDB will at most return a warning if a hint is not applicable.
If the comments do not follow behind the specified keywords, they will be treated as common MySQL comments. The comments do not take effect, and no warning is reported.
Currently, TiDB supports two categories of hints, which are different in scope. The first category of hints takes effect in the scope of query blocks, such as /*+ HASH_AGG() */
; the second category of hints takes effect in the whole query, such as /*+ MEMORY_QUOTA(1024 MB)*/
.
Each query or sub-query in a statement corresponds to a different query block, and each query block has its own name. For example:
SELECT * FROM (SELECT * FROM t) t1, (SELECT * FROM t) t2;
The above query statement has three query blocks: the outermost SELECT
corresponds to the first query block, whose name is sel_1
; the two SELECT
sub-queries correspond to the second and the third query block, whose names are sel_2
and sel_3
, respectively. The sequence of the numbers is based on the appearance of SELECT
from left to right. If you replace the first SELECT
with DELETE
or UPDATE
, then the corresponding query block names are del_1
or upd_1
.
Hints that take effect in query blocks
This category of hints can follow behind any SELECT
, UPDATE
or DELETE
keywords. To control the effective scope of the hint, use the name of the query block in the hint. You can make the hint parameters clear by accurately identifying each table in the query (in case of duplicated table names or aliases). If no query block is specified in the hint, the hint takes effect in the current block by default.
For example:
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;
This hint takes effect in the sel_1
query block, and its parameters are the t1
and t3
tables in sel_1
(sel_2
also contains a t1
table).
As described above, you can specify the name of the query block in the hint in the following ways:
- Set the query block name as the first parameter of the hint, and separate it from other parameters with a space. In addition to
QB_NAME
, all the hints listed in this section also have another optional hidden parameter@QB_NAME
. By using this parameter, you can specify the effective scope of this hint. - Append
@QB_NAME
to a table name in the parameter to explicitly specify which query block this table belongs to.
You must put the hint in or before the query block where the hint takes effect. If the hint is put after the query block, it cannot take effect.
QB_NAME
If the query statement is a complicated statement that includes multiple nested queries, the ID and name of a certain query block might be mistakenly identified. The hint QB_NAME
can help us in this regard.
QB_NAME
means Query Block Name. You can specify a new name to a query block. The specified QB_NAME
and the previous default name are both valid. For example:
SELECT /*+ QB_NAME(QB1) */ * FROM (SELECT * FROM t) t1, (SELECT * FROM t) t2;
This hint specifies the outer SELECT
query block's name to QB1
, which makes QB1
and the default name sel_1
both valid for the query block.
In the above example, if the hint specifies the QB_NAME
to sel_2
and does not specify a new QB_NAME
for the original second SELECT
query block, then sel_2
becomes an invalid name for the second SELECT
query block.
MERGE_JOIN(t1_name [, tl_name ...])
The MERGE_JOIN(t1_name [, tl_name ...])
hint tells the optimizer to use the sort-merge join algorithm for the given table(s). Generally, this algorithm consumes less memory but takes longer processing time. If there is a very large data volume or insufficient system memory, it is recommended to use this hint. For example:
select /*+ MERGE_JOIN(t1, t2) */ * from t1, t2 where t1.id = t2.id;
TIDB_SMJ
is the alias for MERGE_JOIN
in TiDB 3.0.x and earlier versions. If you are using any of these versions, you must apply the TIDB_SMJ(t1_name [, tl_name ...])
syntax for the hint. For the later versions of TiDB, TIDB_SMJ
and MERGE_JOIN
are both valid names for the hint, but MERGE_JOIN
is recommended.
INL_JOIN(t1_name [, tl_name ...])
The INL_JOIN(t1_name [, tl_name ...])
hint tells the optimizer to use the index nested loop join algorithm for the given table(s). This algorithm might consume less system resources and take shorter processing time in some scenarios and might produce an opposite result in other scenarios. If the result set is less than 10,000 rows after the outer table is filtered by the WHERE
condition, it is recommended to use this hint. For example:
select /*+ INL_JOIN(t1, t2) */ * from t1, t2 where t1.id = t2.id;
The parameter(s) given in INL_JOIN()
is the candidate table for the inner table when you create the query plan. For example, INL_JOIN(t1)
means that TiDB only considers using t1
as the inner table to create a query plan. If the candidate table has an alias, you must use the alias as the parameter in INL_JOIN()
; if it does not has an alias, use the table's original name as the parameter. For example, in the select /*+ INL_JOIN(t1) */ * from t t1, t t2 where t1.a = t2.b;
query, you must use the t
table's alias t1
or t2
rather than t
as INL_JOIN()
's parameter.
TIDB_INLJ
is the alias for INL_JOIN
in TiDB 3.0.x and earlier versions. If you are using any of these versions, you must apply the TIDB_INLJ(t1_name [, tl_name ...])
syntax for the hint. For the later versions of TiDB, TIDB_INLJ
and INL_JOIN
are both valid names for the hint, but INL_JOIN
is recommended.
INL_HASH_JOIN
The INL_HASH_JOIN(t1_name [, tl_name])
hint tells the optimizer to use the index nested loop hash join algorithm. The conditions for using this algorithm are the same with the conditions for using the index nested loop join algorithm. The difference between the two algorithms is that INL_JOIN
creates a hash table on the joined inner table, but INL_HASH_JOIN
creates a hash table on the joined outer table. INL_HASH_JOIN
has a fixed limit on memory usage, while the memory used by INL_JOIN
depends on the number of rows matched in the inner table.
HASH_JOIN(t1_name [, tl_name ...])
The HASH_JOIN(t1_name [, tl_name ...])
hint tells the optimizer to use the hash join algorithm for the given table(s). This algorithm allows the query to be executed concurrently with multiple threads, which achieves a higher processing speed but consumes more memory. For example:
select /*+ HASH_JOIN(t1, t2) */ * from t1, t2 where t1.id = t2.id;
TIDB_HJ
is the alias for HASH_JOIN
in TiDB 3.0.x and earlier versions. If you are using any of these versions, you must apply the TIDB_HJ(t1_name [, tl_name ...])
syntax for the hint. For the later versions of TiDB, TIDB_HJ
and HASH_JOIN
are both valid names for the hint, but HASH_JOIN
is recommended.
SEMI_JOIN_REWRITE()
The SEMI_JOIN_REWRITE()
hint tells the optimizer to rewrite the semi-join query to an ordinary join query. Currently, this hint only works for EXISTS
subqueries.
If this hint is not used to rewrite the query, when the hash join is selected in the execution plan, the semi-join query can only use the subquery to build a hash table. In this case, when the result of the subquery is bigger than that of the outer query, the execution speed might be slower than expected.
Similarly, when the index join is selected in the execution plan, the semi-join query can only use the outer query as the driving table. In this case, when the result of the subquery is smaller than that of the outer query, the execution speed might be slower than expected.
When SEMI_JOIN_REWRITE()
is used to rewrite the query, the optimizer can extend the selection range to select a better execution plan.
-- Does not use SEMI_JOIN_REWRITE() to rewrite the query.
EXPLAIN SELECT * FROM t WHERE EXISTS (SELECT 1 FROM t1 WHERE t1.a = t.a);
+-----------------------------+---------+-----------+------------------------+---------------------------------------------------+
| id | estRows | task | access object | operator info |
+-----------------------------+---------+-----------+------------------------+---------------------------------------------------+
| MergeJoin_9 | 7992.00 | root | | semi join, left key:test.t.a, right key:test.t1.a |
| ├─IndexReader_25(Build) | 9990.00 | root | | index:IndexFullScan_24 |
| │ └─IndexFullScan_24 | 9990.00 | cop[tikv] | table:t1, index:idx(a) | keep order:true, stats:pseudo |
| └─IndexReader_23(Probe) | 9990.00 | root | | index:IndexFullScan_22 |
| └─IndexFullScan_22 | 9990.00 | cop[tikv] | table:t, index:idx(a) | keep order:true, stats:pseudo |
+-----------------------------+---------+-----------+------------------------+---------------------------------------------------+
-- Uses SEMI_JOIN_REWRITE() to rewrite the query.
EXPLAIN SELECT * FROM t WHERE EXISTS (SELECT /*+ SEMI_JOIN_REWRITE() */ 1 FROM t1 WHERE t1.a = t.a);
+------------------------------+---------+-----------+------------------------+---------------------------------------------------------------------------------------------------------------+
| id | estRows | task | access object | operator info |
+------------------------------+---------+-----------+------------------------+---------------------------------------------------------------------------------------------------------------+
| IndexJoin_16 | 1.25 | root | | inner join, inner:IndexReader_15, outer key:test.t1.a, inner key:test.t.a, equal cond:eq(test.t1.a, test.t.a) |
| ├─StreamAgg_39(Build) | 1.00 | root | | group by:test.t1.a, funcs:firstrow(test.t1.a)->test.t1.a |
| │ └─IndexReader_34 | 1.00 | root | | index:IndexFullScan_33 |
| │ └─IndexFullScan_33 | 1.00 | cop[tikv] | table:t1, index:idx(a) | keep order:true |
| └─IndexReader_15(Probe) | 1.25 | root | | index:Selection_14 |
| └─Selection_14 | 1.25 | cop[tikv] | | not(isnull(test.t.a)) |
| └─IndexRangeScan_13 | 1.25 | cop[tikv] | table:t, index:idx(a) | range: decided by [eq(test.t.a, test.t1.a)], keep order:false, stats:pseudo |
+------------------------------+---------+-----------+------------------------+---------------------------------------------------------------------------------------------------------------+
From the preceding example, you can see that when using the SEMI_JOIN_REWRITE()
hint, TiDB can select the execution method of IndexJoin based on the driving table t1
.
HASH_AGG()
The HASH_AGG()
hint tells the optimizer to use the hash aggregation algorithm in all the aggregate functions in the specified query block. This algorithm allows the query to be executed concurrently with multiple threads, which achieves a higher processing speed but consumes more memory. For example:
select /*+ HASH_AGG() */ count(*) from t1, t2 where t1.a > 10 group by t1.id;
STREAM_AGG()
The STREAM_AGG()
hint tells the optimizer to use the stream aggregation algorithm in all the aggregate functions in the specified query block. Generally, this algorithm consumes less memory but takes longer processing time. If there is a very large data volume or insufficient system memory, it is recommended to use this hint. For example:
select /*+ STREAM_AGG() */ count(*) from t1, t2 where t1.a > 10 group by t1.id;
USE_INDEX(t1_name, idx1_name [, idx2_name ...])
The USE_INDEX(t1_name, idx1_name [, idx2_name ...])
hint tells the optimizer to use only the given index(es) for a specified t1_name
table. For example, applying the following hint has the same effect as executing the select * from t t1 use index(idx1, idx2);
statement.
SELECT /*+ USE_INDEX(t1, idx1, idx2) */ * FROM t1;
If you specify only the table name but not index name in this hint, the execution does not consider any index but scan the entire table.
FORCE_INDEX(t1_name, idx1_name [, idx2_name ...])
The FORCE_INDEX(t1_name, idx1_name [, idx2_name ...])
hint tells the optimizer to use only the given index(es).
The usage and effect of FORCE_INDEX(t1_name, idx1_name [, idx2_name ...])
are the same as the usage and effect of USE_INDEX(t1_name, idx1_name [, idx2_name ...])
.
The following 4 queries have the same effect:
SELECT /*+ USE_INDEX(t, idx1) */ * FROM t;
SELECT /*+ FORCE_INDEX(t, idx1) */ * FROM t;
SELECT * FROM t use index(idx1);
SELECT * FROM t force index(idx1);
IGNORE_INDEX(t1_name, idx1_name [, idx2_name ...])
The IGNORE_INDEX(t1_name, idx1_name [, idx2_name ...])
hint tells the optimizer to ignore the given index(es) for a specified t1_name
table. For example, applying the following hint has the same effect as executing the select * from t t1 ignore index(idx1, idx2);
statement.
select /*+ IGNORE_INDEX(t1, idx1, idx2) */ * from t t1;
AGG_TO_COP()
The AGG_TO_COP()
hint tells the optimizer to push down the aggregate operation in the specified query block to the coprocessor. If the optimizer does not push down some aggregate function that is suitable for pushdown, then it is recommended to use this hint. For example:
select /*+ AGG_TO_COP() */ sum(t1.a) from t t1;
LIMIT_TO_COP()
The LIMIT_TO_COP()
hint tells the optimizer to push down the Limit
and TopN
operators in the specified query block to the coprocessor. If the optimizer does not perform such an operation, it is recommended to use this hint. For example:
SELECT /*+ LIMIT_TO_COP() */ * FROM t WHERE a = 1 AND b > 10 ORDER BY c LIMIT 1;
READ_FROM_STORAGE(TIFLASH[t1_name [, tl_name ...]], TIKV[t2_name [, tl_name ...]])
The READ_FROM_STORAGE(TIFLASH[t1_name [, tl_name ...]], TIKV[t2_name [, tl_name ...]])
hint tells the optimizer to read specific table(s) from specific storage engine(s). Currently, this hint supports two storage engine parameters - TIKV
and TIFLASH
. If a table has an alias, use the alias as the parameter of READ_FROM_STORAGE()
; if the table does not has an alias, use the table's original name as the parameter. For example:
select /*+ READ_FROM_STORAGE(TIFLASH[t1], TIKV[t2]) */ t1.a from t t1, t t2 where t1.a = t2.a;
If you want the optimizer to use a table from another schema, you need to explicitly specify the schema name. For example:
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 ...])
The USE_INDEX_MERGE(t1_name, idx1_name [, idx2_name ...])
hint tells the optimizer to access a specific table with the index merge method. The given list of indexes are optional parameters. If you explicitly specify the list, TiDB selects indexes from the list to build index merge; if you do not give the list of indexes, TiDB selects indexes from all available indexes to build index merge. For example:
SELECT /*+ USE_INDEX_MERGE(t1, idx_a, idx_b, idx_c) */ * FROM t1 WHERE t1.a > 10 OR t1.b > 10;
When multiple USE_INDEX_MERGE
hints are made to the same table, the optimizer tries to select the index from the union of the index sets specified by these hints.
The parameters of USE_INDEX_MERGE
refer to index names, rather than column names. The index name of the primary key is primary
.
This hint takes effect on strict conditions, including:
- If the query can select a single index scan in addition to full table scan, the optimizer does not select index merge.
LEADING(t1_name [, tl_name ...])
The LEADING(t1_name [, tl_name ...])
hint reminds the optimizer that, when generating the execution plan, to determine the order of multi-table joins according to the order of table names specified in the hint. For example:
SELECT /*+ LEADING(t1, t2) */ * FROM t1, t2, t3 WHERE t1.id = t2.id and t2.id = t3.id;
In the above query with multi-table joins, the order of joins is determined by the order of table names specified in the LEADING()
hint. The optimizer will first join t1
and t2
and then join the result with t3
. This hint is more general than STRAIGHT_JOIN
.
The LEADING
hint does not take effect in the following situations:
- Multiple
LEADING
hints are specified. - The table name specified in the
LEADING
hint does not exist. - A duplicated table name is specified in the
LEADING
hint. - The optimizer cannot perform join operations according to the order as specified by the
LEADING
hint. - The
straight_join()
hint already exists. - The query contains an outer join together with the Cartesian product.
- Any of the
MERGE_JOIN
,INL_JOIN
,INL_HASH_JOIN
, andHASH_JOIN
hints is used at the same time.
In the above situations, a warning is generated.
-- Multiple `LEADING` hints are specified.
SELECT /*+ LEADING(t1, t2) LEADING(t3) */ * FROM t1, t2, t3 WHERE t1.id = t2.id and t2.id = t3.id;
-- To learn why the `LEADING` hint fails to take effect, execute `show warnings`.
SHOW WARNINGS;
+---------+------+-------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+---------+------+-------------------------------------------------------------------------------------------------------------------+
| Warning | 1815 | We can only use one leading hint at most, when multiple leading hints are used, all leading hints will be invalid |
+---------+------+-------------------------------------------------------------------------------------------------------------------+
If the query statement includes an outer join, in the hint you can specify only the tables whose join order can be swapped. If there is a table in the hint whose join order cannot be swapped, the hint will be invalid. For example, in SELECT * FROM t1 LEFT JOIN (t2 JOIN t3 JOIN t4) ON t1.a = t2.a;
, if you want to control the join order of t2
, t3
, and t4
tables, you cannot specify t1
in the LEADING
hint.
MERGE()
Using the MERGE()
hint in queries with common table expressions (CTE) can disable the materialization of the subqueries and expand the subquery inlines into CTE. This hint is only applicable to non-recursive CTE. In some scenarios, using MERGE()
brings higher execution efficiency than the default behavior of allocating a temporary space. For example, pushing down query conditions or in nesting CTE queries:
-- Uses the hint to push down the predicate of the outer query.
WITH CTE AS (SELECT /*+ MERGE() */ * FROM tc WHERE tc.a < 60) SELECT * FROM CTE WHERE CTE.a < 18;
-- Uses the hint in a nested CTE query to expand a CTE inline into the outer query.
WITH CTE1 AS (SELECT * FROM t1), CTE2 AS (WITH CTE3 AS (SELECT /*+ MERGE() */ * FROM t2), CTE4 AS (SELECT * FROM t3) SELECT * FROM CTE3, CTE4) SELECT * FROM CTE1, CTE2;
MERGE()
is only applicable to simple CTE queries. It is not applicable in the following situations:
- Recursive CTE
- Subqueries with inlines that cannot be expanded, such as aggregate operators, window functions, and
DISTINCT
.
When the number of CTE references is too high, the query performance might be lower than the default materialization behavior.
Hints that take effect in the whole query
This category of hints can only follow behind the first SELECT
, UPDATE
or DELETE
keyword, which is equivalent to modifying the value of the specified system variable when this query is executed. The priority of the hint is higher than that of existing system variables.
This category of hints also has an optional hidden variable @QB_NAME
, but the hint takes effect in the whole query even if you specify the variable.
NO_INDEX_MERGE()
The NO_INDEX_MERGE()
hint disables the index merge feature of the optimizer.
For example, the following query will not use index merge:
select /*+ NO_INDEX_MERGE() */ * from t where t.a > 0 or t.b > 0;
In addition to this hint, setting the tidb_enable_index_merge
system variable also controls whether to enable this feature.
NO_INDEX_MERGE
has a higher priority overUSE_INDEX_MERGE
. When both hints are used,USE_INDEX_MERGE
does not take effect.- For a subquery,
NO_INDEX_MERGE
only takes effect when it is placed at the outermost level of the subquery.
USE_TOJA(boolean_value)
The boolean_value
parameter can be TRUE
or FALSE
. The USE_TOJA(TRUE)
hint enables the optimizer to convert an in
condition (containing a sub-query) to join and aggregation operations. Comparatively, the USE_TOJA(FALSE)
hint disables this feature.
For example, the following query will convert in (select t2.a from t2) subq
to corresponding join and aggregation operations:
select /*+ USE_TOJA(TRUE) */ t1.a, t1.b from t1 where t1.a in (select t2.a from t2) subq;
In addition to this hint, setting the tidb_opt_insubq_to_join_and_agg
system variable also controls whether to enable this feature.
MAX_EXECUTION_TIME(N)
The MAX_EXECUTION_TIME(N)
hint places a limit N
(a timeout value in milliseconds) on how long a statement is permitted to execute before the server terminates it. In the following hint, MAX_EXECUTION_TIME(1000)
means that the timeout is 1000 milliseconds (that is, 1 second):
select /*+ MAX_EXECUTION_TIME(1000) */ * from t1 inner join t2 where t1.id = t2.id;
In addition to this hint, the global.max_execution_time
system variable can also limit the execution time of a statement.
MEMORY_QUOTA(N)
The MEMORY_QUOTA(N)
hint places a limit N
(a threshold value in MB or GB) on how much memory a statement is permitted to use. When a statement's memory usage exceeds this limit, TiDB produces a log message based on the statement's over-limit behavior or just terminates it.
In the following hint, MEMORY_QUOTA(1024 MB)
means that the memory usage is limited to 1024 MB:
select /*+ MEMORY_QUOTA(1024 MB) */ * from t;
In addition to this hint, the tidb_mem_quota_query
system variable can also limit the memory usage of a statement.
READ_CONSISTENT_REPLICA()
The READ_CONSISTENT_REPLICA()
hint enables the feature of reading consistent data from the TiKV follower node. For example:
select /*+ READ_CONSISTENT_REPLICA() */ * from t;
In addition to this hint, setting the tidb_replica_read
environment variable to 'follower'
or 'leader'
also controls whether to enable this feature.
IGNORE_PLAN_CACHE()
The IGNORE_PLAN_CACHE()
hint reminds the optimizer not to use the Plan Cache when handling the current prepare
statement.
This hint is used to temporarily disable the Plan Cache for a certain type of queries when prepare-plan-cache is enabled.
In the following example, the Plan Cache is forcibly disabled when executing the prepare
statement.
prepare stmt from 'select /*+ IGNORE_PLAN_CACHE() */ * from t where t.id = ?';
STRAIGHT_JOIN()
The STRAIGHT_JOIN()
hint reminds the optimizer to join tables in the order of table names in the FROM
clause when generating the join plan.
SELECT /*+ STRAIGHT_JOIN() */ * FROM t t1, t t2 WHERE t1.a = t2.a;
STRAIGHT_JOIN
has higher priority overLEADING
. When both hints are used,LEADING
does not take effect.- It is recommended to use the
LEADING
hint, which is more general than theSTRAIGHT_JOIN
hint.
NTH_PLAN(N)
The NTH_PLAN(N)
hint reminds the optimizer to select the N
th physical plan found during the physical optimization. N
must be a positive integer.
If the specified N
is beyond the search range of the physical optimization, TiDB will return a warning and select the optimal physical plan based on the strategy that ignores this hint.
This hint does not take effect when the cascades planner is enabled.
In the following example, the optimizer is forced to select the third physical plan found during the physical optimization:
SELECT /*+ NTH_PLAN(3) */ count(*) from t where a > 5;
NTH_PLAN(N)
is mainly used for testing, and its compatibility is not guaranteed in later versions. Use this hint with caution.
- Syntax
- Hints that take effect in query blocks
- QB_NAME
- MERGE_JOIN(t1_name , tl_name ...)
- INL_JOIN(t1_name , tl_name ...)
- INL_HASH_JOIN
- HASH_JOIN(t1_name , tl_name ...)
- SEMI_JOIN_REWRITE()
- HASH_AGG()
- STREAM_AGG()
- USE_INDEX(t1_name, idx1_name , idx2_name ...)
- FORCE_INDEX(t1_name, idx1_name , idx2_name ...)
- IGNORE_INDEX(t1_name, idx1_name , idx2_name ...)
- AGG_TO_COP()
- LIMIT_TO_COP()
- READ_FROM_STORAGE(TIFLASH[t1_name , tl_name ...], TIKV[t2_name , tl_name ...])
- USE_INDEX_MERGE(t1_name, idx1_name , idx2_name ...)
- LEADING(t1_name , tl_name ...)
- MERGE()
- Hints that take effect in the whole query