不論是開發、運維仍是實施等崗位的同窗,對於本身所接觸、所編寫的各類SQL語句,都應該可以進行調優,從而使本身能寫出更優解的SQL語句,我的以爲更是一種必備技能。mysql
先提供例子中涉及到表的建表、建索引語句。算法
-- 用戶表 create table t_user( id int primary key, loginname varchar(100), name varchar(100), age int, sex char(1), dep_id int, address varchar(100) ); --部門表 create table t_dep( id int primary key, name varchar(100) ); --建立普通索引 mysql> alter table t_user add index idx_dep_id(dep_id); --建立惟一索引 mysql> alter table t_user add unique index uk_loginname(loginname); --建立組合索引 mysql> alter table t_user add index idx_name_age_sex(name,age,sex);
explain出來的信息有12列,分別是:sql
id、select_type 、table 、partitions 、type、possible_keys、key 、key_len、ref 、rows、filtered、Extra
接下來會對各個字段作對應的說明,並列舉對應的例子。服務器
每一個select語句都會自動分配一個惟一的標識符運維
表示SELECT的類型,常見的取值有SIMPLE(簡單表,即不使用錶鏈接查詢)、PRIMARY(主查詢,即外層的查詢)、UNION(UNION中的第二個或者後面的查詢語句)、SUBQUERY(子查詢中的第一個SELECT)等性能
mysql> explain select * from t_user; +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+ | 1 | SIMPLE | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | NULL | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)
mysql> explain select (select name from t_user) from t_user; +----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+-------------+ | 1 | PRIMARY | t_user | NULL | index | NULL | idx_dep_id | 5 | NULL | 6 | 100.00 | Using index | | 2 | SUBQUERY | t_user | NULL | index | NULL | idx_name_age_sex | 312 | NULL | 6 | 100.00 | Using index | +----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+-------------+ 2 rows in set, 1 warning (0.00 sec)
mysql> explain select name from t_user union select name from t_user2; +----+--------------+------------+------------+-------+---------------+------------------+---------+------+---------+----------+-----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------+------------+------------+-------+---------------+------------------+---------+------+---------+----------+-----------------+ | 1 | PRIMARY | t_user | NULL | index | NULL | idx_name_age_sex | 312 | NULL | 6 | 100.00 | Using index | | 2 | UNION | t_user2 | NULL | ALL | NULL | NULL | NULL | NULL | 9756827 | 100.00 | NULL | | NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary | +----+--------------+------------+------------+-------+---------------+------------------+---------+------+---------+----------+-----------------+ 3 rows in set, 1 warning (0.00 sec)
輸出結果集的表優化
表示MySQL在表中找到所需行的方式,或者叫訪問類型,常見類型以下:線程
ALL < index < range < ref < eq_ref < const,systemserver
從左到右,性能由差到最好blog
mysql> explain select * from t_user; +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+ | 1 | SIMPLE | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | NULL | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)
mysql> explain select name from t_user; +----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+-------------+ | 1 | SIMPLE | t_user | NULL | index | NULL | idx_name_age_sex | 312 | NULL | 6 | 100.00 | Using index | +----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
mysql> explain select * from t_user where dep_id > 10; +----+-------------+--------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | t_user | NULL | range | idx_dep_id | idx_dep_id | 5 | NULL | 1 | 100.00 | Using index condition | +----+-------------+--------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+ 1 row in set, 1 warning (0.01 sec)
mysql> explain select * from t_user where dep_id = 10; +----+-------------+--------+------------+------+---------------+------------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+---------------+------------+---------+-------+------+----------+-------+ | 1 | SIMPLE | t_user | NULL | ref | idx_dep_id | idx_dep_id | 5 | const | 1 | 100.00 | NULL | +----+-------------+--------+------------+------+---------------+------------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)
索引idx_dep_id是非惟一索引,查詢條件爲等值查詢條件dep_id = 10,因此掃描索引的類型爲ref。ref 還常常出如今join操做中。
mysql> explain select * from t_user a join t_dep b on a.name = b.name; +----+-------------+-------+------------+------+------------------+------------------+---------+------------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+------------------+------------------+---------+------------+------+----------+-------------+ | 1 | SIMPLE | b | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 100.00 | Using where | | 1 | SIMPLE | a | NULL | ref | idx_name_age_sex | idx_name_age_sex | 303 | ssm.b.name | 1 | 100.00 | NULL | +----+-------------+-------+------------+------+------------------+------------------+---------+------------+------+----------+-------------+ 2 rows in set, 1 warning (0.00 sec)
mysql> explain select b.id from t_user a left join t_dep b on a.dep_id = b.id; +----+-------------+-------+------------+--------+---------------+------------+---------+--------------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+---------------+------------+---------+--------------+------+----------+-------------+ | 1 | SIMPLE | a | NULL | index | NULL | idx_dep_id | 5 | NULL | 5 | 100.00 | Using index | | 1 | SIMPLE | b | NULL | eq_ref | PRIMARY | PRIMARY | 4 | ssm.a.dep_id | 1 | 100.00 | Using index | +----+-------------+-------+------------+--------+---------------+------------+---------+--------------+------+----------+-------------+ 2 rows in set, 1 warning (0.00 sec)
mysql> explain select * from t_user where loginname = 'liubei'; +----+-------------+--------+------------+-------+---------------+--------------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+-------+---------------+--------------+---------+-------+------+----------+-------+ | 1 | SIMPLE | t_user | NULL | const | uk_loginname | uk_loginname | 303 | const | 1 | 100.00 | NULL | +----+-------------+--------+------------+-------+---------------+--------------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)
類型type還有其餘值,如ref_or_null(與ref相似,區別在於條件中包含對NULL的查詢)、index_merge(索引合併優化)、unique_subquery(in 的後面是一個查詢主鍵字段的子查詢)、index_subquery(與 unique_subquery 相似,區別在於 in 的後面是查詢非惟一索引字段的子查詢)等
表示查詢時可能使用的索引
表示實際使用的索引
使用到索引字段的長度
掃描行的數據
執行狀況的說明和描述,包含不適合在其餘列中顯示可是對執行計劃很是重要的額外信息。
mysql> explain select * from t_user order by address; +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+----------------+ | 1 | SIMPLE | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 100.00 | Using filesort | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+----------------+ 1 row in set, 1 warning (0.00 sec)
全部不是經過索引直接返回排序結果的排序都叫Filesort排序。Filesort並不表明經過磁盤文件進行排序,而只是說明進行了一個排序操做,至於排序操做是否使用了磁盤文件或臨時表等,則取決於MySQL服務器對排序參數的設置和須要排序數據的大小。
Filesort是經過相應的排序算法,將取得的數據在sort_buffer_size系統變量設置的內存排序區中進行排序,若是內存裝載不下,它就會將磁盤上的數據進行分塊,再對各個數據塊進行排序,而後將各個塊合併成有序的結果集。sort_buffer_size 設置的排序區是每一個線程獨佔的,因此同一個時刻,MySQL中存在多個 sort buffer排序區。瞭解了MySQL排序的方式,優化目標就清晰了:儘可能減小額外的排序,經過索引直接返回有序數據。WHERE條件和ORDER BY使用相同的索引,而且ORDER BY的順序和索引順序相同,而且ORDER BY的字段都是升序或者都是降序。不然確定須要額外的排序操做,這樣就會出現Filesort。
--引自<深刻淺出Mysql>18.4.3優化ORDER BY語句 章節
mysql> explain select name,age,sex from t_user ; +----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+-------------+ | 1 | SIMPLE | t_user | NULL | index | NULL | idx_name_age_sex | 312 | NULL | 5 | 100.00 | Using index | +----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) mysql> explain select name,age,sex from t_user where age > 10; +----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+ | 1 | SIMPLE | t_user | NULL | index | NULL | idx_name_age_sex | 312 | NULL | 5 | 33.33 | Using where; Using index | +----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+ 1 row in set, 1 warning (0.00 sec)
mysql> explain select distinct a.id from t_user a,t_dep b where a.dep_id = b.id; +----+-------------+-------+------------+-------+--------------------------------------------------+------------+---------+----------+------+----------+------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+--------------------------------------------------+------------+---------+----------+------+----------+------------------------------+ | 1 | SIMPLE | b | NULL | index | PRIMARY | PRIMARY | 4 | NULL | 1 | 100.00 | Using index; Using temporary | | 1 | SIMPLE | a | NULL | ref | PRIMARY,uk_loginname,idx_dep_id,idx_name_age_sex | idx_dep_id | 5 | ssm.b.id | 5 | 100.00 | Using index | +----+-------------+-------+------------+-------+--------------------------------------------------+------------+---------+----------+------+----------+------------------------------+ 2 rows in set, 1 warning (0.00 sec)
問題:若是變動條件,出現的執行計劃應該是怎樣的?
explain select distinct a.id from t_user a,t_dep b where a.dep_id = b.id and b.name = 'na';
有興趣回答的讀者能夠評論區回覆。
mysql> explain select * from t_user where age > 10; +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 33.33 | Using where | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ mysql> explain select * from t_user where address = 'beijing'; +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 20.00 | Using where | +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) mysql> explain select * from t_user where id in (1,2,3); +----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | 1 | SIMPLE | t_user | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 3 | 100.00 | Using where | +----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
mysql> show index from t_user; +--------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +--------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | t_user | 0 | PRIMARY | 1 | id | A | 5 | NULL | NULL | | BTREE | | | | t_user | 0 | uk_loginname | 1 | loginname | A | 5 | NULL | NULL | YES | BTREE | | | | t_user | 1 | idx_dep_id | 1 | dep_id | A | 1 | NULL | NULL | YES | BTREE | | | | t_user | 1 | idx_name_age_sex | 1 | name | A | 5 | NULL | NULL | YES | BTREE | | | | t_user | 1 | idx_name_age_sex | 2 | age | A | 5 | NULL | NULL | YES | BTREE | | | | t_user | 1 | idx_name_age_sex | 3 | sex | A | 5 | NULL | NULL | YES | BTREE | | | +--------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 6 rows in set (0.00 sec) mysql> explain select * from t_user where id in (1,2,3) and name = 'liubei'; +----+-------------+--------+------------+------+--------------------------+------------------+---------+-------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+--------------------------+------------------+---------+-------+------+----------+-----------------------+ | 1 | SIMPLE | t_user | NULL | ref | PRIMARY,idx_name_age_sex | idx_name_age_sex | 303 | const | 1 | 60.00 | Using index condition | +----+-------------+--------+------------+------+--------------------------+------------------+---------+-------+------+----------+-----------------------+ 1 row in set, 1 warning (0.00 sec) mysql> explain select * from t_user where name = 'aa' and sex = '1'; +----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-----------------------+ | 1 | SIMPLE | t_user | NULL | ref | idx_name_age_sex | idx_name_age_sex | 303 | const | 1 | 20.00 | Using index condition | +----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-----------------------+ 1 row in set, 1 warning (0.00 sec)
今天的Mysql執行計劃分析就到這了。相信掌握了上面的內容,會對本身也是一個不錯的提高。
上文有誤的地方,歡迎讀者提出來,一塊兒探討。