1. 程式人生 > >13-6_mysql索引_1_Mysql_Learning_Notes_20180719_13-6

13-6_mysql索引_1_Mysql_Learning_Notes_20180719_13-6

thead blob 索引範圍掃描 啟用 sim 中間節點 err binary default

mysql索引_1_Mysql_Learning_Notes

一種在有序數組中查找某一特定元素的搜索算法;
二分查找法的優點是比較少次數,查找速度快,平均性能好;其缺點是要求待查表為有序表,且插入刪除困難,因此二分查找方法適用於不經常變動而查找頻繁的有序列表.

  • 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 TABLEsbtest2(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), KEYk_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以後怎麽查看索引使用情況?
  1. 通過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。

  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