MySQL索引(九)

1、索引介紹

1.1 什麼是索引mysql

索引就比如一本書的目錄,它會讓你更快的找到內容。linux

讓獲取的數據更有目的性,從而提升數據庫檢索數據的性能。web

分爲如下四種:redis

  • BTREE:B+樹索引(基本上都是使用此索引)
  • HASH:HASH索引
  • FULLTEXT:全文索引
  • RTREE:R樹索引

樹形結構(B樹:B樹、B+樹、B*樹),算法

B樹索引由多個層次構成:‘根’,‘枝’,‘葉’,它創建在表的列上sql

stu(id, name, age)數據庫

假如說,在id列上建索引服務器

  A. 對id列的值,進行自動排序,把這些值有規律的存放到各個葉子節點運維

  B. 而且葉子節點還會存儲整行數據的指針信息elasticsearch

  C. 生成上層枝節點,存儲每一個對應葉子節點最小值和葉子節點指針

  D. 生成根節點,存儲每一個枝節點的最小值以及對應的存儲指針

  以上是B樹索引的基本構成

  E. 對於B+樹索引結構,對於範圍查詢有了更好的優化,葉子節點還會記錄相鄰葉子節點指針

  F. 對於B*樹索引結構,枝節點還會記錄相領枝節點的指針狀況

B+樹圖:

1.2 主鍵和索引的區別

索引:索引比如是一本書的目錄,能夠快速的經過頁碼找到你須要的那一頁。唯一地標識一行。
主鍵:作爲數據庫表惟一行標識,做爲一個能夠被外鍵有效引用的對象。
索引是一種特殊的文件(InnoDB數據表上的索引是表空間的一個組成部分),它們包含着對數據表裏全部記錄的引用指針。索引能夠大大提升MySQL的檢索速度。
數據庫有兩種查詢方式,一個全表掃描,條件匹配。一個是索引。
主鍵是特殊的索引,主鍵是索引,索引不必定是主鍵,索引能夠是多列,主鍵只能是一列。

 2、索引執行計劃管理

基於特色的一些分類:

  1. 主鍵索引(彙集索引 cluseter indexes):parmary key(建立主鍵後自動生成的,最符合B+ 樹的)
  2. 惟一鍵:惟一鍵索引(都是惟一值的列)
  3. 普通鍵:輔助索引(sec indexex)

優先使用主鍵索引,查詢的時候還要基於主鍵索引進行查詢。

3、添加、查詢、刪除索引

-- 添加一張表
mysql> create table stu (id int not null auto_increment primary key,name varchar(20),age tinyint,gender enum('m','f'),telnum varchar(12),qq varchar(20));
Query OK, 0 rows affected (0.02 sec)

-- 把name列設置爲普通索引,idx_name爲key的名字
mysql> alter table stu add index idx_name(name);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

-- 查詢索引
mysql> desc stu;
+--------+---------------+------+-----+---------+----------------+
| Field  | Type          | Null | Key | Default | Extra          |
+--------+---------------+------+-----+---------+----------------+
| id     | int(11)       | NO   | PRI | NULL    | auto_increment |
| name   | varchar(20)   | YES  | MUL | NULL    |                |
| age    | tinyint(4)    | YES  |     | NULL    |                |
| gender | enum('m','f') | YES  |     | NULL    |                |
| telnum | varchar(12)   | YES  |     | NULL    |                |
| qq     | varchar(20)   | YES  |     | NULL    |                |
+--------+---------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)

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

mysql> show index from stu\G
*************************** 1. row ***************************
        Table: stu
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: id
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: 
   Index_type: BTREE
      Comment: 
Index_comment: 
*************************** 2. row ***************************
        Table: stu
   Non_unique: 1
     Key_name: idx_name
 Seq_in_index: 1
  Column_name: name
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment: 
Index_comment: 
2 rows in set (0.00 sec)

-- 刪除索引
mysql> alter table stu drop key idx_name;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

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

-- key就是索引的意思,PRI就是主鍵,MUL就是普通的索引,UNQ、UNI 是惟一鍵

4、建立表的時候建立自增主鍵

CREATE TABLE `stu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `age` tinyint(4) DEFAULT NULL,
  `gender` enum('m','f') DEFAULT NULL,
  `telnum` varchar(12) DEFAULT NULL,
  `qq` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

若是當時沒有建立,後面能夠增長

mysql> CREATE TABLE `stu_test` (
    ->   `id` int(11) NOT NULL,
    ->   `name` varchar(20) DEFAULT NULL
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.23 sec)

mysql> desc stu_test;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   |     | NULL    |       |
| name  | varchar(20) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.01 sec)

mysql> alter table stu_test change id id int(11) primary key not null auto_increment;
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc stu_test;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(20) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

5、惟一索引

內容惟一,但不是主鍵

能夠統計一下有沒有重複值,用去重後的行數,和總行數作個比較,若是不同,說明有重複的值。

-- 添加telnum爲惟一鍵索引
alter table stu add UNIQUE key uni_tel(telnum);

-- 統計總行數
select count(*) from webdb.t1;
-- telnum列去重以後還剩多少行 SELECT count(distinct telnum) from webdb.t1;

還能夠判斷是否是惟一索引,最簡單的方法是建一建試試,若是建不上 說明有重複的。

 6、前綴索引和聯合索引

若是字符較長的時候,可使用前綴索引

-- 根據字段的前10個字符創建索引,名稱爲index_note
alter table stu add note varchar(200);
alter table stu add index index_note(note(10));

聯合索引

多個字段創建一個索引

條件:a(女生) and b(身高165) and c(身材好)

Index(a,b,c)

特色:前綴生效特性。

a,ab,abc,ac  能夠走索引或者部分走索引

原則:把最經常使用來做爲條件查詢的列放在前面。

mysql> alter table stu add money int;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> alter table stu add index idx_dup(gender,age,money);
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> show index from stu;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| stu   |          0 | PRIMARY  |            1 | id          | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| stu   |          0 | uni_tel  |            1 | telnum      | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| stu   |          1 | idx_dup  |            1 | gender      | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| stu   |          1 | idx_dup  |            2 | age         | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| stu   |          1 | idx_dup  |            3 | money       | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
5 rows in set (0.00 sec)

7、explain 命令分析

MySQL中的執行計劃,只分爲兩種。都是優化器決定的

全表掃描:

  通常在線上業務系統,要避免全表掃描

索引掃描:

  將要獲取的數據,變得更有目的性。

經過explain命令來 獲取優化器選擇後的執行計劃,並不輸出後面的語句結果。

mysql> explain select id,name from t1 where name='andy';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | ALL  | NULL          | NULL | NULL    | NULL |    2 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

-- type 表示的是使用的是全表掃描仍是索引掃描
-- type 類型以下:ALL、index、range、ref、eq_ref、const、system、Null
-- 從左到右,性能愈來愈好,咱們在使用索引是,最底應達到range
-- key_len值越小越好
-- rows值越小越好

ALL 全表掃描

index:Full index scan,index與ALL區別爲index類型只遍歷索引樹

range:索引範圍掃描,對索引的掃描開始於某一點,返回匹配值域的行。顯而易見的索引範圍掃描是帶有between或者where子句裏帶有<,>查詢。

where條件後 > < >= <= in or between and   like 'm%'

 不等因而不走索引的!= 、<>、like '%m%'

 此句性能略差

可改寫爲

ref:使用非惟一索引掃描或者惟一索引的前綴掃描,返回匹配某個單獨值的記錄行

 eq_ref:相似ref,區別就在使用的索引是惟一索引,對於每一個索引鍵值,表中只有一條記錄匹配,簡單來講,就是多表鏈接中使用primary key或者unique key做爲關鍵條件。

A join B

on A.sid=b.sid

const、system:當MySQL對查詢某部分進行優化,並轉換爲一個常量時,使用這些類方法訪問。

如:將主鍵置於where列表中,MySQL就能將該查詢轉換爲一個常量。

NULL:MySQL在優化過程當中分解語句,執行時甚至不用訪問表過索引。

例如:從一個索引列裏選取最小值可經過單獨索引查找完成

若是出現以上附加信息,請檢查order by,group by,distince,join條件列上有沒有合理的索引。(聯合索引)

單列索引也不會避免filesort的出現

若是想優化,必須建立聯合索引。

會發現,下面有兩個索引,最後走的新建立的dup_codepogo

可是基於countcode有兩個索引,須要刪除一個,不然會影響優化器的算法。

Possible_key只有一個了,裏面的extra正常了,只要不是filesort就正常。

 8、創建索引的原則(運維規範)

數據庫索引的設計原則:

爲了使索引的使用效率更高,在建立索引時,必須考慮在哪些字段上建立索引和建立什麼類型的索引,那麼索引設計緣由又是怎樣的呢?

  1. 選擇惟一性索引
  • 惟一性索引的值是惟一的,能夠更快速的經過該索引來肯定某條記錄
  • 例如:學生表中學號是具備惟一性的字段。爲該字段創建惟一性索引能夠很快的肯定某個學生的信息,若是使用姓名的話,可能存在同名現象,從而下降查詢速度。
  • 主鍵索引和惟一索引,在查詢中使用的效率最高的。

  注意:若是重複值較多,能夠考慮採用聯合索引

   2.爲常常須要排序、分組和聯合操做的字段創建索引

  • 常常須要order by、group by、distinct和union等操做的字段,排序操做會浪費不少時間。
  • 若是爲其創建索引,能夠有效地避免排序操做。

   3.爲常做爲查詢條件的字段創建索引

  • 若是某個字段常常用來作查詢條件,那麼該字段查詢的速度會影響整個表的查詢速度。所以爲這樣的字段創建索引,能夠提升整個表的查詢速度。
  • 常常查詢
  • 列值的重複值少

   4.儘可能使用前綴來索引

  • 若是索引字段的值很長,最好使用值的前綴來索引。
  • 例如:TEXT和BLOG類型的字段,進行全文檢索會很浪費時間。若是隻檢索字段前面的若干字符,這樣能夠提升檢索速度。

――――以上重點關注――――如下是能保護則保證的―――――

  1. 限制索引的數目
  • 索引的數目不是越多越好。每一個索引都須要佔用磁盤空間,索引越多,須要佔用的磁盤就越大,修改表時,對索引的重構和和更新很麻煩。越多的索引,會使更新表變得很浪費時間

   2.刪除再也不使用,或者不多使用索引

  • 表中的數據被大量更新,或者數據的使用方式被改變後,原有的一些索引可能再也不須要。數據庫管理員應當按期找出這些索引,將它們刪除,從而減小索引對更新操做的影響。

――――不走索引的狀況---------(開發規範)

重點關注:

  1.沒有查詢條件,或者查詢條件沒有創建索引

-- 全表掃描
select * from t1;

-- 工具生成,和全表掃描是同樣的
select * from t1 where 1=1; 

  在線上業務數據庫中,特別是數據量比較大的表,是沒有全表掃描這種需求的。

  A.對用戶查看是很是痛苦的。

  B.對服務器來說是毀滅性的

  例外:數據處理分析的業務,通常也不用mysql了

select * from t1;  
-- SQL改寫成如下語句
-- 須要在price列上創建索引
select * from t1 ORDER BY price limit 10;

  2.查詢結果集是原表中的大部分數據,應該是25%以上。

  查詢的結果集,超過了總數行數25%,優化器以爲不必走索引了。

  若是業務容許,可使用limit控制

  怎麼改寫?

  結合業務判斷,有沒有更好的方式。若是沒有更好的改寫方案,儘可能不要在mysql存放這個數據了,放到redis中。

  3.索引自己失效,統計數據不真實

  索引有自我維護能力。

  對於表內容變化比較頻繁的狀況下,有可能會出現索引失敗。

  4.查詢條件使用函數在索引列上,或者對索引進行運算。運算符包括(+ - * / ! 等)

-- 錯誤的
select * from test where id-1=9;

--正確的
select * from test where id=10;

  5.隱式轉換致使索引失效,這一點應當引發重視,也是開發中常犯的錯誤。

  這樣會導航不索引失效,錯誤的例子

 

 

 

   隱式的把數字轉換成字符串

 

  6.<>  、not in 不走索引

 

  7.like '%a' 百分號在最前面不走索引

 

  %linux%類的搜索需求,可使用elasticsearch

  8. 單獨引用複合索引裏非第一位置的索引列。

  複合索引index(a,b,c)

  where a

  where a b

  where a b c

  保會走a的部分索引

  where a c

  where a c b

  不走索引的:

  任何where條件列a不在第一條件列的狀況不走索引

相關文章
相關標籤/搜索