MySQL索引及數據結構


Hash索引

(1) 它會使用到hash函數,算出一個確切的值 , 如果key發生變化. hash值也會跟着發生變化. 而且還存在着hash沖突的情況.

(2)  聯合索引的情況 hash(id+name) = hash值 , 不能支持 部分索引查詢和范圍查找.

 

紅黑樹

(1) 樹太高,讀取磁盤的次數過多, 1,2,4,8,16......

  比如第一層只會存一個數據,讀一次磁盤,就取1個數據 ,如果是n叉樹,讀一次磁盤可以拿到n個數據....

(2) 每次讀取磁盤浪費太多

所以, 不適合做數據庫索引, 畢竟它只是個二叉樹,太浪費磁盤IO了.

 

紅黑樹用於HashMap的原因?

因為HashMap是內存數據結構,所以不存在上面提到的兩個問題

 

B樹

B-tree,  B+tree,   B*tree

 

B-tree是n叉排序樹.

M階的Btree的幾個重要特性:

1. 結節最多含有m顆子樹(指針), m-1個關鍵字(存儲數據的空間) (m>2)

2. 除根節點和葉子節點外,其它每個節點至少有ceil(m/2)個子節點, ciel向上取整 . 比如, 5/2 =2.5 (取整) = 3 . 分裂的時候從中間分開,分成兩顆子樹.

3. 若根節點不是葉子節點,則至少有兩根子樹

 

示例

  創建一顆5階Btree,插入的數據有3 14 7 1 8 5 11 17 13 6 23 12 20 26 4 16 18 24 25 19(數據庫的userId),根據Btree的特點, 5階Btree即一個磁盤空間最多有5個指針(存的查找路徑), 4個關鍵字(mysql中存的數據)

 

 

現在插入8,發現空間不夠,就會出現一次分裂,移動中間元素到根節點即可

 

 

 

 

 

 

現在插入13, 7的右子樹空間又不夠了,將會再次發生分裂, 不過這次需要注意的是,13會插入到7的后面

 

 

 

 

后面過程略,最終結構如下圖所示

 

 

再比較一下B+tree的結構

 

MySQL是如何建聯合索引的?

假設我們使用user_id+name+age建聯合索引, MySQL的底層還是使用user_id字段來創建的索引,只不過使用user_id創建的這顆索引樹的葉子節點不再是存儲的MySQL數據,它存儲的是 user_id+name+age+ 數據地址(這個地址指向了數據庫的真實數據).

所以,在使用聯合索引查詢數據的時候,首先是通過user_id查找這顆樹的葉子,然后在葉子節點中找到 user_id+name+age 對應的這條數據地址,然后再使用這個地址去磁盤讀取數據.

聯合索引也體現了最左原則,所以在使用聯合索引查詢的時候,如果沒有user_id,MySQL根本就找不到索引樹.所以,索引就會失敗.

 

MySQL索引樹一個節點存儲關鍵字的多少 = 頁大小(16K)/索引大小

每個節點上關鍵字越多,樹就應該越矮, 查找速度也就更快, 所以,建索引的字段應該越小越好.

 

再來一個小示例

 

mysql> select * from tb_emp;
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | 喬峰 | 123 |
+----+--------+------+
1 row in set (0.00 sec)

 


mysql> show index from tb_emp;
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| tb_emp | 0 | PRIMARY | 1 | id | A | 2 | NULL | NULL | | BTREE | | |
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

 

 

# 創建一個name+age 的聯合索引

 


mysql> alter table tb_emp add index `name_age_index`(name,age);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0


mysql> show index from tb_emp;
+--------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| tb_emp | 0 | PRIMARY | 1 | id | A | 2 | NULL | NULL | | BTREE | | |
| tb_emp | 1 | name_age_index | 1 | name | A | 1 | NULL | NULL | YES | BTREE | | |
| tb_emp | 1 | name_age_index | 2 | age | A | 1 | NULL | NULL | YES | BTREE | | |
+--------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec)

索引創建成功!

 

(1) name_age_index索引存在 

mysql> explain select * from tb_emp where age =123;
+----+-------------+--------+------------+-------+---------------+----------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+----------------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | tb_emp | NULL | index | NULL | name_age_index | 88 | NULL | 2 | 50.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+----------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

 

(2) drop name_age_index索引

mysql> drop index name_age_index on tb_emp;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0

 

mysql> explain select * from tb_emp where age =123;
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | tb_emp | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

 

在原先的認知, name+age創建的組合索引name_age_index后,如果查詢條件中沒有使用name,僅使用age是不會走索引的.

然而,通過上面的測試可知,Extra給出的解釋是Using where; Using index ,說明它即使用了索引, 也回了表.

 

刪除name_age_index后,Extra給出的解釋只有Using where, 說明只是回表查詢.

 

why? 不知道!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM