1.1 什麼是索引mysql
索引就比如一本書的目錄,它會讓你更快的找到內容。linux
讓獲取的數據更有目的性,從而提升數據庫檢索數據的性能。web
分爲如下四種:redis
樹形結構(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的檢索速度。
數據庫有兩種查詢方式,一個全表掃描,條件匹配。一個是索引。
主鍵是特殊的索引,主鍵是索引,索引不必定是主鍵,索引能夠是多列,主鍵只能是一列。
基於特色的一些分類:
優先使用主鍵索引,查詢的時候還要基於主鍵索引進行查詢。
-- 添加一張表 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 是惟一鍵
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)
內容惟一,但不是主鍵
能夠統計一下有沒有重複值,用去重後的行數,和總行數作個比較,若是不同,說明有重複的值。
-- 添加telnum爲惟一鍵索引 alter table stu add UNIQUE key uni_tel(telnum); -- 統計總行數 select count(*) from webdb.t1;
-- telnum列去重以後還剩多少行 SELECT count(distinct telnum) from webdb.t1;
還能夠判斷是否是惟一索引,最簡單的方法是建一建試試,若是建不上 說明有重複的。
若是字符較長的時候,可使用前綴索引
-- 根據字段的前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)
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就正常。
數據庫索引的設計原則:
爲了使索引的使用效率更高,在建立索引時,必須考慮在哪些字段上建立索引和建立什麼類型的索引,那麼索引設計緣由又是怎樣的呢?
注意:若是重複值較多,能夠考慮採用聯合索引
2.爲常常須要排序、分組和聯合操做的字段創建索引
3.爲常做爲查詢條件的字段創建索引
4.儘可能使用前綴來索引
――――以上重點關注――――如下是能保護則保證的―――――
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不在第一條件列的狀況不走索引