1. 程式人生 > 實用技巧 >「MySQL系列」索引設計原則、索引失效場景、limit 、order by、group by 等常見場景優化

「MySQL系列」索引設計原則、索引失效場景、limit 、order by、group by 等常見場景優化

索引失效有哪些?limit 、order by、group by 等常見場景優化。

一 索引使用

1.1 概述

1. 定義
索引幫助MySQL高效獲取資料的資料結構(按照一定規則)。
2. 定義解釋
MySQL在儲存資料之外,資料庫系統還維護者滿足特定查詢演算法的 資料結構,這些資料結構以某種方式引用(指向)資料, 這樣就 可以在這些資料結構上實現高階查詢演算法,這種資料結構就是索引。
3. 優缺點

優點
提高資料檢索效率,降低資料庫IO成本。通過索引對資料進
行排序降低資料排序成本,降低CPU消耗。

缺點
實際上索引也是一張表,該表中儲存了主鍵與索引欄位,並指向實體類的記錄,所以索引列也是要佔用空間的。更新表時,MySQL 不
僅要儲存資料,還要儲存一下索引檔案每次更新添加了索引列的字
段,都會調整因為更新所帶來的鍵值變化後的索引資訊。

1.2 索引結構(InnoDB)

MySQL資料庫中預設的儲存引擎InnoDB的索引結構為B+樹,而根據 葉子節點的記憶體儲存不同,索引型別分為主鍵索引和非主鍵索引。
1. 主鍵索引(聚簇索引)
主鍵索引的葉子節點儲存的是整行資料,其結構如下:
2. 非主鍵索引(二級索引或輔助索引)
而非主鍵索引的葉子節點內容儲存時的主鍵的值,其結構如下:

1.3 索引使用規則

沒有建立索引,執行計劃如下
建立索引

createindexidx_seller_name_status_addressontb_seller(name,status,seller);

1. 全值匹配,對索引所有列都制定具體值

explainselect*fromtb_sellerwherename='小米科技'andstatus='1'and
address='北京市';


2. 最左字首法制

違背最左法則,索引失效
如果符合最左法則,但是出現跳躍某一列,只有最左列索引生效:

3. 範圍查詢右邊的列,不能使用索引

根據前面的兩個欄位name,status查詢是走索引的,但是最後一 個條件address 沒有用到索引。
4. 索引列上進行運算操作,索引失效

5. 字串不加單引號,造成索引失效

由於,在查詢是,沒有對字串加單引號,MySQL的查詢優化器, 會自動的進行型別轉換,造成索引失效。
6. 用or分割開的條件
示例,name欄位是索引列 , 而createtime不是索引列,中間是 or進行連線是不走索引的 :

explainselect*fromtb_sellerwherename='黑馬程式設計師'orcreatetime='2088-01-0112:00:00'\G;


7. 以%開頭的Like模糊查詢,索引失效。

解決方案

8. 如果MySQL評估使用索引比全表更慢,則不使用索引

9. is NULL,is NOT NULL有時索引失效。

10. in,not in有時索引失效

11. 儘量使用覆蓋索引,避免select
儘量使用覆蓋索引(只訪問索引的查詢(索引列完全包含查詢列)),減少select。
如果查詢列,超出索引列,也會降低效能。

using index :使用覆蓋索引的時候就會出現
usingwhere:在查詢使用索引的情況下,需要回表去查詢所需的資料
using index condition:查詢使用了索引,但是需要回表查詢資料
usingindex;usingwhere:查詢使用了索引,但是需要的資料都在索引列中能找到,所以不需要
回表查詢資料

1.4 索引設計原則

索引的設計可以遵循一些已有的原則,建立索引的時候請儘量考 慮符合這些原則,便於提升索引的使用效率,更高效的使用索引。

對查詢頻次較高,且資料量比較大的表建立索引。

索引欄位的選擇,最佳候選列應當從where子句的條件中提取,如
where子句中的組合比較多,那麼應當挑選最常用、過濾效果最
好的列的組合。

使用唯一索引,區分度越高,使用索引的效率越高。

