一:MySQL索引與慢查詢優化
1.什么是索引?
簡單的理解為可以幫助你加快數據查詢速度的工具
也可以把索引比喻成書的目錄,它能讓你更快的找到自己想要的內容
2.索引類型分類介紹
#===========B+樹索引(等值查詢與范圍查詢都快)
二叉樹->平衡二叉樹->B樹->B+樹
#===========HASH索引(等值查詢快,范圍查詢慢)
將數據打散再去查詢
#===========FULLTEXT:全文索引 (只可以用在MyISAM引擎)
通過關鍵字的匹配來進行查詢,類似於like的模糊匹配
like + %在文本比較少時是合適的
但是對於大量的文本數據檢索會非常的慢
全文索引在大量的數據面前能比like快得多,但是准確度很低
百度在搜索文章的時候使用的就是全文索引,但更有可能是ES
RTREE: R樹索引
3.不同的存儲引擎支持的索引類型也不一樣
innDB存儲引擎
支持事務,支持行級別鎖定,支持 B-tree(默認)、Full-text 等索引,不支持 Hash 索引;
mylSAM存儲引擎
不支持事務,支持表級別鎖定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
Memory存儲引擎
不支持事務,支持表級別鎖定,支持 B-tree、Hash 等索引,不支持 Full-text 索引;
- 因為mysql默認的存儲引擎是innodb,而innodb存儲引擎的索引模型/結構是B+樹,所以我們着重介紹B+樹,那么大家最關注的問題來了:
B+樹索引到底是如何加速查詢的呢?
二:索引的數據結構
innodb存儲引擎默認的索引結構為B+樹,而B+樹是由二叉樹、平衡二叉樹、B樹再到B+樹一路演變過來的
1.二叉樹(每個節點只能分兩個叉)

2.數據結構(B樹)
b樹 :精確查詢 三次IO操作
案例:
select * from user where id=38
b樹 :范圍內查詢 九次IO操作
案例:
select * from where id > 38 and id < 73;
總結b樹:
b樹查詢的次數又b樹的層次決定

3.b+樹 范圍查詢 五次IO操作(葉節點指針)
b+樹 : 范圍查詢 五次IO操作(葉節點指針)
id > 38 and id < 73
指針分為兩個部分:
pdata : 前面存數據
pnext : 存下面一個指針的位置
總結b+樹:
b+樹依靠指針比b樹查詢速度更快,也是MySQ目前L默認使用的索引

4.b*樹(枝節點也添加了指針)
b*樹(枝節點也添加了指針)
指針的作用:
添加指針是為了加快范圍查詢的速度

5.總結(索引)
索引的作用:
索引就是為了提供數據的查詢速度
在計算機底層的表現形式就是一些數據結構(樹)
數據結構:
二叉樹 : 每個節點只能分兩個叉
b樹 : 枝節點和葉節點沒有指針
b+樹 : 葉節點添加指針
b*樹 : 枝節點添加了指針(葉節點也有)
指針添加的作用:
指針的添加主要是為了解決范圍查詢的問題
精確查找取決於樹的高度
索引的必要性:
將某個字段添加成索引就相當於依據該字段建立了一顆b+樹從而加快查詢速度
如果某個字段沒有添加索引 那么依據該字段查詢數據會非常的慢(一行行查找)
6.索引的分類
1.primary key
主鍵索引除了有加速查詢的效果之外 還具有一定的約束條件
2.unique key
唯一鍵索引 除了有加速查詢的效果之外 還具有一定的約束條件
3.index key
普通索引 只有加速查詢的效果 沒有額外約束
注意外鍵不是索引 它僅僅是用來創建表與表之間關系的
foreign key
三:操作索引
1.創建唯一索引需要提前排查是否有重復數據
select count(字段) from t1
select count(distinct(字段)) from t1
2.查看當前表內部索引值
show index from t1;

3.主鍵索引(指定索引)
alter table t1 add primary key pri_id(id); # 以id字段為索引
pri_id : 索引名<見名之意>
再次查看當前內部索引值
show index from t1\G;

4.查詢以id索引字段,此時加速查詢(如果使用name字段查詢,就還是一行一行查詢)
select * from t1 where id=3

5.唯一索引
alter table t1 add unique key uni_pwd(pwd)
1.測試使用唯一索引

6.報錯原因
使用唯一索引時,指定字段是唯一索引時,該字段如果有重復,使用唯一索引會報錯。
四:解決字段重復
1.創建唯一索引需要提前排查是否有重復數據
1.統計當前字段個數
select count(pwd) from t1

2.去重pwd字段重復 排除重復數據(進行對比是否有重復數據)
select count(distinct(pwd)) from t1

3.刪除重復數據
delete from t1 where id=4;
4.指定唯一索引(pwd字段索引)
alter table t1 add unique key uni_pwd(pwd);
5.查看當前所有索引
show index from t1\G;

6.普通索引(只能加速查詢,沒有其他約束條件)
alter table t1 add index idx_name(name)

7.刪除索引
alter table 表名 drop index 索引名;
8.前綴索引(屬於普通索引)
前綴索引的作用:
避免對大列建索引(數據很多情況),如果有就使用前綴索引
比如:
博客內容 百度搜索內容等
根據字段前N個字符建立索引
alter table t1 add index idx_name(name(10))
9.聯合索引(屬於普通索引)
聯合索引作用:
相親平台 搜索心儀對象的時候 《女,富婆,未婚,漂亮,1.69》
遵循:最左匹配原則
例:
where a.女生 and b.身高 and c.體重 and d.身材好
index(a.b.c)
特點: 前綴生效特性
a,ab,ac,abc,abcd 可以走索引或部分走索引
b bc bcd c d ba... 不走索引
10.創建聯合索引(前綴生效特性)
alter table t1 add index idx_all(id,name,pwd)

