記一次MySql索引使用記錄

   學習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索引的區別

  1. hash索引不能使用範圍查詢,只能使用一些比如 “=”, “<>”, “in”查詢。因爲hash索引會計算索引列的hash值,計算出後的hash值經過了hash算法與原先的值完全不一樣,只能進行等值的過濾,不能基於範圍的過濾
  2. hash索引遇到大量hash值相同的情況下,性能比btree要差
  3. hash索引並不一定一次可以定位到數據。因爲基於索引列計算出的hash值會有重複,重複的話需要掃描hash表進行比較
  4. 由於hash索引中存放的是經過hash計算之後的hash值,而且hash值的大小關係並不一定和hash運算前的鍵值完全一樣,所以數據庫無法利用索引的數據來避免任何排序運算
  5. 對於組合索引,hash索引在計算hash值的時候是組合索引鍵合併後再一起計算hash值,而不是單獨計算hash值,所以通過組合索引的前面一個或幾個索引鍵進行查詢的時候,hash索引也無法被利用
  6. InnoDB和MyISAM引擎不支持hash索引

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章