MySQL Advanced - 11 Situations of Index Failure

navigation:

[Dark Horse Java Notes + Stepping on the Pit Summary] Java Basics + Advanced + JavaWeb + SSM + SpringBoot + St. Regis Takeaway + Spring Cloud + Dark Horse Travel + Grain Mall + Xuecheng Online + Design Mode + Niuke Interview Questions

Table of contents

1. Index optimization ideas

2. 11 cases of index failure

2.0. Data preparation

2.1 Try to meet the full value matching

2.2 To satisfy the best left prefix rule

2.3 The insertion order of the primary key should be auto-incremented as much as possible

2.4 Calculations and functions lead to index failure

2.5 Index invalidation due to type conversion

2.6 The column index on the right side of the range condition is invalid

2.7 When the index is not covered, "not equal to" causes the index to fail

2.8 When the index is not covered, is not null and not like cause the index to fail

2.9 When the index is not covered, the left fuzzy query causes the index to fail

2.10 There are non-indexed columns before and after "OR", causing the index to fail

2.11 Different character sets lead to index failure, recommend utf8mb4

2.12 Summary

3. Self-assessment exercise


1. Index optimization ideas

What dimensions can be used for database tuning? In short:

  • Index failure, underutilization of the index - indexing

  • There are too many JOINs in associated queries (design flaws or unavoidable requirements)—— SQL optimization

  • Server tuning and various parameter settings (close slow query logs, buffering, number of threads, etc.) - adjust my.cnf

  • Too much data - sub-database sub-table

  • Regularly clean up garbage: For tables, data, logs, caches, etc. that are no longer used, they should be cleaned up in time to avoid occupying too many MySQL resources, thereby improving MySQL performance.

  • Use an appropriate storage engine: MyISAM is more suitable for scenarios with frequent reads and less writes (because of table-level locks, B+ leaf storage addresses), while InnoDB is more suitable for concurrent write scenarios (because of row-level locks, B+ leaf storage records ).

Knowledge about database tuning is very scattered. Different DBMSs, different companies, different positions, and different projects encounter different problems. Here we divide it into three chapters for detailed explanation.

Although there are many technologies for SQL query optimization, they can be divided into two parts: physical query optimization and logical query optimization .

  • Physical query optimization : technologies such as index and table connection methods are used for optimization. The focus here is to master the use of indexes.

  • Logical query optimization : Improve query efficiency through SQL equivalent transformation . To put it bluntly, a different query writing method may be more efficient.

2. 11 cases of index failure

Whether to use an index or not depends on the optimizer in the end:

What is the optimizer based on? Based on cost (CostBaseOptimizer) , it is not based on rules (Rule-Basedoptimizer), nor is it based on semantics. No matter how little the cost is, you can get it. In addition, whether an SQL statement uses an index is related to the database version, data volume, and data selectivity.

2.0. Data preparation

Insert 500,000 entries in the student list** , and insert 10,000 entries in the class list .

CREATE DATABASE atguigudb2;
USE atguigudb2;

Step 1: Create a table

CREATE TABLE `class` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `className` VARCHAR(30) DEFAULT NULL,
    `address` VARCHAR(40) DEFAULT NULL,
    `monitor` INT NULL ,
    PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

CREATE TABLE `student` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `stuno` INT NOT NULL ,
    `name` VARCHAR(20) DEFAULT NULL,
    `age` INT(3) DEFAULT NULL,
    `classId` INT(11) DEFAULT NULL,
    PRIMARY KEY (`id`)
    #CONSTRAINT `fk_class_id` FOREIGN KEY (`classId`) REFERENCES `t_class` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

Step 2: Setting parameters

  • command on: allow creation of function settings:
set global log_bin_trust_function_creators=1; # 不加global只是当前窗口有效。

Step 3: Create the function

Make sure that each piece of data is different.

DELIMITER //

CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
DETERMINISTIC
NO SQL
BEGIN
    DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
    DECLARE return_str VARCHAR(255) DEFAULT '';
    DECLARE i INT DEFAULT 0;
    WHILE i < n DO
        SET return_str = CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
        SET i = i + 1;
    END WHILE;
    RETURN return_str;
END //

DELIMITER ;

random class number

DELIMITER //

CREATE FUNCTION rand_num (from_num INT ,to_num INT) RETURNS INT(11)
DETERMINISTIC
NO SQL
BEGIN
    DECLARE i INT DEFAULT 0;
    SET i = FLOOR(from_num +RAND()*(to_num - from_num+1)) ;
    RETURN i;
END //

DELIMITER ;

Step 4: Create a stored procedure