索引可以有效的提升查詢資料的效率,但索引數量不是多多益
善,索引越多,維護索引的代價自然也就水漲船高。對於插入、
更新、刪除等DML操作比較頻繁的表來說,索引過多,會引入相當
高的維護代價,降低DML操作的效率,增加相應操作的時間消耗。
另外索引過多的話,MySQL也會犯選擇困難病,雖然最終仍然會找
到一個可用的索引,但無疑提高了選擇的代價。

使用短索引,索引建立之後也是使用硬碟來儲存的,因此提升索
引訪問的I/O效率,也可以提升總體的訪問效率。假如構成索引的
欄位總長度比較短,那麼在給定大小的儲存塊內可以儲存更多的
索引值,相應的可以有效的提升MySQL訪問索引的I/O效率。

利用最左字首,N個列組合而成的組合索引,那麼相當於是建立了
N個索引,如果查詢時where子句中使用了組成該索引的前幾個字
段,那麼這條查詢SQL可以利用組合索引來提升查詢效率。

二 常見SQL優化

2.1 資料庫準備

1. sql

CREATETABLE`emp`(
`id`int(11)NOTNULLAUTO_INCREMENT,
`name`varchar(100)NOTNULL,
`age`int(3)NOTNULL,
`salary`int(11)DEFAULTNULL,
PRIMARYKEY(`id`)
)ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;
insertinto`emp`(`id`,`name`,`age`,`salary`)values('1','Tom','25','2300');
insertinto`emp`(`id`,`name`,`age`,`salary`)
values('2','Jerry','30','3500');
insertinto`emp`(`id`,`name`,`age`,`salary`)
values('3','Luci','25','2800');
insertinto`emp`(`id`,`name`,`age`,`salary`)values('4','Jay','36','3500');
insertinto`emp`(`id`,`name`,`age`,`salary`)
values('5','Tom2','21','2200');
insertinto`emp`(`id`,`name`,`age`,`salary`)
values('6','Jerry2','31','3300');
insertinto`emp`(`id`,`name`,`age`,`salary`)
values('7','Luci2','26','2700');
insertinto`emp`(`id`,`name`,`age`,`salary`)
values('8','Jay2','33','3500');
insertinto`emp`(`id`,`name`,`age`,`salary`)
values('9','Tom3','23','2400');
insertinto`emp`(`id`,`name`,`age`,`salary`)
values('10','Jerry3','32','3100');
insertinto`emp`(`id`,`name`,`age`,`salary`)
values('11','Luci3','26','2900');
insertinto`emp`(`id`,`name`,`age`,`salary`)
values('12','Jay3','37','4500');
createindexidx_emp_age_salaryonemp(age,salary);

2.2 order by優化

1. filesort 排序
第一種是通過對返回資料進行排序,也就是通常說的 filesort排 序,所有不是通過索引直接返回排序結果的排序都叫 FileSort排 序。

2. using index
第二種通過有序索引順序掃描直接返回有序資料,這種情況即為 using index,不需要額外排序,操作效率高。
多欄位排序

瞭解了MySQL的排序方式,優化目標就清晰了:儘量減少額外的排 序,通過索引直接返回有序資料。where 條件和Order by 使用 相同的索引,並且Order By 的順序和索引順序相同, 並且 Order by 的欄位都是升序,或者都是降序。否則肯定需要額外的 操作,這樣就會出現FileSort。
3. 對上面兩種進行優化
通過建立合適的索引,能夠減少 Filesort 的出現,但是在某些 情況下,條件限制不能讓Filesort消失,那就需要加快Filesort 的排序操作。對於Filesort , MySQL 現在採用的是一次掃描算 法:一次性取出滿足條件的所有欄位,然後在排序區 sortbuffer 中排序後直接輸出結果集。排序時記憶體開銷較大,但是排序效率 比兩次掃描演算法要高。

MySQL 通過比較系統變數 max_length_for_sort_data 的大小 和Query語句取出的欄位總大小, 來判定是否那種排序演算法,如 果max_length_for_sort_data 更大,那麼使用第二種優化之後 的演算法;否則使用第一種。

可以適當提高 sort_buffer_size max_length_for_sort_data 系統變數,來增大排序區的大小,提高排序的效率。

2.3 group by優化

由於GROUP BY 實際上也同樣會進行排序操作,而且與ORDER BY 相比,GROUP BY 主要只是多了排序之後的分組操作。當然,如果 在分組的時候還使用了其他的一些聚合函式,那麼還需要一些聚 合函式的計算。所以,在GROUP BY 的實現過程中,與 ORDER BY 一樣也可以利用到索引。