五:explain句式(全表掃描-索引掃描)
1.全表掃描與索引掃描區別
全表掃描(在explain語句結果中type為ALL)
不走索引 一行行查找數據 效率極低 生產環境下盡量不要書寫類似SQL
索引掃描(const)
走索引 加快數據查詢 建議書寫該類型SQL
注意:
生成過程中,MySQL在使用全表掃描時的性能是極差的,所有MySQL盡量避免出現全表掃描。
explain就是幫助我們查看SQL語句屬於那種掃描(全表掃描 還是 索引掃描)
2.explain命令使用格式:
explain select * from t1 where id=2;
3.使用explain驗證全表掃描存在

4.使用explain驗證索引掃描存在

5.什么時候出現全表掃描?
1.業務確實要獲取所有數據
2.不走索引導致的全盤掃描
3.沒索引
4.索引創建有問題
5.語句有問題
6.常見的索引掃描類型
1)index
2)range
3)ref
4)eq_ref
5)const
6)system
7)null
從上到下,性能從最差到最好,我們認為至少要達到range級別
7.索引掃描(內容解析)
index : index與ALL區別為index類型只遍歷索引樹
range : 索引范圍掃描,對索引的掃描開始於某一點,返回匹配值域的行。顯而易見的索引范圍掃描是帶有between或者where子句里帶有<,>查詢。<范圍>
案例演示:
mysql> alter table city add index idx_city(population);
mysql> explain select * from city where population>30000000;
ref : 使用非唯一索引掃描或者唯一索引的前綴掃描,返回匹配某個單獨值得記錄 行。<精確>
案例演示:
mysql> alter table city drop key idx_code;
mysql> explain select * from city where countrycode='chn';
mysql> explain select * from city where countrycode in ('CHN','USA');
mysql> explain select * from city where countrycode='CHN' union all select * from city where countrycode='USA';
eq_ref : 類似ref,但不加前綴,區別就在使用得索引是唯一索引,對於每個索引鍵值,表中只有一條記錄匹配,簡單來說,就是多表連接中使用primary key或者 unique key作為關聯條件A。
案例演示:
join B
on A.sid=B.sid
const,system : 當MySQL查詢某部分進行優化,並轉換為一個常量是,使用這些類型訪問。
案例演示:
mysql> explain select * from city where id=1000;
NULL : MySQL在優化過程中分解語句,執行時甚至不用訪問表或索引,例如從一個索引列里選取最小值可以通過單獨索引查找完成。
案例演示:
mysql> explain select * from city where id=1000000000000000000000000000;
8.在業務數據庫中,特別是數據量比較大的表,是沒有全表掃描這種需求。
1.對用戶查看時非常痛苦的
2.對服務器來講毀滅性的
3.SQL改寫成以下語句:
| #情況1 | |
|---|---|
| #全表掃描 | |
| select * from table; | |
| #需要在price列上建立索引 | |
| selec * from tab order by price limit 10; | |
| #情況2 | |
| #name列沒有索引 | |
| select * from table where name='zhangsan'; | |
| 1、換成有索引的列作為查詢條件 | |
| 2、將name列建立索引 |
六:不走索引情況(起碼記憶四條及以上)
1.沒有查詢條件,或者查詢條件沒有建立索引
全表掃描
select * from table;
select * from tab where 1=1;
2.查詢結果集是原表中的大部分數據(25%以上)有可能不走索引
mysql> explain select * from city where population>3000 order by population;
1)如果業務允許,可以使用limit控制。
2)結合業務判斷,有沒有更好的方式。如果沒有更好的改寫方案就盡量不要在mysql存放這個數據了,放到redis里面。
3.索引本身失效,統計數據不真實
索引有自我維護的能力。
對於表內容變化比較頻繁的情況下,有可能會出現索引失效。
重建索引就可以解決
4.查詢條件使用函數在索引列上或者對索引列進行運算,運算包括(+,-,*等)
例子:
錯誤的例子: select * from test where id-1=9;
正確的例子: select * from test where id=10;
5.隱式轉換導致索引失效.這一點應當引起重視.也是開發中經常會犯的錯誤
eg:字段是字符類型 查詢使用整型
| mysql> create table test (id int ,name varchar(20),telnum varchar(10)); | |
|---|---|
| mysql> insert into test values(1,'zs','110'),(2,'l4',120),(3,'w5',119),(4,'z4',112); | |
| mysql> explain select * from test where telnum=120; | |
| mysql> alter table test add index idx_tel(telnum); | |
| mysql> explain select * from test where telnum=120; | |
| mysql> explain select * from test where telnum=120; | |
| mysql> explain select * from test where telnum='120'; |
-
測試隱式轉換導致失效(類型轉錯成int類型)

-
糾正隱式轉換導致的失敗(傳入正確的 字符串類型)

6.<> ,not in 不走索引
單獨的>,<,in 有可能走,也有可能不走,和結果集有關,盡量結合業務添加limit、or或in盡量改成union
7.like "%_" 百分號在最前面不走
| #走range索引掃描 | |
|---|---|
| EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%'; | |
| #不走索引 | |
| EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '%110'; |
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 | |
| #走索引的情況測試 | |
| 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'; |
索引的創建會加快數據的查詢速度 但是一定程度會拖慢數據的插入和刪除速度。
