MySQL-索引优化篇(1)_安装演示库 [前缀索引、联合索引、覆盖索引] explain参数
文章目錄
- 生猛干貨
- 官方文檔
- 安裝演示數(shù)據(jù)庫sakila
- 索引優(yōu)化策略
- 索引列上不能使用表達(dá)式或者函數(shù)
- 前綴索引和索引列的選擇性
- 前綴索引的創(chuàng)建
- 索引列的選擇性
- 前綴索引的優(yōu)缺點(diǎn)
- 聯(lián)合索引
- 如何選擇索引列的順序
- 覆蓋索引
- 定義
- 優(yōu)點(diǎn)
- 無法使用覆蓋索引的情況
- 演示
- explain的幾個(gè)參數(shù)的說明
- 搞定MySQL
生猛干貨
帶你搞定MySQL實(shí)戰(zhàn),輕松對(duì)應(yīng)海量業(yè)務(wù)處理及高并發(fā)需求,從容應(yīng)對(duì)大場(chǎng)面試
官方文檔
https://dev.mysql.com/doc/
如果英文不好的話,可以參考 searchdoc 翻譯的中文版本
http://www.searchdoc.cn/rdbms/mysql/dev.mysql.com/doc/refman/5.7/en/index.com.coder114.cn.html
安裝演示數(shù)據(jù)庫sakila
[root@artisan ~]# wget http://downloads.mysql.com/docs/sakila-db.tar.gz ..... ..... ..... 2020-02-01 21:31:15 (2.74 KB/s) - ‘sakila-db.tar.gz’ saved [732161/732161][root@artisan ~]# tar -xvzf sakila-db.tar.gz sakila-db/ sakila-db/sakila-data.sql sakila-db/sakila-schema.sql sakila-db/sakila.mwb [root@artisan ~]# cd sakila-db [root@artisan sakila-db]# ls sakila-data.sql sakila.mwb sakila-schema.sql [root@artisan sakila-db]# mysql -uroot -p < sakila-schema.sql Enter password: [root@artisan sakila-db]# mysql -uroot -p < sakila-data.sql Enter password: [root@artisan sakila-db]#索引優(yōu)化策略
索引列上不能使用表達(dá)式或者函數(shù)
舉個(gè)例子
select .... from t_order where to_days(out_date) - to_days(current_date) < = 30即使我們?cè)趏ut_date建立了 B樹索引,因?yàn)槭褂昧撕瘮?shù)to_days,無法走索引。
那該如何改造呢? ------------> 如下
select .... from t_order where out_date <= data_add(current_date , interval 30 day) ;前綴索引和索引列的選擇性
當(dāng)索引是很長(zhǎng)的字符序列(比如BLOB,TEXT,或者很長(zhǎng)的VARCHAR)時(shí),這個(gè)索引將會(huì)很占內(nèi)存,而且會(huì)很慢,這時(shí)候就會(huì)用到前綴索引了。所謂的前綴索引就是去索引的前面幾個(gè)字母作為索引,但是要降低索引的重復(fù)率,索引我們還必須要判斷前綴索引的重復(fù)率
前綴索引的創(chuàng)建
創(chuàng)建前綴索引
create index index_name on table(col_name(n));注意建立索引的區(qū)別 col_name(n)
這個(gè)n的長(zhǎng)度,取悅于存儲(chǔ)引擎
-
innodb 最大767個(gè)字節(jié)
-
myIsam 最大1000個(gè)直接
索引列的選擇性
索引的選擇性是指不重復(fù)的索引值和表的記錄數(shù)的比值
選擇性越高,查詢效率越快。 因?yàn)檫x擇性高的索引可以讓MySQL在查找時(shí)過濾掉更多的行。唯一索引的選擇性是1,這是最好的索引選擇性,性能也是最好的。
舉個(gè)例子:
有4條記錄
如果前綴索引,我們創(chuàng)建的時(shí)候,長(zhǎng)度設(shè)置的是2 , 那么
不重復(fù)的索引為 2 ,總記錄數(shù)為4 , 索引選擇性 0.5
如果設(shè)置為 3 ,則
不重復(fù)的索引為 4 ,總記錄數(shù)為4 , 索引選擇性1 . 此時(shí),性能最高,因?yàn)椴挥眠^濾數(shù)據(jù)啊。
合理選擇,對(duì)提高查詢性能幫助很大
前綴索引的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 前綴索引是一種能使索引更小,更快的有效辦法 。
缺點(diǎn):
- mysql無法使用其前綴索引做ORDER BY和GROUP BY,也無法使用前綴索引做覆蓋掃描。
聯(lián)合索引
如何選擇索引列的順序
-
經(jīng)常會(huì)被使用到的列優(yōu)先,放到聯(lián)合索引的最左邊 。
但也不是絕對(duì)的,舉個(gè)例子 有個(gè)狀態(tài) state,就幾個(gè)值, 選擇性很差(因?yàn)楦鶕?jù)每個(gè)state,篩選出來的數(shù)據(jù)太多了。。。)就不適合放到聯(lián)合索引的最左邊 ,放到了最左邊,mysql也不一定使用
-
選擇性高的列優(yōu)先 。
啥叫選擇性高的列? 比如根據(jù)這個(gè)列 經(jīng)過篩選后,能夠把大部分的數(shù)據(jù)都過濾掉,之剩下很少的數(shù)據(jù),那么就可以把這列稱為選擇性高的列。
-
寬度小的列優(yōu)先
當(dāng)然了有個(gè)前提,不違反選擇性。 寬度小意味著I/O 少,效率高
覆蓋索引
定義
覆蓋索引: 如果一個(gè)索引包含(或覆蓋)所有需要查詢的字段的值 ,簡(jiǎn)言之----->只需掃描索列而無須回表查非索引列的字段。
優(yōu)點(diǎn)
-
可優(yōu)化緩存,減少磁盤I/O操作
舉個(gè)例子: 一個(gè)表 15個(gè)字段, 索引字段 3個(gè), 我們就查詢這3個(gè)索引列的值,而不用回表,查詢的字段少,可以緩存更多的數(shù)據(jù),同時(shí)從內(nèi)存中獲取,可以極大的減少磁盤I/O操作
-
可以減少隨機(jī)I/O, 變隨機(jī)I/O為順序I/O操作
-
可以避免對(duì)Innodb主鍵索引的二次查詢
-
可以避免MyISAM表進(jìn)行系統(tǒng)調(diào)用
無法使用覆蓋索引的情況
- 有些存儲(chǔ)引擎不支持覆蓋索引
- 如果查詢中使用了太多的列,尤其是那種查詢?nèi)孔侄蔚?#xff0c;或者 select * 的
- 使用了雙% 號(hào)的like查詢
演示
我們用剛才導(dǎo)入的 sakila 數(shù)據(jù) 來演示下
select * 無法使用覆蓋索引的演示:
[root@artisan sakila-db]# mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 63 Server version: 5.7.29-log MySQL Community Server (GPL) ....... ....... mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | artisan | | artisanBinLog | | data | | mysql | | performance_schema | | sakila | | sys | +--------------------+ 8 rows in set (0.01 sec)mysql> use sakila; No connection. Trying to reconnect... Connection id: 64 Current database: *** NONE ***Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -ADatabase changed mysql> show tables; +----------------------------+ | Tables_in_sakila | +----------------------------+ | actor | | actor_info | | address | | category | | city | | country | | customer | | customer_list | | film | | film_actor | | film_category | | film_list | | film_text | | inventory | | language | | nicer_but_slower_film_list | | payment | | rental | | sales_by_film_category | | sales_by_store | | staff | | staff_list | | store | +----------------------------+ 23 rows in set (0.00 sec)mysql> desc film; +----------------------+---------------------------------------------------------------------+------+-----+-------------------+-----------------------------+ | Field | Type | Null | Key | Default | Extra | +----------------------+---------------------------------------------------------------------+------+-----+-------------------+-----------------------------+ | film_id | smallint(5) unsigned | NO | PRI | NULL | auto_increment | | title | varchar(128) | NO | MUL | NULL | | | description | text | YES | | NULL | | | release_year | year(4) | YES | | NULL | | | language_id | tinyint(3) unsigned | NO | MUL | NULL | | | original_language_id | tinyint(3) unsigned | YES | MUL | NULL | | | rental_duration | tinyint(3) unsigned | NO | | 3 | | | rental_rate | decimal(4,2) | NO | | 4.99 | | | length | smallint(5) unsigned | YES | | NULL | | | replacement_cost | decimal(5,2) | NO | | 19.99 | | | rating | enum('G','PG','PG-13','R','NC-17') | YES | | G | | | special_features | set('Trailers','Commentaries','Deleted Scenes','Behind the Scenes') | YES | | NULL | | | last_update | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP | +----------------------+---------------------------------------------------------------------+------+-----+-------------------+-----------------------------+ 13 rows in set (0.00 sec)# 查詢 索引列 language_id 重點(diǎn)看 Extra mysql> explain select language_id from film where language_id = 1 \G ; *************************** 1. row ***************************id: 1select_type: SIMPLEtable: filmpartitions: NULLtype: ref possible_keys: idx_fk_language_id -----> 可能用的索引 key: idx_fk_language_id ----------------> 實(shí)際使用的索引key_len: 1ref: constrows: 1000filtered: 100.00Extra: Using index ---------------->使用了索引,因?yàn)閮H查詢了索引列,這里就是覆蓋索引 1 row in set, 1 warning (0.07 sec)ERROR: No query specified# 查看執(zhí)行計(jì)劃 重點(diǎn)看 Extra mysql> explain select * from film where language_id = 1 \G ; *************************** 1. row ***************************id: 1select_type: SIMPLEtable: filmpartitions: NULLtype: ALL ----------------------> 連接類型 possible_keys: idx_fk_language_id -----> 可用的索引key: NULL ----------------> 實(shí)際的索引key_len: NULLref: NULLrows: 1000filtered: 100.00Extra: Using where --------------------> using where:表示優(yōu)化器需要通過索引回表查詢數(shù)據(jù); 1 row in set, 1 warning (0.00 sec)ERROR: No query specifiedmysql>在來看個(gè)例子
mysql> show create table actor \G ; *************************** 1. row ***************************Table: actor Create Table: CREATE TABLE `actor` (`actor_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,`first_name` varchar(45) NOT NULL,`last_name` varchar(45) NOT NULL,`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`actor_id`),KEY `idx_actor_last_name` (`last_name`) ) ENGINE=InnoDB AUTO_INCREMENT=201 DEFAULT CHARSET=utf8mb4 1 row in set (0.00 sec)ERROR: No query specifiedmysql> explain select actor_id , last_name from actor where last_name = 'Joe' \G *************************** 1. row ***************************id: 1select_type: SIMPLEtable: actorpartitions: NULLtype: ref possible_keys: idx_actor_last_namekey: idx_actor_last_namekey_len: 182ref: constrows: 1filtered: 100.00Extra: Using index ------> using index 表示直接訪問索引就足夠獲取到所需要的數(shù)據(jù),不需要通過索引回表; 1 row in set, 1 warning (0.30 sec)主鍵 actor_id ,默認(rèn)就是索引 ,所以雖然增加了 actor_id , last_name也是索引列(創(chuàng)建primary key的時(shí)候肯定會(huì)創(chuàng)建一個(gè)unique index。),所以這個(gè)查詢也是使用了覆蓋索引。
explain的幾個(gè)參數(shù)的說明
id: 1select_type: SIMPLEtable: actorpartitions: NULLtype: ref possible_keys: idx_actor_last_namekey: idx_actor_last_namekey_len: 182ref: constrows: 1filtered: 100.00Extra: Using index-
id
-
select_type: 查詢中每個(gè)select子句的類型
(1) SIMPLE(簡(jiǎn)單SELECT,不使用UNION或子查詢等)
(2) PRIMARY(查詢中若包含任何復(fù)雜的子部分,最外層的select被標(biāo)記為PRIMARY)
(3) UNION(UNION中的第二個(gè)或后面的SELECT語句)
(4) DEPENDENT UNION(UNION中的第二個(gè)或后面的SELECT語句,取決于外面的查詢)
(5) UNION RESULT(UNION的結(jié)果)
(6) SUBQUERY(子查詢中的第一個(gè)SELECT)
(7) DEPENDENT SUBQUERY(子查詢中的第一個(gè)SELECT,取決于外面的查詢)
(8) DERIVED(派生表的SELECT, FROM子句的子查詢)
(9) UNCACHEABLE SUBQUERY(一個(gè)子查詢的結(jié)果不能被緩存,必須重新評(píng)估外鏈接的第一行)
-
type 代表連接類型
常用的類型有: ALL, index, range, ref, eq_ref, const, system, NULL
從左到右,性能從差到好 , ALL 最差, NULL最好
ALL:Full Table Scan, MySQL將遍歷全表以找到匹配的行
index: Full Index Scan,index與ALL區(qū)別為index類型只遍歷索引樹
range:只檢索給定范圍的行,使用一個(gè)索引來選擇行
ref: 表示上述表的連接匹配條件,即哪些列或常量被用于查找索引列上的值
eq_ref: 類似ref,區(qū)別就在使用的索引是唯一索引,對(duì)于每個(gè)索引鍵值,表中只有一條記錄匹配,簡(jiǎn)單來說,就是多表連接中使用primary key或者 unique key作為關(guān)聯(lián)條件
const、system: 當(dāng)MySQL對(duì)查詢某部分進(jìn)行優(yōu)化,并轉(zhuǎn)換為一個(gè)常量時(shí),使用這些類型訪問。如將主鍵置于where列表中,MySQL就能將該查詢轉(zhuǎn)換為一個(gè)常量,system是const類型的特例,當(dāng)查詢的表只有一行的情況下,使用system
NULL: MySQL在優(yōu)化過程中分解語句,執(zhí)行時(shí)甚至不用訪問表或索引,例如從一個(gè)索引列里選取最小值可以通過單獨(dú)索引查找完成。
-
possible_keys: 可用的索引 ,實(shí)際不一定用
-
keys : MyQL實(shí)際的索引
-
key_len: 表示索引中使用的字節(jié)數(shù)
-
extra (需重點(diǎn)關(guān)注)
1) using tempoaray : 中間使用了臨時(shí)表
2) using index 表示直接訪問索引就足夠獲取到所需要的數(shù)據(jù),不需要通過索引回表;
3) using index condition:5.6加入 ,會(huì)先條件過濾索引,過濾完索引后找到所有符合索引條件的數(shù)據(jù)行,隨后用 WHERE 子句中的其他條件去過濾這些數(shù)據(jù)行;
4) using where: 未使用索引,通過where條件過濾
搞定MySQL
總結(jié)
以上是生活随笔為你收集整理的MySQL-索引优化篇(1)_安装演示库 [前缀索引、联合索引、覆盖索引] explain参数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL-Btree索引和Hash索引
- 下一篇: MySQL-索引优化篇(2)_使用索引扫