學習MySql索引,看下數據量多的情況下,使用索引來進行查詢和不使用索引來進行查詢的區別大不大。當然這裏準備的數據,肯定不會是啥公司的數據或者什麼真實的數據,是通過存儲函數來進行插入的100w的數據,來進行索引操作的使用學習記錄。
先來創建一張表 ; 這裏我記錄的使用的索引是 InnoDB
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;
使用存儲過程來插入數據
DELIMITER $$
CREATE
PROCEDURE `test`.`insert_user_func`()
BEGIN
DECLARE i INT;
SET i=0;
WHILE i>=0 && i<= 1000000 DO
INSERT INTO test.`t_users`
(`name`, `age`, `num`) VALUES
(CONCAT('format-',i),i,RAND()*100);
SET i=i + 1;
END WHILE;
END$$
調用存儲過程
call insert_user_func();
調用完了;MySql 需要一點時間去插入數據。
可以使用count(1) 來查詢看你這個表中的數量多少。
select count(1) from test.t_users;
當存儲函數執行完了的時候,數據就來了,就可以看到具體的效果。
先來看下用不用索引的效果
這個上面就是name 這個字段是否使用索引的情況,可以從時間和explain的分析上可以看出來效果。因爲在存儲過程中,name這個字段是根據i的一次疊加,所以是不會又重複的。
下面來看下num這個隨機生成100以內的數據,是肯定會有重複的數據的。
雖然我們在num字段上加了索引,但是由於num值在這100w條數據中有很多重複的數據,這個時候索引對查詢速度的提高就沒有那麼明顯了。 num 和 name這二個字段的區別就是在於,name這個字段是唯一的,num是會有很多重複的,從explain中的rows就可以看出來效果了。
因爲不論是hash類型的索引還是btree類型的索引,他們對於重複的數據的查詢並沒有提高多少。相反,由於添加了索引,導致數據寫入性能變差,而查詢性能又沒有增強多少。所以說不能盲目地添加索引。
下面來看下複合索引; 複合索引是支持最左匹配原則.
刪除之前的單個索引;創建一個複合索引.複合索引支持最左原則,也就是說INDEX_FOR_NAME_AGE索引支持name字段的查找,支持name,age字段的查找,但是不支持age,name字段的查找以及age字段的查找。
drop index IDX_FOR_NUM on t_users;
drop index IDX_FOR_NAME on t_users;create index INDEX_FOR_NAME_AGE on t_users(name, age);
從這個圖片上的效果看出來,當我們使用age name 去組合的時候,居然也會中索引.這是怎麼回事呢?
這是因爲MySQL內部有個查詢優化器幫我們進行了優化。
是不是索引創建完了就完事了呢?如果索引沒有使用上,那麼和不創建索引有啥區別呢?
下面來看看索引失效的 explain來分析的.
索引創建之後,查詢的過程並不一定會使用索引。整理如下(t_users表中name和num字段都有單獨的索引):
當使用索引查詢比全表掃描更慢。比如下面這句sql中num字段的值分佈在1-10之間
mysql> explain select * from t_users where num > 1 and num < 8;
+----+-------------+---------+------------+-------+---------------+-------------+---------+------+--------+----------+----------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+-------------+---------+------+--------+----------+----------------------------------+
| 1 | SIMPLE | t_users | NULL | range | IDX_FOR_NUM | IDX_FOR_NUM | 8 | NULL | 112416 | 100.00 | Using index condition; Using MRR |
+----+-------------+---------+------------+-------+---------------+-------------+---------+------+--------+----------+----------------------------------+
1 row in set, 1 warning (0.01 sec)
使用or進行查詢,並且or左右兩邊的列有不存在索引的列
mysql> explain select * from t_users where name = 'format-4000' or age = 50;
+----+-------------+---------+------------+------+---------------------------------+------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------------------------+------+---------+------+--------+----------+-------------+
| 1 | SIMPLE | t_users | NULL | ALL | INDEX_FOR_NAME_AGE,IDX_FOR_NAME | NULL | NULL | NULL | 996519 | 19.00 | Using where |
+----+-------------+---------+------------+------+---------------------------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
or兩邊的列都有索引:
mysql> explain select * from t_users where name = 'format-4000' or num = 50;
+----+-------------+---------+------------+-------------+---------------------------------------------+--------------------------------+---------+------+-------+----------+---------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------------+---------------------------------------------+--------------------------------+---------+------+-------+----------+---------------------------------------------------------------+
| 1 | SIMPLE | t_users | NULL | index_merge | INDEX_FOR_NAME_AGE,IDX_FOR_NUM,IDX_FOR_NAME | INDEX_FOR_NAME_AGE,IDX_FOR_NUM | 767,8 | NULL | 18205 | 100.00 | Using sort_union(INDEX_FOR_NAME_AGE,IDX_FOR_NUM); Using where |
+----+-------------+---------+------------+-------------+---------------------------------------------+--------------------------------+---------+------+-------+----------+---------------------------------------------------------------+
1 row in set, 1 warning (0.01 sec)
使用like,並以 % 開頭
mysql> explain select * from t_users where name like "%format-200";
+----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| 1 | SIMPLE | t_users | NULL | ALL | NULL | NULL | NULL | NULL | 996519 | 11.11 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
以 % 結尾的查詢會使用索引:
mysql> explain select * from t_users where name like "format-200%";
+----+-------------+---------+------------+-------+---------------------------------+--------------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------------------------+--------------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | t_users | NULL | range | INDEX_FOR_NAME_AGE,IDX_FOR_NAME | INDEX_FOR_NAME_AGE | 767 | NULL | 1111 | 100.00 | Using index condition |
+----+-------------+---------+------------+-------+---------------------------------+--------------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.02 sec)
對varchar 類型進行查詢的時候,不加上''的問題
mysql> explain select * from t_users where name = 111;
+----+-------------+---------+------------+------+---------------------------------+------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------------------------+------+---------+------+--------+----------+-------------+
| 1 | SIMPLE | t_users | NULL | ALL | INDEX_FOR_NAME_AGE,IDX_FOR_NAME | NULL | NULL | NULL | 996519 | 10.00 | Using where |
+----+-------------+---------+------------+------+---------------------------------+------+---------+------+--------+----------+-------------+
1 row in set, 5 warnings (0.00 sec)explain select * from t_users where name = "111";
mysql> explain select * from t_users where name = "111";
+----+-------------+---------+------------+------+---------------------------------+--------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------------------------+--------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | t_users | NULL | ref | INDEX_FOR_NAME_AGE,IDX_FOR_NAME | INDEX_FOR_NAME_AGE | 767 | const | 1 | 100.00 | NULL |
+----+-------------+---------+------------+------+---------------------------------+--------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
從explain 中可以看出具體的Sql效果是怎麼樣的。
總結:
hash索引和btree索引的區別
- hash索引不能使用範圍查詢,只能使用一些比如 “=”, “<>”, “in”查詢。因爲hash索引會計算索引列的hash值,計算出後的hash值經過了hash算法與原先的值完全不一樣,只能進行等值的過濾,不能基於範圍的過濾
- hash索引遇到大量hash值相同的情況下,性能比btree要差
- hash索引並不一定一次可以定位到數據。因爲基於索引列計算出的hash值會有重複,重複的話需要掃描hash表進行比較
- 由於hash索引中存放的是經過hash計算之後的hash值,而且hash值的大小關係並不一定和hash運算前的鍵值完全一樣,所以數據庫無法利用索引的數據來避免任何排序運算
- 對於組合索引,hash索引在計算hash值的時候是組合索引鍵合併後再一起計算hash值,而不是單獨計算hash值,所以通過組合索引的前面一個或幾個索引鍵進行查詢的時候,hash索引也無法被利用
- InnoDB和MyISAM引擎不支持hash索引