1. 程式人生 > >數據庫 之 MySQL的索引

數據庫 之 MySQL的索引

xpl 站點 總數 有一個 ria ddos攻擊 結束 子查詢 分層

1 概述

本文將介紹索引的相關概念,以及用EXPLAIN來分析索引

2 索引的相關概念

索引是指提取索引的創建在的表上字段中的數據,構建出一個獨特的數據結構;

索引的作用:加速查詢操作;副作用:降低寫操作性能;

表中數據子集:把表中某個或某些字段的數據提取出來另存為一個特定數據結構組織的數據;

某個字段或某些字段:WHERE子句中用到的字段;

索引對寫操作有影響,寫操作只能放在對應的位置。

索引本身是平衡樹。其排序是分層構建的。

索引能夠加速讀操作,但是對寫操作是相反的效果。因為多一次索引就多一次寫IO,因此要適量創建索引。

索引要構建在查詢條件之上。即索引要建立在where語句中常用到的條件

索引是一種數據結構,是為了加速某種操作而設定的數據結構

mysql因為有索引,所以在海量數據的查詢更高效

索引優點:

降低需要掃描的數據量,根據索引定位資源,減少IO次數;

可以幫助避免排序操作,因為索引本身就是根據順序存放的,避免使用臨時表;

幫助將隨機IO轉為順序IO,提示系統性能;

3 索引類型

這裏介紹兩種索引類型:B+ TREE和HASH

B+ TREE

balance tree,順序存儲,每一個葉子結點到根結點的距離相同;將數據抽取出來後按順序重新存放,占據一定空間,mysql數據庫的索引一般為B tree索引,為左前綴索引,適合於範圍類型的數據查詢,最左側的數據很關鍵,

適用於B+ TREE索引的查詢類型:全鍵值、鍵值範圍或鍵前綴;

全值匹配:精確匹配某個值;也稱全鍵值匹配。

WHERE COLUMN = 'value';

匹配最左前綴:只精確匹配起頭的部分;

WEHRE COLUMN LIKE 'PREFIX%';

匹配範圍值:

精確匹配某一列,範圍匹配另一列;如根據第一字段索引,如果第一字段一樣,再根據第二字段索引

只用訪問索引的查詢,也叫覆蓋索引;這是一種快速查詢的方法

index(Name),表示Name這個字段為索引,根據Name這個字段創建查詢條件時,查詢時,只需要查找索引,而不需要再到表中查找,效率會更高,如下

SELECT Name FROM students WHERE Name LIKE 'L%';

不適用B+ TREE索引:

如果查詢條件不是從最左側列開始,索引無效;如有個索引為index(age,Fname),那麽查詢條件為 WHERE Fname='Jerry'; , WHERE age>30 AND Fname='Smith';,此時索引是無效的,因為不是從左側索引開始查詢

不能跳過索引中的某列;

如有索引為index(name,age,gender)

條件:WHERE name='black' and age > 30;該索引有效

條件:WHERE name='black' AND gender='F';跳過age這個索引項,則為無效索引

如果查詢中的某個列是為範圍查詢,那麽其右側的列都無法再使用索引優化查詢;

WHERE age>30 AND Fname='Smith';此時左側的age>30這個條件匹配到的範圍更大,那麽 Fname='Smith'就無效。如果條件為WHERE Fname='Smith' AND age>30;就有效

Hash索引

基於哈希表實現,特別適用於值的精確匹配查詢;鍵值索引。

hash索引也叫鍵值索引。基於鍵查找值,MyISAM和Innodb都不支持hash索引,只有memory存儲引擎才支持hash.但是innodb支持自適應hash索引。

適用場景:

只支持等值的精確比較查詢,例如=, IN(), <=>;MySQL僅對memory存儲引擎支持顯式的hash索引;

index(name)

where name="sunny"

不用場景:

所有非精確值查詢;

4 高性能索引策略

以下是高性能索引策略,即用好索引的原則

