MySQL索引知识总结
對MySQL索引知識的總結筆記。
普通索引
索引是一種數據結構,主要用于性能的提高。
比如我們有一個表t_users,有4個字段:
| 1234567 | create table t_users ( id bigint(20) not null auto_increment, name varchar(255) not null,age bigint(20) not null, num bigint(20) not null,primary key (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
往這個表中插入100w條數據,name字段是format-1到100w,age字段也是1-100w,num字段是個隨機的10以內的數字。然后執行sql語句進行查詢:
| 1234567 | mysql> select * from t_users where name = 'format-500000';+--------+---------------+--------+-----+| id | name | age | num |+--------+---------------+--------+-----+| 500000 | format-500000 | 500000 | 38 |+--------+---------------+--------+-----+1 row in set (0.47 sec) |
explain一下這條語句:
| 123456 | mysql> explain select * from t_users where name = 'format-500000';+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+| 1 | SIMPLE | t_users | ALL | NULL | NULL | NULL | NULL | 996677 | Using where |+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+ |
發現查詢了996677條數據,進行了全表的掃描。
我們給name字段加上索引:
| 1 | mysql> create index IDX_FOR_NAME on t_users(name); |
再次執行sql語句:
| 1234567 | mysql> select * from t_users where name = 'format-500000';+--------+---------------+--------+-----+| id | name | age | num |+--------+---------------+--------+-----+| 500000 | format-500000 | 500000 | 38 |+--------+---------------+--------+-----+1 row in set (0.00 sec) |
explain一下:
| 1234567 | mysql> explain select * from t_users where name = 'format-500000';+----+-------------+---------+------+---------------+--------------+---------+-------+------+-----------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+---------+------+---------------+--------------+---------+-------+------+-----------------------+| 1 | SIMPLE | t_users | ref | IDX_FOR_NAME | IDX_FOR_NAME | 767 | const | 1 | Using index condition |+----+-------------+---------+------+---------------+--------------+---------+-------+------+-----------------------+1 row in set (0.00 sec) |
只查詢了1條數據,因為我們加了btree索引,可以快速定位到具體的值。
接下來我們查詢num字段:
| 12345678910 | mysql> select * from t_users where num = 66;+--------+---------------+--------+-----+....| 965109 | format-965109 | 965109 | 66 || 965172 | format-965172 | 965172 | 66 || 965182 | format-965182 | 965182 | 66 || 965213 | format-965213 | 965213 | 66 |....+--------+---------------+--------+-----+10029 rows in set (0.30 sec) |
由于num字段也是沒有加索引的,查詢的時候也進行全表的掃描,查詢耗時。接下來我們給num字段加上索引。
| 1 | mysql> create index IDX_FOR_NUM on t_users(num); |
然后進行查詢:
| 12345678910 | mysql> select * from t_users where num = 5;+--------+---------------+--------+-----+....| 965109 | format-965109 | 965109 | 5 || 965172 | format-965172 | 965172 | 5 || 965182 | format-965182 | 965182 | 5 || 965213 | format-965213 | 965213 | 5 |....+--------+---------------+--------+-----+10029 rows in set (0.04 sec) |
explain一下:
| 123456 | mysql> explain select * from t_users where num = 5;+----+-------------+---------+------+---------------+-------------+---------+-------+-------+-------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+---------+------+---------------+-------------+---------+-------+-------+-------+| 1 | SIMPLE | t_users | ref | IDX_FOR_NUM | IDX_FOR_NUM | 8 | const | 206712 | NULL |+----+-------------+---------+------+---------------+-------------+---------+-------+-------+-------+ |
雖然我們在num字段上加了索引,但是由于num值在這100w條數據中有很多重復的數據,這個時候索引對查詢速度的提高就沒有那么明顯了。
因為不論是hash類型的索引還是btree類型的索引,他們對于重復的數據的查詢并沒有提高多少。相反,由于添加了索引,導致數據寫入性能變差,而查詢性能又沒有增強多少。所以說不能盲目地添加索引。
復合索引
復合索引也叫聯合索引,表示索引建立在多個列上。
我們刪除t_users上的索引,然后創建一個復合索引。
| 1234 | mysql> drop index IDX_FOR_NUM on t_users;mysql> drop index IDX_FOR_NAME on t_users;mysql> create index INDEX_FOR_NAME_AGE on t_users(name, age); |
復合索引支持最左原則,也就是說INDEX_FOR_NAME_AGE索引支持name字段的查找,支持name,age字段的查找,但是不支持age,name字段的查找以及age字段的查找。
| 1234567891011121314151617181920 | mysql> explain select * from t_users where name = 'format-100000';+----+-------------+---------+------+--------------------+--------------------+---------+-------+------+-----------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+---------+------+--------------------+--------------------+---------+-------+------+-----------------------+| 1 | SIMPLE | t_users | ref | INDEX_FOR_NAME_AGE | INDEX_FOR_NAME_AGE | 767 | const | 1 | Using index condition |+----+-------------+---------+------+--------------------+--------------------+---------+-------+------+-----------------------+mysql> explain select * from t_users where name = 'format-100000' and age = 100000;+----+-------------+---------+------+--------------------+--------------------+---------+-------------+------+-----------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+---------+------+--------------------+--------------------+---------+-------------+------+-----------------------+| 1 | SIMPLE | t_users | ref | INDEX_FOR_NAME_AGE | INDEX_FOR_NAME_AGE | 775 | const,const | 1 | Using index condition |+----+-------------+---------+------+--------------------+--------------------+---------+-------------+------+-----------------------+mysql> explain select * from t_users where age = 100000;+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+| 1 | SIMPLE | t_users | ALL | NULL | NULL | NULL | NULL | 996677 | Using where |+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+ |
使用age,name字段查找:
| 123456 | mysql> explain select * from t_users where age = 100000 and name = 'format-100000';+----+-------------+---------+------+--------------------+--------------------+---------+-------------+------+-----------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+---------+------+--------------------+--------------------+---------+-------------+------+-----------------------+| 1 | SIMPLE | t_users | ref | INDEX_FOR_NAME_AGE | INDEX_FOR_NAME_AGE | 775 | const,const | 1 | Using index condition |+----+-------------+---------+------+--------------------+--------------------+---------+-------------+------+-----------------------+ |
我們發現使用age,name字段的查找使用了復合索引,這是因為MySQL內部有個查詢優化器幫我們進行了優化。
索引的生效
索引創建之后,查詢的過程并不一定會使用索引。整理如下(t_users表中name和num字段都有單獨的索引):
1.當使用索引查詢比全表掃描更慢。比如下面這句sql中num字段的值分布在1-10之間
| 123456 | mysql> explain select * from t_users where num > 1 and num < 8;+----+-------------+----------+------+---------------+------+---------+------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+----------+------+---------------+------+---------+------+--------+-------------+| 1 | SIMPLE | t_users | ALL | IDX_ON_NUM | NULL | NULL | NULL | 996504 | Using where |+----+-------------+----------+------+---------------+------+---------+------+--------+-------------+ |
2.使用or進行查詢,并且or左右兩邊的列有不存在索引的列
| 123456 | mysql> explain select * from t_users where name = 'format-4000' or age = 50;+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+| 1 | SIMPLE | t_users | ALL | IDX_FOR_NAME | NULL | NULL | NULL | 996677 | Using where |+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+ |
or兩邊的列都有索引:
| 123456 | mysql> explain select * from t_users where name = 'format-4000' or num = 50;+----+-------------+----------+-------------+---------------------------------+---------------------------------+---------+------+------+-----------------------------------------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+----------+-------------+---------------------------------+---------------------------------+---------+------+------+-----------------------------------------------------------+| 1 | SIMPLE | t_users | index_merge | IDX_ON_NUM,IDX_FOR_NAME | IDX_FOR_NAME,IDX_ON_NUM | 767,8 | NULL | 2 | Using union(IDX_FOR_NAME,IDX_ON_NUM); Using where |+----+-------------+----------+-------------+---------------------------------+---------------------------------+---------+------+------+-----------------------------------------------------------+ |
3.使用like,并以 % 開頭
| 12345678910111213 | mysql> explain select * from t_users where name like "%format-200";+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+| 1 | SIMPLE | t_users | ALL | NULL | NULL | NULL | NULL | 996677 | Using where |+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+mysql> explain select * from t_users where name like "%format-200%";+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+| 1 | SIMPLE | t_users | ALL | NULL | NULL | NULL | NULL | 996677 | Using where |+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+ |
以 % 結尾的查詢會使用索引:
| 123456 | mysql> explain select * from t_users where name like "format-200%";+----+-------------+---------+-------+---------------+--------------+---------+------+------+-----------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+---------+-------+---------------+--------------+---------+------+------+-----------------------+| 1 | SIMPLE | t_users | range | IDX_FOR_NAME | IDX_FOR_NAME | 767 | NULL | 1110 | Using index condition |+----+-------------+---------+-------+---------------+--------------+---------+------+------+-----------------------+ |
4.復合索引
不是復合索引的最左字段(t_users表有(name,age)復合索引)。
| 123456 | mysql> explain select * from t_users where age = 100000;+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+| 1 | SIMPLE | t_users | ALL | NULL | NULL | NULL | NULL | 996677 | Using where |+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+ |
5.對varchar類型的字段進行查詢的時候,沒有加上引號
| 12345678910111213 | mysql> explain select * from t_users where name = 111;+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+| 1 | SIMPLE | t_users | ALL | IDX_FOR_NAME | NULL | NULL | NULL | 996677 | Using where |+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+mysql> explain select * from t_users where name = "111";+----+-------------+---------+------+---------------+--------------+---------+-------+------+-----------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+---------+------+---------------+--------------+---------+-------+------+-----------------------+| 1 | SIMPLE | t_users | ref | IDX_FOR_NAME | IDX_FOR_NAME | 767 | const | 1 | Using index condition |+----+-------------+---------+------+---------------+--------------+---------+-------+------+-----------------------+ |
hash索引和btree索引的區別
原文鏈接: http://fangjian0423.github.io/2017/07/05/mysql-index-summary/
總結
以上是生活随笔為你收集整理的MySQL索引知识总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sql必知必会(第四版) 学习笔记二 视
- 下一篇: MyBatis动态SQL底层原理分析