一、索引的底層數據結構與算法
1、什么是索引?
索引是幫助MySQL高效獲取數據的排好序的數據結構。
2、索引的數據結構
-
B+Tree(B-Tree變種)
- 非葉子節點不存儲data,只存儲索引(冗余),可以放更多的索引
-
葉子節點包含所有索引字段
-
葉子節點用指針連接,提高區間訪問的性能
-
MyISAM索引文件和數據文件是分離的(非聚集)
-
InnoDB索引實現(聚集)
- 表數據文件本身就是按B+Tree組織的一個索引結構文件
-
葉節點包含了完整的數據記錄
- 建議InnoDB表必須建主鍵,並且推薦使用整型的自增主鍵
- 為什么非主鍵索引結構葉子節點存儲的是主鍵值?(一致性和節省存儲空間)
-
聯合索引數據結構
二、Explain工具使用
1、explain?
在 select 語句之前增加 explain 關鍵字,MySQL 會在查詢上設置一個標記,執行查詢會返回執行計划的信息,而不是執行這條SQL
-
explain extended+show warnings:會在 explain 的基礎上額外提供一些查詢優化的信息。緊隨其后通過 show warnings 命令可以得到優化后的查詢語句
-
explain partitions:相比 explain 多了個 partitions 字段,如果查詢是基於分區表的話,會顯示查詢將訪問的分區。
2、explain中的列
-
id:
有幾個 select 就有幾個id,並且id的順序是按 select 出現的順序增長的。id列越大執行優先級越高,id相同則從上往下執行,id為NULL最后執行。
-
select_type:
對應行是簡單還是復雜的查詢
-
-
simple:簡單查詢。查詢不包含子查詢和union
-
primary:復雜查詢中最外層的 select
-
subquery:包含在 select 中的子查詢(不在 from 子句中)
-
derived:包含在 from 子句中的子查詢。MySQL會將結果存放在一個臨時表中,也稱為派生表(derived的英文含義)
-
union:在 union 中的第二個和隨后的 select
-
-
table這一列表示 explain 的一行正在訪問哪個表。
-
type:
-
system > const > eq_ref > ref > range > index > ALL,一般來說要保證達到range級別
-
- const, system:mysql能對查詢的某部分進行優化並將其轉化成一個常量(可以看show warnings 的結果)。
-
eq_ref:primary key 或 unique key 索引的所有部分被連接使用 ,最多只會返回一條符合條件的記錄。
-
ref:相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前綴,索引要和某個值相比較,可能會找到多個符合條件的行。
-
range:范圍掃描通常出現在 in(), between ,> ,<, >= 等操作中。使用一個索引來檢索給定范圍的行。
-
index:掃描全索引就能拿到結果,一般是掃描某個二級索引,這種掃描不會從索引樹根節點開始快速查找,而是直接對二級索引的葉子節點遍歷和掃描,速度還是比較慢的,這種查詢一般為使用覆蓋索引,二級索引一般比較小,所以這種通常比ALL快一些。
-
ALL:即全表掃描,掃描你的聚簇索引的所有葉子節點。通常情況下這需要增加索引來進行優化了。
- possible_keys:顯示查詢可能使用哪些索引來查找。
- key:實際采用哪個索引來優化對該表的訪問。
- key_len:通過這個值可以算出具體使用了索引中的哪些列。
-
- 字符串:char(n):3n , varchar(n): 3n + 2 ,加的2字節用來存儲字符串長度,因為varchar是變長字符串
-
- 數值類型:tinyint:1 smallint:2 int:4 bigint:8字節
-
- 時間類型:date:3 timestamp:4 datetime:8
- ref:這一列顯示了在key列記錄的索引中,表查找值所用到的列或常量,常見的有:const(常量),字段名(例:film.id)
- rows:這一列是mysql估計要讀取並檢測的行數,注意這個不是結果集里的行數。
- Extra:這一列展示的是額外信息。常見的重要值如下:
-
Using index:使用覆蓋索引
-
Using where:使用 where 語句來處理結果,並且查詢的列未被索引覆蓋
-
Using index condition:查詢的列不完全被索引覆蓋,where條件中是一個前導列的范圍;
-
Using temporary:mysql需要創建一張臨時表來處理查詢。出現這種情況一般是要進行優化的,首先是想到用索引來優化。
-
Using filesort:將用外部排序而不是索引排序,數據較小時從內存排序,否則需要在磁盤完成排序。這種情況下一般也是要考慮使用索引來優化的。
-
Select tables optimized away:使用某些聚合函數(比如 max、min)來訪問存在索引的某個字段是
3、優化建議
三、bin-log歸檔
1、如果誤刪了數據庫,可以使用bin-log進行歸檔
- Binlog在MySql的Server層實現
- bin-log為邏輯日志,記錄每條語句的原始邏輯
- bin-log不限大小,追加寫入,不會覆蓋以前日志
2、開啟bin-log
#配置開啟binlog,my.conf log‐bin=/usr/local/mysql/data/binlog/mysql‐bin #注意5.7以及更高版本需要配置本項: server‐id=123454(自定義,保證唯一性); #binlog格式,有3種statement,row,mixed binlog‐format=ROW #表示每1次執行寫入就與硬盤同步,會影響性能,為0時表示,事務提交時mysql不做刷盤操作,由系統決定 sync‐binlog=1
bin-log 命令
1 mysql> show variables like '%log_bin%'; 查看bin‐log是否開啟 2 mysql> flush logs; 會多一個最新的bin‐log日志 3 mysql> show master status; 查看最后一個bin‐log日志的相關信息 4 mysql> reset master; 清空所有的bin‐log日志
查看bin-log內容
1 mysql> /usr/local/mysql/bin/mysqlbinlog ‐‐no‐defaults /usr/local/mysql/data/binlog/mysql‐bin. 000001 查看binlog內容
數據歸檔操作
1 從bin‐log恢復數據 2 恢復全部數據 3 /usr/local/mysql/bin/mysqlbinlog ‐‐no‐defaults /usr/local/mysql/data/binlog/mysql‐bin.000001 |mysql ‐uroot ‐p tuling(數據庫名) 4 恢復指定位置數據 5 /usr/local/mysql/bin/mysqlbinlog ‐‐no‐defaults ‐‐start‐position="408" ‐‐stop‐position="731" /usr/local/mysql/data/binlog/mysql‐bin.000001 |mysql ‐uroot ‐p tuling(數據庫) 6 恢復指定時間段數據 7 /usr/local/mysql/bin/mysqlbinlog ‐‐no‐defaults /usr/local/mysql/data/binlog/mysql‐bin.000001 ‐‐stop‐date= "2018‐03‐02 12:00:00" ‐‐start‐date= "2019‐03‐02 11:55:00"|mysql ‐uroot ‐p test(數 據庫)
四、性能調優
1、索引設計原則
1、代碼先行,索引后上
2、聯合索引盡量覆蓋條件
3、不要在小基數字段上建立索引
4、長字符串我們可以采用前綴索引
5、where與order by沖突時優先where
6、基於慢sql查詢做優化
2、索引下推(like'Lilei%')
可以在索引遍歷過程中,對索引中包含的所有字段先做判斷,過濾掉不符合條件的記錄之后再回表,可以有效的減少回表次數。
為什么范圍查找Mysql沒有用索引下推優化?
是Mysql認為范圍查找過濾的結果集過大,like KK% 在絕大多數情況來看,過濾后的結果集比較小,所以這里Mysql選擇給 likeKK% 用了索引下推優化,當然這也不是絕對的,有時like KK% 也不一定就會走索引下推。
3、Order by與Group by優化
1、優化例子
- 利用最左前綴法則:中間字段不能斷,因此查詢用到了name索引,從key_len=74也能看出,age索引列用在排序過程中,因為Extra字段里沒有using filesort
-
從explain的執行結果來看:key_len=74,查詢使用了name索引,由於用了position進行排序,跳過了age,出現了Using filesort。
-
查找只用到索引name,age和position用於排序,無Using filesort。
- 和Case 3中explain的執行結果一樣,但是出現了Using filesort,因為索引的創建順序為name,age,position,但是排序的時候age和position顛倒位置了。
- 與Case 4對比,在Extra中並未出現Using filesort,因為age為常量,在排序中被優化,所以索引未顛倒,不會出現Using filesort
- 雖然排序的字段列與索引順序一樣,且order by默認升序,這里position desc變成了降序,導致與索引的排序方式不同,從而產生Using filesort。Mysql8以上版本有降序索引可以支持該種查詢方式。
-
對於排序來說,多個相等條件也是范圍查詢
-
可以用覆蓋索引優化
2、優化總結
4、分頁查詢優化
很多分頁查詢的sql實現 ,看似只查詢了 10 條記錄,實際這條 SQL 是先讀取 10010條記錄,然后拋棄前 10000 條記錄,然后讀到后面 10 條想要的數據。因此要查詢一張大表比較靠后的數據,執行效率是非常低的。
mysql> select * from employees limit 10000,10;
常見技巧:
1、根據自增且連續的主鍵排序的分頁查詢