#创建往stu表中插入数据的存储过程
DELIMITER //
CREATE PROCEDURE insert_stu( START INT , max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0; #设置手动提交事务
REPEAT #循环
SET i = i + 1; #赋值
INSERT INTO student (stuno, name ,age ,classId ) VALUES
((START+i),rand_string(6),rand_num(1,50),rand_num(1,1000));
UNTIL i = max_num
END REPEAT;
COMMIT; #提交事务
END //
DELIMITER ;
#假如要删除
#drop PROCEDURE insert_stu;

Create a stored procedure to insert data into the class table

#执行存储过程,往class表添加随机数据
DELIMITER //
CREATE PROCEDURE `insert_class`( max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO class ( classname,address,monitor ) VALUES
(rand_string(8),rand_string(10),rand_num(1,100000));
UNTIL i = max_num
END REPEAT;
COMMIT;
END //
DELIMITER ;
#假如要删除
#drop PROCEDURE insert_class;

Step 5: Call the stored procedure

class

#执行存储过程,往class表添加1万条数据
CALL insert_class(10000);

stu

#执行存储过程,往stu表添加50万条数据
CALL insert_stu(100000,500000);

Step 6: Drop an index on a table

create stored procedure

DELIMITER //
CREATE PROCEDURE `proc_drop_index`(dbname VARCHAR(200),tablename VARCHAR(200))
BEGIN
        DECLARE done INT DEFAULT 0;
        DECLARE ct INT DEFAULT 0;
        DECLARE _index VARCHAR(200) DEFAULT '';
        DECLARE _cur CURSOR FOR SELECT index_name FROM
information_schema.STATISTICS WHERE table_schema=dbname AND table_name=tablename AND
seq_in_index=1 AND index_name <>'PRIMARY' ;
#每个游标必须使用不同的declare continue handler for not found set done=1来控制游标的结束
        DECLARE CONTINUE HANDLER FOR NOT FOUND set done=2 ;
#若没有数据返回,程序继续,并将变量done设为2
        OPEN _cur;
        FETCH _cur INTO _index;
        WHILE _index<>'' DO
            SET @str = CONCAT("drop index " , _index , " on " , tablename );
            PREPARE sql_str FROM @str ;
            EXECUTE sql_str;
            DEALLOCATE PREPARE sql_str;
            SET _index='';
            FETCH _cur INTO _index;
        END WHILE;
    CLOSE _cur;
END //
DELIMITER ;

execute stored procedure

CALL proc_drop_index("dbname","tablename");

2.1 Try to meet the full value matching

When querying age and classId and name, the (age,classId,name) index is faster than (age,classId).

The query order is not important. For example, querying age and classId and name is the same as querying classId, name and age.

Analyzing the query plan:

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4 AND name = 'abcd';

It was found that the index was not taken:

Create an age index

CREATE INDEX idx_age ON student(age);

Analyze the query plan again and find that the index is taken:

 

Create (age, classId) index

CREATE INDEX idx_age_classid ON student(age,classId);

Analyze the query plan again and find that the index is taken:

 

Build (age, classId, name) index

CREATE INDEX idx_age_classid_name ON student(age,classId,name);

Analyze the query plan again and find that the index is taken:

2.2 To satisfy the best left prefix rule

When MySQL builds a joint index, it follows the principle of the best left prefix, that is, the leftmost first, and matches from the leftmost side of the joint index when retrieving data.

For example, index (a,b,c), only queries (a), (a,b), (a,b,c) will go to the index, but (b), (b,c), (c) will not Go index.

Example 1:

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age=30 AND student.name = 'abcd';

Example 2:

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.classId=1 AND student.name = 'abcd';

Example 3: Can the index idx_age_classid_name still be used normally?

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.classId=4 AND student.age=30 AND student.name = 'abcd';

If multiple columns are indexed, the leftmost prefix rule must be followed. Refers to the query starting from the leftmost front column of the index and not skipping columns in the index.

mysql> EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age=30 AND student.name = 'abcd';

Although it can be used normally, only part of it is used.

mysql> EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.classId=1 AND student.name = 'abcd';

Indexes are not used at all.

Conclusion: MySQL can create indexes for multiple fields, and an index can contain 16 fields. For a multi-column index, the filter conditions to use the index must be satisfied in the order in which the index was created. Once a field is skipped, the fields behind the index cannot be used . If the first of these fields is not used in the query condition, the multi-column (or union) index will not be used.

2.3 The insertion order of the primary key should be auto-incremented as much as possible

For a table using the InnoDB storage engine, when we do not explicitly create an index, the data in the table is actually stored in the leaf nodes of the clustered index. The records are stored in the data page, and the data pages and records are sorted according to the order of the primary key value of the record from small to large. So if the primary key values ​​​​of the records we insert increase in order, then every time we insert One data page is switched to the next data page to continue inserting, and if the primary key value we insert is suddenly large or small, it will be more troublesome. Assuming that the records stored in a data page are full, the primary key value it stores Between 1~100:

If you insert a record with a primary key value of 9 at this time, its inserted position is as shown in the figure below:

But this data page is already full, what should I do if I insert it again? We need to split the current page into two pages, and move some records in this page to the newly created page. What does page splitting and record shifting mean? Means: performance loss ! So if we want to avoid such unnecessary performance loss as much as possible, it is best to increase the primary key value of the inserted record in turn , so that such performance loss will not occur. So we suggest: let the primary key have AUTO_INCREMENT , let the storage engine generate the primary key for the table itself, instead of us inserting it manually, for example: person_info table:

CREATE TABLE person_info(
    id INT UNSIGNED NOT NULL AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    birthday DATE NOT NULL,
    phone_number CHAR(11) NOT NULL,
    country varchar(100) NOT NULL,
    PRIMARY KEY (id),
    KEY idx_name_birthday_phone_number (name(10), birthday, phone_number)
);

Our custom primary key column id has the AUTO_INCREMENT attribute, and the storage engine will automatically fill in the auto-incremented primary key value for us when inserting records. Such a primary key takes up less space and is written sequentially, reducing page splits.

2.4 Calculations and functions lead to index failure

In the following sql, the first one is indexed, and the second one is not indexed:

CREATE INDEX idx_name ON student(NAME);
#1.函数导致索引失效,没有走索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE LEFT(student.name,3) = 'abc';
#索引优化成like,走索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name LIKE 'abc%';

#2.计算导致索引失效
CREATE INDEX idx_sno ON student(stuno);
EXPLAIN SELECT SQL_NO_CACHE id, stuno, NAME FROM student WHERE stuno+1 = 900001;

Drop other indexes before testing  

2.5 Index invalidation due to type conversion

CREATE INDEX idx_name ON student(NAME);

#1.手动类型转换,通过调用函数,导致索引失效
EXPLAIN SELECT id, stuno, name FROM student WHERE name=CAST(123 as CHAR);

#2.自动类型转换导致索引失效。name字段类型是varchar,你赋值成数字它会默认转成字符串导致索引失败
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE name=123;
# 索引优化成目标字符串,走索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE name='123';

Drop other indexes before testing  

2.6 The column index on the right side of the range condition is invalid

For example (a, b, c) joint index, query conditions a, b, c, if b uses a range query, then the c index on the right of b is invalid. Here the right side looks at the keys of the union index to the right.

Solution: Create a new joint index (a,c,b) or (c,a,b), and put the fields that need range query at the end

Ranges include: (<) (<=) (>) (>=) and between.

Note: The fuzzy query "like" is not a range query.

case:  

CREATE INDEX idx_age_classid_name ON student(age,classId,name);

#都不使用范围查询。key_len为73
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age=30 AND student.classId=4 AND student.name = 'abcd';

#联合索引第一个参数age使用范围查询,全部索引失效。ken_len为0,全表扫描。
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age>30 AND student.classId=4 AND student.name = 'abcd';


#联合索引第二个参数classId使用范围查询,name索引失效。key_len为10,即只用age和classId。
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age=30 AND student.classId>3 AND student.name = 'abcd';

#联合索引第三个参数name使用范围查询,索引都生效。key_len为73
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age=30 AND student.classId=4 AND student.name > 'abcd';

Range queries in application development, such as: amount queries, date queries are often range queries. In the created joint index, be sure to write the fields involved in the range at the end, and then put the range query condition at the end of the where statement.

2.7 When the index is not covered, "not equal to" causes the index to fail

Because "not equal to" cannot be accurately matched, the efficiency of full table scanning the secondary index tree and then returning to the table is not as good as direct full table scanning of the clustered index tree. However, when using a covering index, the data volume of the joint index is small, and the space required to load it into the memory is smaller than that of the clustered index tree, and it does not need to be returned to the table. The indexing efficiency is better than that of the full table scan clustered index tree. Covering index: An index that contains data that satisfies the query results is called a covering index, and does not require operations such as returning to the table. 

Not equal to symbols: != or <>, these two symbols have the same meaning.

In the absence of a covering index, using "not equal to" causes the index to fail. Because if you use an index, you need to traverse all the leaf nodes in the non-clustered index B+ tree in turn, the time complexity is O(n), and you have to return to the table after finding the record. The efficiency is not as good as the full table scan, so the query optimizer Choose a full table scan.

case: 

 In the absence of a covering index, using "not equal to" causes the index to fail. Because if you use an index, you need to traverse all the leaf nodes in the non-clustered index B+ tree in turn, the time complexity is O(n), and you have to return to the table after finding the record. The efficiency is not as good as the full table scan, so the query optimizer Choose a full table scan.

CREATE INDEX idx_age_name ON student(age, NAME);
#查所有字段,并且使用“不等于”,索引失效
EXPLAIN SELECT * FROM student WHERE age <> 20;

Covering index, the two fields to be checked are covered by the joint index, and the performance is higher. Although it is still necessary to traverse all the leaf nodes in the non-clustered index B+ tree in turn, the time complexity is O(n), but there is no need to return to the table, the overall efficiency is higher than without the index, and the query optimizer uses the index again.

CREATE INDEX idx_age_name ON student(age, NAME);
#查的两个字段正好被联合索引“idx_age_name ”覆盖了,索引成功
EXPLAIN SELECT age,name FROM student WHERE age <> 20;

Case 2:

  • Create an index for the name field
CREATE INDEX idx_name ON student(NAME);
  • Check if the index is invalid
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name <> 'abc';

or

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name != 'abc';

Scenario example: The user puts forward the demand, and the financial data and the product profit amount are not equal to 0 are counted.

2.8 When the index is not covered, is not null and not like cause the index to fail

Because is not null and not like cannot be accurately matched, the efficiency of full table scanning the secondary index tree and then returning to the table is not as good as direct full table scanning of the clustered index tree. However, when using a covering index, the data volume of the joint index is small, and the space required to load it into the memory is smaller than that of the clustered index tree, and it does not need to be returned to the table. The indexing efficiency is better than that of the full table scan clustered index tree. Covering index: An index that contains data that satisfies the query results is called a covering index, and does not require operations such as returning to the table. 

is null can use index, is not null cannot use index 

reason:

When performing an index scan, MySQL will give priority to using the values ​​that already exist in the index for query, and directly skip those rows that are NULL when querying. However, if the IS NOT NULL condition is used, MySQL cannot find NULL values ​​in the index, that is, MySQL cannot skip rows that are NULL as before, and can only scan the entire table to find rows that meet the conditions. So the index cannot be used.

solution:

  • When designing the database, set the fields to NOT NULL constraints
  • For fields of type INT, the default value is set to 0.
  • Sets the default value for character types to the empty string ('').

Extension: Similarly, using not like in the query will also fail to use the index, resulting in a full table scan. 

  • IS NULL: indexing can be triggered
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age IS NULL;
  • IS NOT NULL: Failed to trigger indexing.
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age IS NOT NULL;

2.9 When the index is not covered, the left fuzzy query causes the index to fail

Because the beginning of the string cannot be accurately matched, the efficiency of full table scanning the secondary index tree and then returning to the table is not as good as direct full table scanning of the clustered index tree. However, when using a covering index, the data volume of the joint index is small, and the space required to load it into the memory is smaller than that of the clustered index tree, and it does not need to be returned to the table. The indexing efficiency is better than that of the full table scan clustered index tree.

Covering index: An index that contains data that satisfies the query results is called a covering index, and does not require operations such as returning to the table.

If the index is not covered, like starts with a wildcard character "%" or "_", causing the index to fail. In a query statement that uses the LIKE keyword to query, if the first character of the matching string is '%', the index will not work. The index will only work if the '%' is not in the first position.

Reason: In the case of no covering index, the beginning of the string does not know what it is, and the efficiency of going to the index and then returning to the table is not as good as direct full table scanning

Alibaba "Java Development Manual": [Mandatory] Left blur or full blur is strictly prohibited in page search . If necessary, please use the search engine to solve it.

 In the case of no covering index, the left fuzzy query causes the index to fail

#没覆盖索引的情况下,左模糊查询导致索引失效
CREATE INDEX idx_age_name ON student(age, NAME);
EXPLAIN SELECT * FROM student WHERE NAME LIKE '%abc';

In the case of a covering index, the left fuzzy query index takes effect

The main reason is also because the non-clustered index B+ tree traverses the leaf nodes without returning to the table, the efficiency will be higher than that of the full table scan, and the query optimizer chooses a high-efficiency solution.

#有覆盖索引的情况下,左模糊查询索引生效
CREATE INDEX idx_age_name ON student(age, NAME);
EXPLAIN SELECT id,age,NAME FROM student WHERE NAME LIKE '%abc';

  • use to index
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE name LIKE 'ab%';

  • index not used
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE name LIKE '%ab%';

2.10 There are non-indexed columns before and after "OR", causing the index to fail

In MySQL, even if the condition on the left of or is satisfied, the condition on the right still needs to be judged. 

In the WHERE clause, if the condition column before the OR is indexed, but the condition column after the OR is not indexed, the index will fail.

index_merge: When the columns in the two conditions before and after OR are both indexes, the index is used in the query. The index type is "index_merge". The two index fields are scanned separately and then merged.

Because the meaning of OR is that only one of the two conditions is satisfied, it is meaningless to have only one conditional column indexed . As long as there is no conditional column indexed, the full table scan will be performed , so all conditional columns will also be invalid.

When the query statement uses the OR keyword:

# 未使用到索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 10 OR classid = 100;

Because there is no index on the classId field, the above query does not use an index.

#使用到索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 10 OR name = 'Abel';

Because there are indexes on the age field and the name field, the index is used in the query. You can see that index_merge is used here. Simply put, index_merge scans age and name separately, and then merges the two result sets. The advantage of this is to avoid a full table scan.

2.11 Different character sets lead to index failure, recommend utf8mb4

Different character sets need to be converted before comparison , which will invalidate the index.

The character set of the database and the table uses utf8mb4 uniformly. Unified use of utf8mb4 (supported by version 5.5.3 and above) has better compatibility, and the unified character set can avoid garbled characters caused by character set conversion .

2.12 Summary

Try to match all values: when querying age and classId and name, the (age, classId, name) index is faster than (age, classId).

Consider the leftmost prefix: the joint index puts frequently queried columns on the left. Index (a, b, c), can only search (a, b, c), (a, b), (a).

The primary key should be auto-incremented as much as possible: If the primary key does not auto-increment, it is necessary to find the target location and then insert it, and if the data page where the target location is located is full, it must be divided into pages, resulting in performance loss.

Calculations and functions lead to index failure: calculations such as where num+1=2, functions such as abs(num) take the absolute value

Type conversion invalidates the index: eg name=123 instead of name='123'. Another example is the use of different character sets.

The column index on the right side of the range condition is invalid: for example (a, b, c) joint index, query conditions a, b, c, if b uses range query, then the c index on the right side of b is invalid. It is recommended to put the fields that require range query at the end. Ranges include: (<) (<=) (>) (>=) and between.

When the index is not covered, "not equal to" causes the index to fail: because "not equal to" cannot be accurately matched, the efficiency of full table scanning the secondary index tree and returning to the table is not as good as direct full table scanning clustered index tree. However, when using a covering index, the data volume of the joint index is small, and the space required to load it into the memory is smaller than that of the clustered index tree, and it does not need to be returned to the table. The indexing efficiency is better than that of the full table scan clustered index tree. Covering index: An index that contains data that satisfies the query results is called a covering index, and does not require operations such as returning to the table.

When the index is not covered, the left fuzzy query causes the index to fail: for example, LIKE '%abc'. Because the beginning of the string cannot be matched exactly. Same reason as above.

When the index is not covered, is not null and not like cannot use the index: because they cannot be matched accurately. Same reason as above.

There are non-indexed columns before and after "OR", causing the index to fail: In MySQL, even if the condition on the left side of or is satisfied, the condition on the right side still needs to be judged.

Different character sets lead to index failure: utf8mb4 is recommended. Different character sets need to be converted before comparison, which will cause index failure.

3. Self-assessment exercise

Exercise: Assumption: index(a,b,c)

Where statement whether the index is used Which columns are used
where a = 3 Y a
where a = 3 and b =5 Y a, b
where a = 3 and b = 5 and c =4 Y a, b, c
where b=3 N
where b=3 and c= 4 Y b
where c = 4 Y c
where a = 3 and c =5 Y a, c
where a = 3 and b> 4 and c= 5 Y a, b, c
where a is null and b is not null Y(isnull) a, b
where a <>3 N
where abs(a)=3 N
3 and b like 'kk%' and where a = c=4 Y a, b, c
where a = 3 and b like kk' and c = 4 Y a, b, c
where a = 3 and b like %kk%' and c= 4 Y a, b, c
where a = 3 and b like 'k%kk% Y a, b, c

general advice

  • For a single-column index, try to choose an index with better filterability for the current query
  • When selecting a combined index, the field with the best filterability in the current query is in the order of the index fields, and the higher the position, the better.
  • When choosing a composite index, try to choose more indexes that can be included in the where clause in the current query.
  • When selecting a composite index, if a field may have a range query, try to put this field at the end of the index order.

In short, when writing SQL statements, try to avoid causing index failure

Guess you like

Origin blog.csdn.net/qq_40991313/article/details/130779528