MySQL - 索引优化案例实操
文章目錄
- 生猛干貨
- DB Version
- Table
- Case 1 : 聯(lián)合索引第一個字段用范圍不一定會走索引
- 優(yōu)化一 強制走索引 force index(idx_name_age_position)
- 優(yōu)化二 覆蓋索引優(yōu)化
- Case 2 : in和or在表數(shù)據(jù)量比較大的情況會走索引,在表記錄不多的情況下會選擇全表掃描
- Case 3 : like KK% 一般情況都會走索引
- 特殊例子
- 搞定MySQL
生猛干貨
帶你搞定MySQL實戰(zhàn),輕松對應海量業(yè)務處理及高并發(fā)需求,從容應對大場面試
DB Version
mysql> select version(); +------------+ | version() | +------------+ | 5.7.29-log | +------------+ 1 row in set默認隔離級別 RR 可重復讀
Table
CREATE TABLE `employees` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',`age` int(11) NOT NULL DEFAULT '0' COMMENT '年齡',`position` varchar(20) NOT NULL DEFAULT '' COMMENT '職位',`hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入職時間',PRIMARY KEY (`id`),KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='員工記錄表';重點
我們向表里寫入10萬來條數(shù)據(jù)
Case 1 : 聯(lián)合索引第一個字段用范圍不一定會走索引
mysql> EXPLAIN SELECT * FROM employees WHERE name > 'LiLei' AND age = 22 AND position ='manager'; +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | employees | NULL | range | idx_name_age_position | idx_name_age_position | 74 | NULL | 1 | 5 | Using index condition | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ 1 row in setmysql> EXPLAIN SELECT * FROM employees WHERE name > 'Artisan' AND age = 22 AND position ='manager'; +----+-------------+-----------+------------+------+-----------------------+------+---------+------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+-----------------------+------+---------+------+--------+----------+-------------+ | 1 | SIMPLE | employees | NULL | ALL | idx_name_age_position | NULL | NULL | NULL | 100175 | 0.5 | Using where | +----+-------------+-----------+------------+------+-----------------------+------+---------+------+--------+----------+-------------+ 1 row in setmysql>當然了,也不是所有的情況都不走索引, MySQL會基于Cost選擇一個合適的 ,如果沒有走索引,可能mysql內部可能覺得第一個字段就用范圍,結果集應該很大,回表效率不高,還不如就全表掃描
如果沒有走索引想要去優(yōu)化的話怎么辦呢?
優(yōu)化一 強制走索引 force index(idx_name_age_position)
mysql> EXPLAIN SELECT * FROM employees force index(idx_name_age_position) WHERE name > 'Artisan' AND age = 22 AND position ='manager'; +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+-------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+-------+----------+-----------------------+ | 1 | SIMPLE | employees | NULL | range | idx_name_age_position | idx_name_age_position | 74 | NULL | 50087 | 1 | Using index condition | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+-------+----------+-----------------------+ 1 row in set優(yōu)化二 覆蓋索引優(yōu)化
mysql> EXPLAIN SELECT name , age , position FROM employees WHERE name > 'Artisan' AND age = 22 AND position ='manager'; +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+-------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+-------+----------+--------------------------+ | 1 | SIMPLE | employees | NULL | range | idx_name_age_position | idx_name_age_position | 74 | NULL | 50087 | 1 | Using where; Using index | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+-------+----------+--------------------------+ 1 row in setmysql>name , age , position 是聯(lián)合索引,在索引樹上,同時索引樹上的葉子節(jié)點還會關聯(lián)一個主鍵id , 如果查詢 * 的話,還要根據(jù)id去主鍵索引上去查找其他字段,需要回表, 如果僅查詢二級索引樹idx_name_age_position上的字段,那就無需回表操作了,效率自然高一些。
Case 2 : in和or在表數(shù)據(jù)量比較大的情況會走索引,在表記錄不多的情況下會選擇全表掃描
mysql> EXPLAIN SELECT * FROM employees WHERE name in ('LiLei','HanMeimei','Lucy') AND age = 22 AND position ='manager'; +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | employees | NULL | range | idx_name_age_position | idx_name_age_position | 140 | NULL | 3 | 100 | Using index condition | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ 1 row in setmysql> EXPLAIN SELECT * FROM employees WHERE (name = 'LiLei' or name = 'HanMeimei') AND age = 22 AND position ='manager'; +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | employees | NULL | range | idx_name_age_position | idx_name_age_position | 140 | NULL | 2 | 100 | Using index condition | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ 1 row in set再搞個小表 ,和 employees 一模一樣哈,連索引也得一樣,插入3條數(shù)據(jù) 。
CREATE TABLE `employees_2` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',`age` int(11) NOT NULL DEFAULT '0' COMMENT '年齡',`position` varchar(20) NOT NULL DEFAULT '' COMMENT '職位',`hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入職時間',PRIMARY KEY (`id`),KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='員工記錄表';INSERT INTO employees_2(name,age,position,hire_time) VALUES('LiLei',22,'manager',NOW()); INSERT INTO employees_2(name,age,position,hire_time) VALUES('HanMeimei', 23,'dev',NOW()); INSERT INTO employees_2(name,age,position,hire_time) VALUES('Lucy',23,'dev',NOW()); mysql> EXPLAIN SELECT * FROM employees_2 WHERE name in ('LiLei','HanMeimei','Lucy') AND age = 22 AND position ='manager'; +----+-------------+-------------+------------+------+-----------------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------------+------------+------+-----------------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | employees_2 | NULL | ALL | idx_name_age_position | NULL | NULL | NULL | 3 | 100 | Using where | +----+-------------+-------------+------------+------+-----------------------+------+---------+------+------+----------+-------------+ 1 row in setmysql>為什么呢? 就幾條數(shù)據(jù)的話, 結合B+樹的結構, MySQL認為從根節(jié)點開始向下找,還不如直接從葉子節(jié)點從頭開始掃描快呢
Case 3 : like KK% 一般情況都會走索引
結合索引樹 , like KK% 可以理解為就是按照 = KK 查詢
mysql> EXPLAIN SELECT * FROM employees WHERE name like 'LiLei%' AND age = 22 AND position ='manager'; +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | employees | NULL | range | idx_name_age_position | idx_name_age_position | 140 | NULL | 1 | 5 | Using index condition | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ 1 row in setmysql> EXPLAIN SELECT * FROM employees_2 WHERE name like 'LiLei%' AND age = 22 AND position ='manager'; +----+-------------+-------------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | employees_2 | NULL | range | idx_name_age_position | idx_name_age_position | 140 | NULL | 1 | 33.33 | Using index condition | +----+-------------+-------------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ 1 row in set原因: 索引下推 MySQL -索引下推 Index Condition Pushdown 初探
特殊例子
一般情況 ,但也不絕對。看下面這個例子
假設你這個表 的name字段 是以Artisan開頭的,從Artisan1 到Artisan100000
再去like的話 ,mysql會基于cost,自主選擇 ,比如如下走了全表掃描。
搞定MySQL
總結
以上是生活随笔為你收集整理的MySQL - 索引优化案例实操的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL - 共享锁和排它锁初探
- 下一篇: MySQL - 索引下推 Index C