Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

1. Index optimization

1. Single table index optimization

Build a table

CREATE TABLE IF NOT EXISTS article(
    id INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    author_id INT(10) UNSIGNED NOT NULL,
    category_id INT(10) UNSIGNED NOT NULL,
    views INT(10) UNSIGNED NOT NULL,
    comments INT(10) UNSIGNED NOT NULL,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL
);

INSERT INTO article(author_id,category_id,views,comments,title,content)
VALUES
(1,1,1,1,'1','1'),
(2,2,2,2,'2','2'),
(1,1,3,3,'3','3');

View Code

Enquiry case

  • Query the article_id with the most views when the category_id is 1 and the comments is greater than 1.
  • 查询语句:SELECT id, author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1;
  • 分析语句:EXPLAIN SELECT id, author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1;

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • Analysis: It can be seen that the above type is  ALL  , which proves that the index is missed, and  Using filesort uses file sorting  . Therefore, it can be optimized to use the index.

Recommended viewing: MySQL Introductory Video

Optimization one:

  • Create index  : CREATE INDEX idx_article_ccv ON article(category_id,comments,views);
  • View the current index: SHOW INDEX FROM article;

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • View the execution plan  : EXPLAIN SELECT id, author_id FROM article WHERE category_id = 1 AND comments> 1 ORDER BY views DESC LIMIT 1;

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • Analysis: (Drop index: DROP INDEX idx_article_ccv ON article;) Because according to the working principle of the B+Tree index, the category_id is sorted first, if the same category_id is encountered, the comments are sorted, and if the same comment is encountered, the views are sorted. The leftmost prefix matching principle,  when the comments field is in the middle position in the joint index, because the comments>1 condition is a range value (the so-called range), MySQL cannot use the index to retrieve the following views, that is, the range type query field The following index is invalid. If the condition comments> 1 is changed to comments = 1, then the current index is of type ref and there is no filesort. But not satisfied

Optimization 2:

  • Create index: create index idx_article_ccv on article(category_id, views);
  • View the current index: SHOW INDEX FROM article;

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • View the execution plan again   : EXPLAIN SELECT id, author_id FROM article WHERE category_id = 1 AND comments> 1 ORDER BY views DESC LIMIT 1;

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • It can be seen that the conditions are met at this time, for the convenience of subsequent tests, the current index is still deleted at this time: DROP INDEX idx_article_ccv ON article;

2. Two-table index optimization

Build a table

CREATE TABLE IF NOT EXISTS class(
    id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    card INT(10) UNSIGNED NOT NULL,
    PRIMARY KEY(id)
);

CREATE TABLE IF NOT EXISTS book(
    bookid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    card INT(10) UNSIGNED NOT NULL,
    PRIMARY KEY(bookid)
);

INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20)));

INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));

View Code

Enquiry case

  • To realize the connection of the two tables, the connection condition is class.card = book.card
  • 查询语句:SELECT * FROM class LEFT JOIN book ON class.card = book.card;
  • 分析语句:EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • Analysis: Type has ALL, rows is the total number of rows of data in the table, indicating that class and book have performed a full table search

Add right table index

  • CREATE INDEX idx_book_card ON book(card);
  • 分析语句:EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • Analysis: The conditions are met, so the current index is deleted to facilitate subsequent tests: DROP INDEX idx_book_card ON book; this is determined by the characteristics of the left connection. The LEFT JOIN condition is used to determine how to search for rows from the right table, and there must be all on the left, so the right is our key point, and an index must be established. If the left table is connected to the right table  , you need to take the data of the left table and check it in the right table. The index needs to be established in the  right table

Add left table index

  • CREATE INDEX idx_class_card ON class(card);
  • 分析语句:EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • Analysis: It can be seen from the above figure that the index does not play a big role because it is a left connection. When the left table drives the right side, because the  left table needs to be traversed anyway  , the rows are still the original ones. In fact, this does not have much effect.
  • Turn the left connection into the right connection  : EXPLAIN SELECT * FROM class RIGHT JOIN book ON class.card = book.card;

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • Analysis: It is convenient to meet the conditions, and the subsequent test deletes the current index: DROP INDEX idx_class_card ON class This is because the RIGHT JOIN condition is used to determine how to search for rows from the left table, and there must be all on the right, so the left is our key point, and an index must be established . class RIGHT JOIN book:  The data in the book must exist in the result set  , we need to hold the data in the book table, such as searching in the class table, so the index needs to be established in the class table

3. Three-table index optimization

Build a table

CREATE TABLE IF NOT EXISTS phone(
    phoneid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    card INT(10) UNSIGNED NOT NULL,
    PRIMARY KEY(phoneid)
)ENGINE=INNODB;

INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));

View Code

Query case:

  • 实现三表联查:SELECT * FROM class LEFT JOIN book ON class.card = book.card LEFT JOIN phone ON book.card = phone.card
  • 分析语句:EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card LEFT JOIN phone ON book.card = phone.card

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • Conclusion: Type has ALL, rows is the total number of rows of table data, indicating that the class, book, and phone tables have all been  retrieved using join buffer in Extra, indicating that the join buffer is used during the  join

Create index:

  • ALTER TABLE book ADD INDEX x (card); ALTER TABLE phone ADD INDEX y (card);
  • 分析语句:EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card LEFT JOIN phone ON book.card = phone.card

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • Analysis: For  LEFT JOIN, always create an index on the field of the right table

4. Summary

Think of left join as a two-level nested for loop

  1. As far as possible  to reduce the total number Join statement NestedLoop cycle  ;
  2. Always use  small result sets to drive large result sets  ( build indexes in  large result sets  , and traverse the entire table in small result sets);
  3. Prioritize optimization  of the inner loop of NestedLoop   ;
  4. Ensure that the Join condition field on the driven table in the Join statement  has been indexed  ;
  5. When there is no guarantee that the Join condition field of the driven table is indexed and the memory resources are sufficient, do not be too stingy with the setting of JoinBuffer;

Two, index failure

Create a table:

CREATE TABLE staffs(
    id INT PRIMARY KEY AUTO_INCREMENT,
    `name` VARCHAR(24)NOT NULL DEFAULT'' COMMENT'姓名',
    `age` INT NOT NULL DEFAULT 0 COMMENT'年龄',
    `pos` VARCHAR(20) NOT NULL DEFAULT'' COMMENT'职位',
    `add_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT'入职时间'
)CHARSET utf8 COMMENT'员工记录表';

INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('z3',22,'manager',NOW());
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('July',23,'dev',NOW());
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('2000',23,'dev',NOW());

View Code

1. Index invalidation criteria

  1. Full match my favorite
  2. The best left prefix rule  : If multiple cases are indexed, the leftmost prefix rule must be followed. It means that the  query starts from the leftmost front column of the index and does not skip the columns in the index  .
  3. Do not do any operation  (calculation, function, (automatic or manual) type conversion) on the index column , will cause the index to fail and turn to the full table scan
  4. Storage engine  can not use the index  in  the column to the right of the range of conditions
  5. Try to use the covering index (only access to the index query (  index column and query column  )),  reduce select *
  6. MySQL   cannot use the index when it is not equal to (!= or <>), which will result in a  full table scan  (not absolute, cost  issues need to be considered  , for example, the index will still be used when id!='')
  7. is null and is not null  also cannot use index (not absolute, cost should be considered   )
  8. Like starts with a wildcard ('%abc...') mysql index failure will become a full table scan operation (if it is'a%', the range index is used)
  9. String unquoted index is invalid
  10. Use or less  , the index will fail when you use it to connect

2. Index failure case

a) Create a composite index

#创建复合索引
ALTER TABLE staffs ADD INDEX index_staffs_nameAgePos(`name`,`age`,`pos`);
#查看索引
SHOW INDEX FROM staffs;

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

b) where conditions match

  • When the order matches:

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • When  there is no leftmost index  (name): you can see that the index is invalid at this time

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • When the  intermediate index does not exist  (age): it can be seen that the valid index has only one const at this time, that is: only the name is valid

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • When  calculating  or using functions on the index column, the index will become invalid: use left(name,4)='July'

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • When the range index is used   , the subsequent index will be invalid: the name condition remains unchanged, and the age is changed from the original equal to greater than, and the index type is changed from ref -> range

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • Try to  use the covering index  (only access to the index query (  index column and query column  )), reduce select *

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • Use the position of% in like to determine whether to use the index (if% is on the left or there are indexes on the left and right, it will be invalid, if% is only valid on the right)

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • If you really  need to put% on the left, you  can use  covering index optimization  (only query the required columns and hit the index)

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • The index of the string without single quotes is invalid  : name=2000 and name='2000'

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • Use or less  , the index will fail when you use it to connect

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • MySQL   cannot use the index when it is not equal to (!= or <>), which will result in a full table scan (of course it is not absolute, there is a cost of receipt)

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

  • is null, is not null will cause the index to fail: key = null means the index is invalid (not absolute, cost issues will be considered)

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

Note that in in, !=, is null and is not null, when should the index be used and when should the full table scan be used? For details, please click to view

成本。对于使用二级索引(innodb)进行查询来说,成本组成主要有两个方面:
    读取二级索引记录的成本
    将二级索引记录执行回表操作,也就是到聚簇索引中找到完整的用户记录的操作所付出的成本。
很显然,要扫描的二级索引记录条数越多,那么需要执行的回表操作的次数也就越多,达到了某个比例时,使用二级索引执行查询的成本也就超过了全表扫描的成本
(举一个极端的例子,比方说要扫描的全部的二级索引记录,那就要对每条记录执行一遍回表操作,自然不如直接扫描聚簇索引来的快)。
所以MySQL优化器在真正执行查询之前,对于每个可能使用到的索引来说,都会预先计算一下需要扫描的二级索引记录的数量。所以对于以上三种查询条件是否会命中索引就取决于二级索引查询的成本与全局查询成本的高低。

Three, index case

1. Build a table

create table test03(
    id int primary key not null auto_increment,
    c1 char(10),
    c2 char(10),
    c3 char(10),
    c4 char(10),
    c5 char(10)
);

insert into test03(c1,c2,c3,c4,c5) values ('a1','a2','a3','a4','a5');
insert into test03(c1,c2,c3,c4,c5) values ('b1','b2','b3','b4','b5');
insert into test03(c1,c2,c3,c4,c5) values ('c1','c2','c3','c4','c5');
insert into test03(c1,c2,c3,c4,c5) values ('d1','d2','d3','d4','d5');
insert into test03(c1,c2,c3,c4,c5) values ('e1','e2','e3','e4','e5');
#创建复合索引
create index idx_test03_c1234 on test03(c1,c2,c3,c4);

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

2. Case

#只有where
EXPLAIN SELECT * FROM test03 WHERE c1='a1' AND c2='a2' AND c3='a3' AND c4='a4';
EXPLAIN SELECT * FROM test03 WHERE c4='a4' AND c3='a3' AND c2='a2' AND c1='a1';
EXPLAIN SELECT * FROM test03 WHERE c1='a1' AND c2='a2' AND c3>'a3' AND c4='a4';
EXPLAIN SELECT * FROM test03 WHERE c1='a1' AND c2='a2' AND c4>'a4' AND c3='a3';
#where条件与order by 结合
EXPLAIN SELECT * FROM test03 WHERE c1='a1' AND c2='a2' AND c4='a4' ORDER BY c3;
EXPLAIN SELECT * FROM test03 WHERE c1='a1' AND c2='a2' ORDER BY c3;
EXPLAIN SELECT * FROM test03 WHERE c1='a1' AND c2='a2' ORDER BY c4;
EXPLAIN SELECT * FROM test03 WHERE c1='a1' AND c5='a5' ORDER BY c2, c3;
EXPLAIN SELECT * FROM test03 WHERE c1='a1' AND c5='a5' ORDER BY c3, c2;
EXPLAIN SELECT * FROM test03 WHERE c1='a1' AND c2='a2' ORDER BY c2, c3;
EXPLAIN SELECT * FROM test03 WHERE c1='a1' AND c2='a2' AND c5='a5' ORDER BY c2, c3;
EXPLAIN SELECT * FROM test03 WHERE c1='a1' AND c2='a2' AND c5='a5' ORDER BY c3, c2;
#where与group by结合
EXPLAIN SELECT * FROM test03 WHERE c1='a1' AND c4='a4' GROUP BY c2, c3;
EXPLAIN SELECT * FROM test03 WHERE c1='a1' AND c4='a4' GROUP BY c3, c2;

3. Case study

a) Where query

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

b) Combination of where and order by

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

c)where与group by

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

Conclusion:  Group by basically needs to be sorted  (the usage is basically the same as order by, and the index order will appear after where),  but if it is used improperly, a temporary table will be generated  .

4. Summary of index failure

a) Suggestion

  1. For single-key indexes, try to choose an index with better filtering performance for the current query
  2. When selecting a  composite index  , the field with the best filtering ability in the current query is   in the index field order, and the position is as far to  the left as possible  .
  3. When choosing a  composite index  , try to choose an index that may contain more fields in the where clause in the current query query condition 
  4. Try to achieve the purpose of selecting a suitable index by analyzing statistical information and adjusting the wording of the query

b) Case

Case plus source code: a ten-thousand-word long text takes you to thoroughly understand MySQL index optimization

 

Original link: http://www.cnblogs.com/bbgs-xc/p/14287432.html

If you think this article is helpful to you, you can follow my official account and reply to the keyword [Interview] to get a compilation of Java core knowledge points and an interview gift package! There are more technical dry goods articles and related materials to share, let everyone learn and progress together!

Guess you like

Origin blog.csdn.net/weixin_48182198/article/details/112771702
Recommended