如果查詢包含 group by 但是使用者想要避免排序結果的消耗, 則 可以執行order by null 禁止排序。如下 :

dropindexidx_emp_age_salaryonemp;
explainselectage,count(*)fromempgroupbyage;


優化後

explainselectage,count(*)fromempgroupbyageorderbynull;


從上面的例子可以看出,第一個SQL語句需要進行"filesort",而 第二個SQL由於order by null 不需要進行 "filesort", 而上 文提過Filesort往往非常耗費時間。

建立索引

createindexidx_emp_age_salaryonemp(age,salary);

2.4 limit優化

一般分頁查詢時,通過建立覆蓋索引能夠比較好地提高效能。一 個常見又非常頭疼的問題就是 limit5000000,10 ,此時需要 MySQL排序前5000010 記錄,僅僅返回5000000 - 5000010 的記 錄,其他記錄丟棄,查詢排序的代價非常大 。

limit分頁操作, 越往後, 效能越低 :
優化方案

select*fromtb_skut,(selectidfromtb_skuorderbyidlimit9000000,1)awheret.id=a.id;

2.5 count優化

在很多的業務系統中,都需要考慮進行分頁操作,但是當我們執 行分頁操作時,都需要進行一次count操作,求取總記錄數,如果 資料庫表的資料量大,在InnoDB引擎中,執行count操作的效能是 比較低的,需要遍歷全表資料,對計數進行累加。

優化方案

①. 在大資料量的查詢中,只查詢資料,而不展示總記錄數;
②. 通過快取redis維護一個表的計數,來記錄資料庫表的總記錄數,在執行插入/刪除時,需要動態更新;
③. 在資料庫表中定義一個大資料量的計數表,在執行插入/刪除時,需要動態更新。

2.6 大批量插入優化

1. 環境準備

CREATETABLE`tb_user`(
`id`INT(11)NOTNULLAUTO_INCREMENT,
`username`VARCHAR(50)NOTNULL,
`password`VARCHAR(50)NOTNULL,
`name`VARCHAR(20)NOTNULL,
`birthday`DATEDEFAULTNULL,
`sex`CHAR(1)DEFAULTNULL,
PRIMARYKEY(`id`),
UNIQUEKEY`unique_user_username`(`username`)
)ENGINE=INNODBDEFAULTCHARSET=utf8;

當使用 load 命令匯入資料的時候,適當的設定可以提高匯入的效率。
對於InnoDB 型別的表,有以下幾種方式可以提高匯入的效率:
主鍵順序插入
因為InnoDB型別的表是按照主鍵的順序儲存的,所以將匯入的數 據按照主鍵的順序排列,可以有效的提高匯入資料的效率。如果 InnoDB表沒有主鍵,那麼系統會自動預設建立一個內部列作為主 鍵,所以如果可以給表建立一個主鍵,將可以利用這點,來提高 匯入資料的效率。

指令碼檔案介紹:
sql1.log---->主鍵有序
sql2.log---->主鍵無序

插入ID順序排列資料:

loaddatalocalinfile'/root/sql1.log'intotable`tb_user`fieldsterminatedby','linesterminatedby'\n';


插入ID無序排列資料:

關閉唯一性校驗
在匯入資料前執行 SET UNIQUE_CHECKS=0,關閉唯一性校驗,在 匯入結束後執行 SET UNIQUE_CHECKS=1,恢復唯一性校驗,可以 提高匯入的效率。

手動提交事務
如果應用使用自動提交的方式,建議在匯入前執行 SET AUTOCOMMIT=0,關閉自動提交,匯入結束後再執行 SET AUTOCOMMIT=1,開啟自動提交,也可以提高匯入的效率。

END


●「Netty系列」使用Netty開發,踩坑到解決全過程(附解決方案原始碼)

●「MySQL系列」分析Sql執行時間及查詢執行計劃(附資料庫和一千萬資料)

●快取僅僅是使用Redis?看看高併發系統快取體系架構(快取體系架構一)

●一文入門Netty(Netty一)


微信公眾號花花與Java識別二維碼 關注我們

來源:鄂爾多斯SEO