滿足以下兩個條件:
-
- 主鍵自增且連續
-
- 果是按照主鍵排序的
2、根據非主鍵字段排序的分頁查詢
select * from employees ORDER BY name limit 90000,5; (沒有使用name索引) select * from employees e inner join (select id from employees order by name limit 90000,5) ed on e.id = ed.id; (使用了索引,並且時間減少了一半以上)
3、Join關聯查詢優化
3.1 標關聯常見的兩種算法
-
-
- 嵌套循環連接 Nested-Loop Join(NLJ) 算法 (走索引)
一次一行循環地從第一張表(稱為驅動表)中讀取行,在這行數據中取到關聯字段,根據關聯字段在另一張表(被驅動表)里取出滿足條件的行,然后取出兩張表的結果合集。 - Block Nested-Loop Join 算法(不走索引)
把驅動表的數據讀入到 join_buffer 中,然后掃描被驅動表,把被驅動表每一行取出來跟 join_buffer 中的數據做對比。對於關聯sql的優化關聯字段加索引:讓mysql做join操作時盡量選擇NLJ算法小表驅動大表:寫多表連接sql時如果明確知道哪張表是小表可以用straight_join寫法固定連接驅動方式,省去mysql優化器自己判斷的時間
- 嵌套循環連接 Nested-Loop Join(NLJ) 算法 (走索引)
-
3.2 in和exsits優化
select * from A where id in (select id from B)
in:當B表的數據集小於A表的數據集時,in優於exists
exists:當A表的數據集小於B表的數據集時,exists優於in
3.3 count(*)查詢優化
5、阿里Mysql規范解讀
1、數值類型

2、日期和時間
3、字符串

優化建議
2、事務的隔離級別
3、鎖
結論1
鎖優化建議