13-6_mysql索引_1_Mysql_Learning_Notes_20180719_13-6
mysql索引_1_Mysql_Learning_Notes
二分查找/折半查找法,binary search
一種在有序數組中查找某一特定元素的搜索算法;
二分查找法的優點是比較少次數,查找速度快,平均性能好;其缺點是要求待查表為有序表,且插入刪除困難,因此二分查找方法適用於不經常變動而查找頻繁的有序列表.
innodb 要求聚集索引列都要求是有序自增列.innnodb 是事務型的存儲引擎,其中的行總是會被刪除並提交,count(*) 相對要慢一些.
二叉樹,binary tree
二叉樹的每個節點至多只有二棵子樹(不存在大於2的節點),二叉樹的子樹有左右有序之分,次序不能顛倒.左子樹一定小於根,右子樹一定大於根.根節點是子節點的中間節點.
4 1 10
平衡樹,平衡二叉樹,Self balancing binary search tree
- 改進的二叉查找樹.一般的二叉查找樹的查詢復雜度是跟目標節點到樹根的距離(深度)有關,因此當前節點的深度普遍較大時,查詢的均攤復雜度會上升.為了更高效的查詢就有了平衡樹.
- 平衡二叉樹的特點:
- 它是一棵空樹或其左右兩個子樹的高度差的絕對值不超過1(比如左邊高度是6,右的高度只能是5或7),且左右兩個子樹也是平衡二叉樹.
- 不平衡的樹會通過自旋變成平衡樹.
- 平衡樹和二叉查找樹最大的區別是:前者是平衡的,後者不一定.
數據庫裏使用平衡二叉樹,如果有插入值很大可能會導致其自旋?.
B樹,balanced tree(平衡多叉樹)
- 又稱B-樹\B_樹
- B樹,一個節點可以擁有多於2個的子節點的多叉查找樹
- 適合大量數據的讀寫操作,普遍運用在數據庫和文件系統.
- 一棵m階(比如m=4階)的B樹滿足以下條件.
- 樹中每個節點至多有m個(4個)子節點
- 除根節點和葉子節點外,其它每個節點至少有m/2個(2個)子節點.
- 若根節點不是葉子節點,則至少有2個子節點.
- 所有葉子節點都出現在同一層,葉子節點不包含任何鍵值信息.
有k個子節點的非葉子節點恰好包含有k-1個鍵值(索引節點)
記錄中應該用‘節點‘還是‘結節‘,baidu了一下,有師兄解釋:一個節點是兩線相交,中間的點,另一個結點是最後的點。二叉樹好像特別一點,是結點,葉子結點和非葉子節點(但不確定正確性,我就所有都用節點了)
B+樹,與B樹不同點在:
- 有n棵子樹對的節點(node)中含有n-1個關鍵字(key),每個關鍵字不保存數據,只用來索引,所有數據都保存在葉子節點.
- 所有的葉子節點中包含了全部關鍵字的信息,及指向含這些關鍵字記錄的指針,且葉子節點本身依關鍵字的大小自小而大順序鏈接.
- 所有的非葉子節點可以看成是索引的部分,節點中僅含其子樹(根節點)中的最大(或最小)關鍵字.
在MySQL中,為了方便,直接寫成BTREE
B+樹高度相關性
高度 1 2 3 4 - 以三層B+樹為列,最查詢3次,平均不到3次就可以查詢到結果.查詢效率高,XFS文件系統也是使用的B+樹,所以優於之前的esx4
- 怎麽看innodb的B+TREE層數?,下面以sysbench_testdata.sbtest2為例查看索引層數:
- 查看相關系統
``sql root@localhost [sysbench_testdata]>show create table sbtest2; | sbtest2 | CREATE TABLE
sbtest2(
idint(11) NOT NULL AUTO_INCREMENT,
kint(11) NOT NULL DEFAULT ‘0‘,
cchar(120) NOT NULL DEFAULT ‘‘,
padchar(60) NOT NULL DEFAULT ‘‘, PRIMARY KEY (
id), KEY
k_2(
k`)
) ENGINE=InnoDB AUTO_INCREMENT=67840915 DEFAULT CHARSET=utf8 |
1 row in set (0.00 sec)
- 查看相關系統
root@localhost [sysbench_testdata]>select count(id) from sbtest2;
+-----------+
| count(id) |
+-----------+
| 67840914 |
+-----------+
1 row in set (56.87 sec)
```
- 查看information_schema中相關表信息,註意索引的PAGE_NO和:index_id
root@localhost [sysbench_testdata]>SELECT b.name, a.name, index_id, type, a.space, a.PAGE_NO FROM information_schema.INNODB_SYS_INDEXES a, information_schema.INNODB_SYS_TABLES b WHERE a.table_id = b.table_id AND a.space <> 0 and b.name=‘sysbench_testdata/sbtest2‘;
+---------------------------+---------+----------+------+-------+---------+
| name | name | index_id | type | space | PAGE_NO |
+---------------------------+---------+----------+------+-------+---------+
| sysbench_testdata/sbtest2 | PRIMARY | 51 | 3 | 33 | 3 |
| sysbench_testdata/sbtest2 | k_2 | 58 | 0 | 33 | 38 |
+---------------------------+---------+----------+------+-------+---------+
2 rows in set (0.00 sec)
root@localhost [sysbench_testdata]>show global variables like ‘innodb_page_size‘;
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| innodb_page_size | 16384 |
+------------------+-------+
1 row in set (0.00 sec)
- 到表的文件系統目錄中:cd /data/57mysql/mysql3508/data/sysbench_testdata
#hexdump -s 49216 -n 10 ./sbtest2.ibd
000c040 0300 0000 0000 0000 3300
000c04a
#hexdump -s 622656 -n 10 ./sbtest2.ibd
0098040 0200 0000 0000 0000 3a00
009804a
- 註:hexdump中49216和622656是怎麽算出來的?這個數分別對應sbtest2表的兩個索引,公式是 page_no * innodb_page_size + 64。PRIMARY:316384+64=49216 k_2:3816384+64=622656 ,同時可以觀察hexdump結果中的3300和3a00,此數十六進制為33和3a,轉換成十進制為:51和58,分別和information_schema中的index_id對應上了.
- 可以發現 主鍵索引(PRIMARY)的PAGE_LEVEL 為 0300,表示這棵二級索引樹的高度為 4,k_2索引的PAGE_LEVEL 為 0200,表示這棵二級索引樹的高度為 3.
哈希索引,Hash Index
- 建立在哈希表的基礎上,它只對使用了索引中的每個值的
精確查找
有用. - 對於每一行,存儲引擎計算出了被索引的哈希碼(Hash Code),它是一個較小的值,並且有可能和其他行的哈希碼不同(對象相等則hashCode一定相等;hashCode相等對象未必相等)
- 把哈希碼保存在索引中,並且保存了一個指向哈希表中的每一行的指針
- 也叫散列索引.
- 問題:如果不同對像產生了相同的hashCode(
哈希沖突
),如在索引表裏無法做到一一對應到記錄行? - 哈希索引優勢:合適大量的等值查詢,此種情況效率高於B+TREE
- HASH Index 缺點:
- 不支持模糊查詢和範圍查詢
- 不支持排序
- 不支持聯合索引中的最左匹配規則
- 只能顯式應用於HEAP/MEMORY/NDB表
- 註意:
Innodb內部的自適應哈希索引和此處說的不是一回事.自適應哈希索引是沒辦法被引用和修改的,innodb自適應哈希索引只能用啟用或禁用,沒辦法指定某一個表使用哈希索引.
什麽是索引
相當於書目,用於快速檢索
- 優點
- 提高數據檢索效率
- 提高表間的join效率
- 利用唯一性索引,保證數據的唯一性.
- 提交排序和分組效率.
- 缺點
- 消耗更多物理存儲空間
數據變更時,索引也需要更新,降低更新效率.(更新索引時會導致cpu在sys增高),在5.7以上,可以通過查詢得到索引的利用率.
MySQL 5.7以後怎麽查看索引使用情況?
- 通過show status like ‘%Handler_read%‘方法查看:整體的
root@localhost [sysbench_testdata]>show status like ‘%Handler_read%‘;
+-----------------------+---------+
| Variable_name | Value |
+-----------------------+---------+
| Handler_read_first | 7 |
| Handler_read_key | 29 |
| Handler_read_last | 0 |
| Handler_read_next | 8446377 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 20 |
| Handler_read_rnd_next | 8344612 |
+-----------------------+---------+
7 rows in set (0.00 sec)
Handler_read_key這個值代表了一個行將索引值讀的次數,很低的值表明增加索引得到的性能改善不高,因為索引並不經常使用。
Handler_read_rnd_next 的值高則查詢低效,並且應該建立索引補救。這個值是指在數據文件中讀下一行的請求數。如果正進行大量的表掃描,Handler_read_rnd_next的值較高,則通常說明表索引不正確或查詢沒有利用索引
2.查看具體某一個sql的索引使用情況 :
root@localhost [sysbench_testdata]>explain select k from sbtest2 where k=432 limit 2;
+----+-------------+---------+------------+------+---------------+------+---------+-------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+-------+--------+----------+-------------+
| 1 | SIMPLE | sbtest2 | NULL | ref | k_2 | k_2 | 4 | const | 110944 | 100.00 | Using index |
+----+-------------+---------+------------+------+---------------+------+---------+-------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
字段說明:
Type:告訴我們對表所使用的訪問方式,主要包含如下集中類型;
◇ all:全表掃描
◇ const:讀常量,且最多只會有一條記錄匹配,由於是常量,所以實際上只需要讀一次;
◇ eq_ref:最多只會有一條匹配結果,一般是通過主鍵或者唯一鍵索引來訪問;
◇ fulltext:
◇ index:全索引掃描;
◇ index_merge:查詢中同時使用兩個(或更多)索引,然後對索引結果進行merge 之後再讀取表數據;
◇ index_subquery:子查詢中的返回結果字段組合是一個索引(或索引組合),但不是一個主鍵或者唯一索引;
◇ rang:索引範圍掃描;
◇ ref:Join 語句中被驅動表索引引用查詢;
◇ ref_or_null:與ref 的唯一區別就是在使用索引引用查詢之外再增加一個空值的查詢;
◇ system:系統表,表中只有一行數據;
◇ unique_subquery:子查詢中的返回結果字段組合是主鍵或者唯一約束;
possible_keys:可能可以利用的索引的名字。這裏的索引名字是創建索引時指定的索引昵稱;如果索引沒有昵稱,則默認顯示的是索引中第一個列的名字(在本例中,它是“firstname”)。默認索引名字的含義往往不是很明顯。
key:它顯示了MySQL實際使用的索引的名字。如果它為空(或NULL),則MySQL不使用索引。
key_len:索引中被使用部分的長度,以字節計
ref:列出是通過常量(const),還是某個表的某個字段(如果是join)來過濾(通過key)
的;
rows:MySQL所認為的它在找到正確的結果之前必須掃描的記錄數。顯然,這裏最理想的數字就是1。
- 通過performance_schema可以查詢到.查看sbtest2表索引情況的查詢語句:
root@localhost [sysbench_testdata]>select object_type,object_schema,object_name,index_name,count_star,count_read,COUNT_FETCH from performance_schema.table_io_waits_summary_by_index_usage where object_name=‘sbtest2‘;
- 具體查看過程:
root@localhost [sysbench_testdata]>select object_type,object_schema,object_name,index_name,count_star,count_read,COUNT_FETCH from performance_schema.table_io_waits_summary_by_index_usage where object_name=‘sbtest2‘;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id: 1697669
Current database: sysbench_testdata
+-------------+-------------------+-------------+------------+------------+------------+-------------+
| object_type | object_schema | object_name | index_name | count_star | count_read | COUNT_FETCH |
+-------------+-------------------+-------------+------------+------------+------------+-------------+
| TABLE | sysbench_testdata | sbtest2 | PRIMARY | 0 | 0 | 0 |
| TABLE | sysbench_testdata | sbtest2 | k_2 | 76287298 | 76287298 | 76287298 |
| TABLE | sysbench_testdata | sbtest2 | NULL | 8344631 | 8344631 | 8344631 |
+-------------+-------------------+-------------+------------+------------+------------+-------------+
3 rows in set (0.00 sec)
root@localhost [sysbench_testdata]>select k from sbtest2 where k=432 limit 2;
+-----+
| k |
+-----+
| 432 |
| 432 |
+-----+
2 rows in set (0.00 sec)
root@localhost [sysbench_testdata]>select object_type,object_schema,object_name,index_name,count_star,count_read,COUNT_FETCH from performance_schema.table_io_waits_summary_by_index_usage where object_name=‘sbtest2‘;
+-------------+-------------------+-------------+------------+------------+------------+-------------+
| object_type | object_schema | object_name | index_name | count_star | count_read | COUNT_FETCH |
+-------------+-------------------+-------------+------------+------------+------------+-------------+
| TABLE | sysbench_testdata | sbtest2 | PRIMARY | 0 | 0 | 0 |
| TABLE | sysbench_testdata | sbtest2 | k_2 | 76287300 | 76287300 | 76287300 |
| TABLE | sysbench_testdata | sbtest2 | NULL | 8344631 | 8344631 | 8344631 |
+-------------+-------------------+-------------+------------+------------+------------+-------------+
3 rows in set (0.01 sec)
root@localhost [sysbench_testdata]>select k from sbtest2 where id=432 limit 2;
+-------+
| k |
+-------+
| 49866 |
+-------+
1 row in set (0.00 sec)
root@localhost [sysbench_testdata]>select object_type,object_schema,object_name,index_name,count_star,count_read,COUNT_FETCH from performance_schema.table_io_waits_summary_by_index_usage where object_name=‘sbtest2‘;
+-------------+-------------------+-------------+------------+------------+------------+-------------+
| object_type | object_schema | object_name | index_name | count_star | count_read | COUNT_FETCH |
+-------------+-------------------+-------------+------------+------------+------------+-------------+
| TABLE | sysbench_testdata | sbtest2 | PRIMARY | 1 | 1 | 1 |
| TABLE | sysbench_testdata | sbtest2 | k_2 | 76287300 | 76287300 | 76287300 |
| TABLE | sysbench_testdata | sbtest2 | NULL | 8344631 | 8344631 | 8344631 |
+-------------+-------------------+-------------+------------+------------+------------+-------------+
3 rows in set (0.00 sec)
root@localhost [sysbench_testdata]>
索引使用建議
- 哪種情況下應該創建索引
- 經常檢索的列
- 經常用於表連接的列
- 經常排序或分組的列
- 不建議使用索引的情況
- 基數很低的列
- 更新頻繁但檢索不頻繁的列
- BLOB/TEXT等長內容列
- 很少用於檢索的列
聚集索引,clustered index
- 聚集索引是一種索引,該索引中鍵值的邏輯順序決定了表數據行的物理順序(註:
在聚集索引下,數據在物理上按順序排在數據頁上,重復值也排在一起,因而在那些包含範圍檢查(between、<、<=、>、>=)或使用group by或orderby的查詢時,一旦找到具有範圍中第一個鍵值的行,具有後續索引值的行保證物理上毗連在一起而不必進一步搜索,避免了大範圍掃描,可以大大提高查詢速度
) - 每張表只能建一個聚集索引,除了TokuDB引擎
- InnoDB中,聚集索引即表,表即聚集索引(註:
mysql一個表只支持一個聚集索引。在innodb裏面聚集索引就是整個表,表就是聚集索引,因為innodb的聚集索引後面是整行數據,如果主鍵由多列組成,btree優先按第一列順序存儲,在聚集索引btree裏面每個葉子節點最終存儲每行數據,這就是為什麽在innodb裏面沒有任何條件count (*),它會優先選擇普通索引來完成掃描,而不是采用主鍵索引,因為如果掃聚集索引,掃描的數據量更大,產生的IO更大,如果掃描普通輔助索引,那麽它的數據結構通常來講比主鍵索引小。
) - MyISAM 沒有聚集索引的概念.
- InnoDB表中主鍵一定是聚集索引,但聚集索引不一定是主鍵.
- 在聚集索引中不要包含經常修改的列,因為碼值修改後,數據行必須移動到新的位置。同時新增數據過於離散隨機也不合適.
- INT/BIGINT(單調順序列)可優先做為聚集索引
mysql 聚集索引的選擇順序:
如果有主鍵則選擇主鍵,沒有主鍵則選擇第一個not nullable的唯一索引,沒有滿足要求的唯一鍵,最後會使用rowid.,但這個rowid為實例級全局id,所以如果聚集索引如果選擇rowid,可能會導致性能降低
主鍵索引(PRIMARY KEY)
- 主鍵由表中的一個或多個字段組成,它的值用於唯一的標識表中的某一條記錄;
- 在表引用中,主鍵在一個表中引用來自另一個表中的特定記錄(外鍵foreign key應用);
- 保證數據的完整性
- 加快數據的操作速度;
- 主鍵值不能重復,也不能包含null.
- 主鍵選擇建議:
- 對業務透明,無意義,免受業務變化影響.
- 很少修改和刪除
- 最好是自增的
- 不要具有動態屬性(如隨機值)
MySQL 5.6.9及以後不管索引定義時,有無顯示包含主鍵,實際都會存儲主鍵值,如:c1為主鍵,索引z,為c2,c3聯合索引,但z會存儲c1的值,這一特性加:索引擴展(Index Extensions).
-查詢中是基於主鍵好,還是唯一索引好?唯一索引(UNIQUE KEY)
- 不允許具有索引值相同的行,從而禁止重復的索引或鍵值;
- 嚴格意義上講,應該叫唯一約束;
- 在唯一約束上和主鍵一樣(以MyISAM引擎為代表)
- 唯一索引允許有空值(null)
- 一個表只能有一個主鍵,但可以有多個唯一索引;
唯一索引約束可臨時禁用,但主鍵不行;
聯合索引(Combined Indexes,Multiple-Column Indexes)
- 多列組成,所以也叫多列索引
- 字段從左到右排序,如c1,c2,c3的聯合索引,首先進行c1排序,c1字段值相同的按c2排序,如果前兩列都相同才會按c3排序.
|c1|c2|c3|
|-|
|1|2|2|
|2|3|2|
|2|4|2|
|3|2|0|
|3|2|2| - 適合where條件中的多列組合
- 有時候,還可以用於避免回表(覆蓋索引,
需要的數據都在索引覆蓋的字段範圍內,不需要再去表中取數據
,如果使用的覆蓋索引,執行計劃中的Extra列會顯示關鍵字:using index) - MySQL還不支持多列不同排序規則(8.0起支持)
建議:
1.where條件中,經常同時出現的列放在聯合索引中;2把選擇性(過濾性/基數)大的列放在聯合索引的最左邊(經常出現的列放在最左邊
).覆蓋索引(covering indexes)
- 通過索引數據結構,即可直接返回數據,不需要回表;
執行計劃中的Extra列會顯示關鍵字:using index.
13-6_mysql索引_1_Mysql_Learning_Notes_20180719_13-6