MySQL索引管理及執行計劃
第1章 索引介紹:
索引是對數據庫表中一列或者多了的值進行排序的一種結構,使用索引可以快速訪問數據庫表中的特定信息,如果想按特定職員的姓名來查找,則與他在表中搜索所有的行相比,索引有助於更快的獲取信息
索引的一個主要目的就是加快檢索表中的數據的方法,既能協助信息搜索者盡快找到符合限制條件的記錄ID的輔助數據結構
1.1 索引的類型介紹:
btree:B+樹索引 最為常用
hash:hash索引
fulltest:全文索引
rtree:r數索引
第2章 索引管理
2.1 B樹索引的分類:
主鍵索引:
數據庫表經常有一列或多列組合,其值唯一標識表中的每一行,該列稱為表的主鍵,在數據庫關系圖中為表定義主鍵將自動創建主鍵索引
唯一索引:
是不允許期中任何兩行具有相同索引值的索引,當現有數據中存在重復的鍵值時,大多數數據庫不允許將新創建的唯一索引與表一起保存,數據庫還可能防止添加將在表中創建重復鍵值的新數據
普通索引:
2.2 mysql中的約束索引:
2.2.1 主鍵索引:只能有一個主鍵
主鍵索引,列的內容是唯一值,高中學號
表創建的時候至少要有一個主鍵索引,最好和業務無關
2.2.2 普通索引:
加快查詢速度,工作中優化數據庫的關鍵
在合適的列上建立索引,讓數據查詢更加高效
2.2.3 唯一索引:
內容唯一,但不是關鍵
2.3 添加索引:
mysql> alter table people add index index_name(id); mysql> alter table stu add index ind_mul(id); mysql> create index inx on stu(gender);
2.4 刪除索引:
mysql> alter table stu drop index ind_mul;
2.5 查詢索引:
mysql> desc people; mysql> show index from people;
2.6 主鍵索引的設置:
1. 建表時就設置
2. 建表沒有指定,後期修改
2.6.1 唯一鍵索引的創建:
mysql> alter table stu add unique key ind(name);
2.6.2 聯合索引的創建:
mysql> alter table stu add index ind_id_name(id,name);
聯合索引的特點:a;ab;abc 可以走索引
b;ac;bc;c 不走索引
原則是把最常用來作為條件查詢的列放在前面
2.6.3 前綴索引的創建:
create index ind_name on test(name(8)); alter table test add index ind_name(name(8));
2.6.4 什麽情況下推薦創建索引呢?
where order by group by join on
2.7 索引的企業應用場景:
2.7.1 企業優化思路:
1. 把一個大的不使用索引的sql語句安好功能進行拆分
2. 長的sql語句無法使用索引,能不能變成兩條端的sql語句,讓它分別使用上索引
3. 對sql語句功能的拆分和修改
4. 減少爛sql
5. 由運維和開發交流確認,共同確定如何改,最終由dba執行
6. 制定開發流程
2.7.2 不適合建立索引的場景:
1. 唯一值少的裂傷不適合建立索引或者建立索引會導致效率低,例如性別列
2. 小表可以不創建索引,100條記錄
3. 對於數據倉庫,大量全表掃描的情況,建立索引範圍會慢
第3章 執行計劃獲取及分析
3.1 explain 調取執行計劃
查看是否走了索引:
mysql> explain select name from stu; +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | stu | index | NULL | ind | 63 | NULL | 2 | Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ mysql> explain select id from stu where id=1; +----+-------------+-------+------+---------------+---------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+---------+---------+-------+------+-------------+ | 1 | SIMPLE | stu | ref | ind_mul | ind_mul | 5 | const | 1 | Using index | +----+-------------+-------+------+---------------+---------+---------+-------+------+-------------+
all和index的類型,我們通常認為,索引的創建不夠合理,起碼達到range級別以上
3.2 type 訪問類型的種類:
3.2.1 ALL:
mysql將遍歷全表以紮到匹配的行
3.2.2 index:
索引範圍掃描,index與ALL類型之遍歷索引樹
3.2.3 range:
索引範圍掃描,對索引的掃描開始於某一點,返回匹配值域的行,這種索引範圍掃描是帶有between或者where子句裏帶有<>查詢,
3.2.4 ref:
使用非唯一索引掃描或者唯一索引的前綴掃描,返回匹配某個單獨值的記錄行
3.2.5 eq_ref:
類似ref區別就在於使用的索引是唯一索引,對於每個索引鍵值,表中只有一條記錄匹配,簡單來說,就是多表連接中使用primary key或者unique key 作為關聯條件
3.2.6 const/system:
當mysql對查詢某部分進行優化,並轉換為一個常亮時,使用這些類似訪問,如將主鍵置於where列表中,mysql就能將該查詢轉換為一個常量
3.2.7 null:
mysql在優化過程中分解語句,執行時甚至不用訪問表或索引,例如從一個索引列表裏選取最小值可以通過單獨索引查找完成
type類型從上到下,性能由差到好
3.3 查看表中唯一值的數量:
mysql> select count(distinct user,host) from mysql.user; +---------------------------+ | count(distinct user,host) | +---------------------------+ | 5 | +---------------------------+ 1 row in set (0.00 sec)
第4章 數據庫索引設計的原則:
為了使索引的使用效率更高,在創建索引時,必須考慮在哪些字段上創建索引和創建什麽類型的索引
4.1 索引設計重要原則:
4.1.1 選擇唯一性索引
唯一性索引的值是唯一的,可以更快速的通過該索引來確定某條記錄
例如,學生表中的學生號是具有唯一性的字段,為該字段建立唯一性索引可以很快的確定某個學生的信息,如果使用姓名的話,又可能會存在同名的現象,從而降低查詢速度
主鍵索引和唯一鍵索引,在查詢中是效率最高的
4.1.2 為經常需要排序,分組和聯合操作的字段建立索引
經常需要order by group by distinct和untion等操作的字段,排序操作會浪費很多時間,如果為其建立索引,可以有效的避免排序操作
4.1.3 為經常查詢條件的字段建立索引
如果某個字段上經常用來做查詢條件,那麽該字段的查詢速度回影響整個表的查詢速度,因此,為這樣的字段建立索引,可以提高整個表的查詢速度
4.1.4 盡量使用前綴來索引
如果索引字段的值很長,最好使用值的前綴來索引,例如:test和blog類型的字段,進行全文檢索會很浪費時間,如果只檢索字段的前面的若幹個字符,這樣可以提高檢索速度
4.1.5 限制索引的數目
索引的數目並不是越多越好,每個索引都需要占用磁盤空間,索引越多,需要的磁盤空間就越大,修改表時,對索引的重構和更新很麻煩,越多的索引,會使更新表變得很浪費時間
4.1.6 盡量使用數據量少的索引
如果索引的值很長,那麽查詢的速度必然會受到影響,例如:對弈char(100)類型的字段進行全文檢索需要的時間肯定要比對char(10)類型的字段需要的時間更多
4.1.7 刪除不在使用或者很少使用的索引
表中的數據被大量更新,或者數據的使用方式被改變後,原有的一些索引可能不在需要,數據庫管理員應當定期找出這些索引,並刪除,從而減少索引對更新操作的影響
4.2 索引開發的規範:
不走索引的情況:
4.2.1 沒有查詢條件,或者查詢條件沒有建立索引
在業務數據庫中,數據量大的是沒有必要進行全表掃描的,一定程度上講,全表掃描對用戶體驗就非常痛苦的,同時對服務器也是毀滅性的;又或者索引建立的但是查詢條件沒有where等條件
4.2.2 查詢結果集是原表中的大部分數據,達到了30%以上
查詢結果集超過了總行數的30%,就沒有必要走索引了
例如 :select * from test where id>500000;
如果業務允許的情況下,可以加上limit進行控制,如果因為業務的原因沒有辦法改寫方案,就可以放到redis中
4.2.3 索引本身失效,統計數據不真實
索引有自我維護的能力,對於表的變化內容比較頻繁的情況下,可能會出現索引失效
4.2.4 查詢條件使用函數在索引列上,或者對索引列進行運算,運算包括(+ - * /等)
盡量不要在查詢條件上進行運算,算好了在假如查詢條件中
4.2.5 隱式轉換導致索引失效,這一點應當引起重視,也是開發中經常遇到的錯誤
由於表的字段name定義為varchar(20),但是在查詢時把該字段作為number類型以where條件傳給數據庫,這樣會導致索引失效
例如:在查詢name的字段信息時,因為是字符串的類型,where就需要加上單引號,但是sql語句忘記加上,就會導致索引失效
select * from test where tu_mdn=13333333333; 正確的寫法: select * from test where tu_mdn=’13333333333’; 案例演示: mysql> alter table tab add index inx_tel(telnum); Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> desc tab; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | YES | | NULL | | | name | varchar(20) | YES | | NULL | | | telnum | varchar(20) | YES | MUL | NULL | | +--------+-------------+------+-----+---------+-------+ 3 rows in set (0.01 sec) mysql> select * from tab where telnum='1333333'; +------+------+---------+ | id | name | telnum | +------+------+---------+ | 1 | a | 1333333 | +------+------+---------+ 1 row in set (0.00 sec) mysql> select * from tab where telnum=1333333; +------+------+---------+ | id | name | telnum | +------+------+---------+ | 1 | a | 1333333 | +------+------+---------+ 1 row in set (0.00 sec) mysql> explain select * from tab where telnum='1333333'; +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ | 1 | SIMPLE | tab | ref | inx_tel | inx_tel | 63 | const | 1 | Using index condition | +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ 1 row in set (0.00 sec) mysql> explain select * from tab where telnum=1333333; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tab | ALL | inx_tel | NULL | NULL | NULL | 2 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from tab where telnum=1555555; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tab | ALL | inx_tel | NULL | NULL | NULL | 2 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from tab where telnum='1555555'; +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ | 1 | SIMPLE | tab | ref | inx_tel | inx_tel | 63 | const | 1 | Using index condition | +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ 1 row in set (0.00 sec)
4.2.6 <>,not in 不走索引
<> 的作用是不等於
mysql> select * from stu; +--------+------+------+------+------+---------+--------+------+-------+ | stu_id | id | name | QQ | age | tel_num | gender | addr | state | +--------+------+------+------+------+---------+--------+------+-------+ | NULL | 7 | NULL | NULL | NULL | NULL | NULL | NULL | 1 | | NULL | NULL | xiao | NULL | NULL | NULL | NULL | NULL | 1 | +--------+------+------+------+------+---------+--------+------+-------+ 2 rows in set (0.00 sec) mysql> select * from stu where id <> '7'; Empty set (0.00 sec)
單獨的>,<,in 有可能走,也有可能不走,和結果集有關,盡量結合業務添加limit
or或者in 盡量改寫成union
EXPLAIN SELECT * FROM teltab WHERE telnum IN ('110','119');
改寫成:
EXPLAIN SELECT * FROM teltab WHERE telnum='110' UNION ALL SELECT * FROM teltab WHERE telnum='119'
4.2.7 like “%_” 百分號在前面,不走索引
mysql> explain select * from stu where name like '%xiao'; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | stu | ALL | NULL | NULL | NULL | NULL | 2 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from stu where name like 'xiao%'; +----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+ | 1 | SIMPLE | stu | range | ind | ind | 63 | NULL | 1 | Using index condition | +----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+ 1 row in set (0.00 sec)
帶%號一類的搜索請求,可以使用elasticsearch
4.2.8 單獨引用聯合索引裏非第一位置的索引列
聯合索引案例:
CREATE TABLE t1 (id INT,NAME VARCHAR(20),age INT ,sex ENUM('m','f'),money INT); ALTER TABLE t1 ADD INDEX t1_idx(money,age,sex); DESC t1 SHOW INDEX FROM t1 (a,b,c) a ab abc ac bc b c -----------------------------
走索引:
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND age=30 AND sex='m'; EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND age=30 ; EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND sex='m'; ----部分走索引
不走索引:
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE age=20 EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE age=30 AND sex='m'; EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE sex='m';
4.2.9 blob和text類型的列只能創建前綴索引
4.2.10 mysql目前不支持函數索引
4.2.11 join語句中join條件字段類型不一致的時候mysql無法使用索引
MySQL索引管理及執行計劃