(1) 在WHERE中獨立使用列,盡量避免其參與運算;

WHERE age+2 > 32 ; 不允許這樣的操作

(2) 左前綴索引:索引構建於字段的最左側的多少個字符,要通過索引選擇性來評估

索引選擇性:不重復的索引值和數據表的記錄總數的比值;

(3) 多列索引:

AND連接的多個查詢條件更適合使用多列索引,而非多個單鍵索引;

where gender='F' and age>18;創建索引為index(gender,age)而不創建為index(gender) index(age)兩個單鍵索引,因為index(gender,age)更高效

(4) 選擇合適的索引列次序:選擇性最高的放左側;

5 EXPLAIN來分析索引有效性

EXPLAIN解析某個select語句是否會用到索引以及如何使用索引,以此來判斷自己定義的索引是否有效,把沒有用的索引刪除

EXPLAIN [explain_type] SELECT select_options

explain_type:

EXTENDED

| PARTITIONS

輸出結果:

id: 1

select_type: SIMPLE

table: students

type: const

possible_keys: PRIMARY

key: PRIMARY

key_len: 4

ref: const

rows: 1

Extra:

輸出結果字段介紹

id:當前查詢語句中,第個SELECT語句的編號;可能是復雜查詢,就會有多個查詢

例子:復雜查詢

MariaDB [sunny]> select name,age from students where age > (select avg(age) from students);

復雜的查詢的類型主要三種:

簡單子查詢

用於FROM中的子查詢

聯合查詢

註意:聯合查詢的分析結果會出現一個額外的匿名臨時表;

select_type:查詢類型:

簡單查詢:SIMPLE

復雜查詢:

簡單子查詢:SUBQUERY

用於FROM中的子查詢:DERIVED

聯合查詢中的第一個查詢:PRIMARY

聯合查詢中的第一個查詢之後的其它查詢:UNION

聯合查詢生成的臨時表:UNION RESULT

table:查詢針對的表;

type:關聯類型,或稱為訪問類型,也叫訪問方法;即MySQL如何去查詢表中的行,即獲取數據的類型,有如下的方式:

ALL:全表掃描;

index:根據索引的順序進行的全表掃描;但同時如果Extra列出現了"Using index”表示使用了覆蓋索引;

range:有範圍限制地根據索引實現範圍掃描;掃描位置始於索引中的某一項,結束於另一項;

ref:根據索引返回的表中匹配到某單個值的所有行(匹配給定值的行不止一個);

例子:

以下結果中有多個age=60的記錄,則type為ref

MariaDB [sunny]> explain select name from students where age=60;

eq_ref:根據索引返回的表中匹配到某單個值的單一行,僅返回一個行,但需要與某個額外的參考值比較,而不是常數,如通過變量得到的;

const,system:與某個常數比較,且只返回一行;這兩個是最高效.

例子

以下結果中,id為主鍵,得到的type為const

MariaDB [sunny]> explain select name from students where id=60;

possiable_keys:查詢中可能會用到的索引,可能用到的索引會有多個;

key:查詢中使用的索引,真正用到的索引如果有的話,是只有一個;

key_len:查詢中用到的索引長度,表示索引的前幾個字節;

ref:在利用key字段所顯示的索引完成查詢操作時所引用的列或常量值;

rows:MySQL估計出的為找到所有的目標項而需要讀取的行數;這個值是大於或等於符合條件的行;

Extra:額外信息

Using index:使用了覆蓋索引進行的查詢;高效的方法。

Using where:拿到數據後還要再次進行過濾才能得到最終結果;

Using temporary:使用了臨時表以完成查詢;

Using filesort:對結果使用了一個外部索引排序;只查詢出的結果如果超過16M的大小,那麽要使用內存中的臨時表來排序就不能實現,因此要把數據放到磁盤上再讀出來,因此效率低,所以,如果看到額外信息中有Using filesort,建議進行優化,否則執行性能會很差。

站點訪問很慢,有可能是站點受到DDos攻擊,或者是站點的訪問量太大,也有可能是是業務層有問題,如代碼有bug,要麽是數據層問題,因為並發量太大或者是發起了復制查詢,索引創建不合理等。

例子

創建表,並用for循環插入大量數據,然後用EXPLAIN來分析索引的有效性

創建表

create table students(id int unsigned auto_increment primary key,name char(30) not null,age tinyint unsigned,gender enum('F','M'),major varchar(200));

插入數據

MariaDB [sunny]> insert into students values (1,"sunny",28,"M","math"),(2,"tracy",27,"F","Engilsh");

註意,這裏為了大量生成數據,接下來直接在shell裏生成用for循環生成大量的數據,進行插入

因為字段gender是枚舉型,這裏就定義一個數組gender('F' 'M')進行生成

定義數組

[root@CentOS7A sunny]#gender=('F' 'M')

用for循環來插入大量數據,可以用mysql -e在shell裏執行sql語句

[root@CentOS7A sunny]#for i in {1..1000};do mysql -uroot -pPass123456 -e "insert into sunny.students(name,age,gender) values('stu$i','$[$RANDOM%80+18]','${gender[$RANDOM%2]}');";done

explain分析查詢語句

MariaDB [sunny]> explain select * from students where age>90;

+------+-------------+----------+------+---------------+------+---------+------+------+-------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+------+-------------+----------+------+---------------+------+---------+------+------+-------------+

| 1 | SIMPLE | students | ALL | NULL | NULL | NULL | NULL | 950 | Using where |

+------+-------------+----------+------+---------------+------+---------+------+------+-------------+

1 row in set (0.01 sec)

以上結果表示,select語句的查詢類型是simple,是在students表執行,獲取數據的類型是all,即做全表掃描,將整個表載入內存,遍歷所有數據得到的結果,裝入了950行數據,使用where條件得到的。possible_keys是NULL,表示沒有用到索引

創建索引

#創建單索引

MariaDB [sunny]> create index age on students(age);

#創建多字段索引

MariaDB [sunny]> create index age_and_name on students(age,name);

查看索引,同一Key_name出現多次,表示是多字段的索引

MariaDB [sunny]> show index from students;

刪除索引

MariaDB [sunny]> drop index age_and_name on students;

索引創建完成後,再次用explain分析

MariaDB [sunny]> explain select * from students where age>90;

+------+-------------+----------+-------+---------------+------+---------+------+------+-----------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+------+-------------+----------+-------+---------------+------+---------+------+------+-----------------------+

| 1 | SIMPLE | students | range | age | age | 2 | NULL | 104 | Using index condition |

+------+-------------+----------+-------+---------------+------+---------+------+------+-----------------------+

1 row in set (0.00 sec)

以上結果表示select語句的查詢類型是simple,是在students表執行,獲取數據的類型是range,即排序,possible_keys表示可能用到的索引是age,key實際用到的索引就是age,key_len 只用到的索引項的前2個字節,載入104行數據,使用的是索引的條件查詢

MariaDB [sunny]> explain select name from students where age>90;

+------+-------------+----------+-------+------------------+--------------+---------+------+------+--------------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |

+------+-------------+----------+-------+------------------+--------------+---------+------+------+--------------------------+

| 1 | SIMPLE | students | range | age,age_and_name | age_and_name | 2 | NULL | 103 | Using where; Using index |

+------+-------------+----------+-------+------------------+--------------+---------+------+------+--------------------------+

1 row in set (0.01 sec)

以上結果possible_keys有兩個age,age_and_name索引,實際用到的索引key是age_and_name,extra中有Using index表示覆蓋索引這裏如果索引是index(name,age),這裏就用不上索引index(name,age),而是單純的age這個索引

關於explain更多介紹,建議查看文章:MySQL查詢優化之explain的深入解析:http://www.jb51.net/article/38357.htm


數據庫 之 MySQL的索引