详解mysql什么时候不走索引
全值匹配我最愛,最左前綴要遵守;
帶頭大哥不能死,中間兄弟不能斷;
索引列上不計算,范圍之后全失效;
LIKE百分寫最右,覆蓋索引不寫 *;
不等空值還有or,索引失效要少用;
字符單引不可丟,SQL高級也不難 ;
索引的種類
眾所周知,索引類似于字典的目錄,可以提高查詢的效率。
索引從物理上可以分為:聚集索引,非聚集索引
從邏輯上可以分為:普通索引,唯一索引,主鍵索引,聯合索引,全文索引
索引優化策略
不要在索引列上進行運算或使用函數
在列上進行運算或使用函數會使索引失效,從而進行全表掃描。如下面例子在publish_time,id列上分別加上索引,publish_time為datetime類型,id為int類型
-- 全表掃描 select * from article where year(publish_time) < 2019 -- 走索引 select * from article where publish_time < '2019-01-01' -- 全表掃描 select * from article where id + 1 = 5 -- 走索引 select * from article where id = 4小心隱式類型轉換
假設id為varchar類型
-- 全表掃描 select * from article where id = 100 -- 走索引 select * from article where id = '100'為什么呢?
select * from article where id = 100 -- 等價于 select * from article where CAST(id AS signed int) = 100上一條規則說過,不要在索引列上使用函數,隱式類型轉換在索引字段上做了函數操作,因此會全表掃描
那么如果id是int,執行下面這個語句是否會導致全表掃描呢?
select * from article where id = '100'答案是會用到索引
前導模糊查詢不會使用索引
-- 全表掃描 select * from article where author like '%李'%李,%李%都會導致全表掃描,非前導模糊查詢可以使用索引
-- 走索引 select * from article where author like '李%'聯合索引最左前綴原則
mysql會一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調整
1.將區分度最高的字段放在最左邊
當不需要考慮排序和分組時,將區分度最高的列放在前面通常是很好的。這時候索引的作用只是用于優化WHERE條件的查找
如果在a b列上建立聯合索引,該如何建立,才能使查詢效率最高
select count(distinct a) / count(*), count(distinct b) / count(*), count(*) from table執行如下語句,假設3個輸出依次為0.001,0.373,16049,可以看到b列的選擇性最高,因此將其作為聯合索引的第一列,即建立(b, a)的聯合索引
2.查詢時=可以亂序
如果建立了聯合索引(a, b)。例如下面的2個寫法是等價的,因為MySQL會將查詢的順序優化成和聯合索引的順序一致
select * from table where a = '1' and b = '1'select * from table where b = '1' and a = '1'3.優化查詢,避免出現filesort
select * from table where a = ? and b = ? order by c最左前綴原則不僅用在查詢中,還能用在排序中。MySQL中,有兩種方式生成有序結果集:
因為索引的結構是B+樹,索引中的數據是按照一定順序進行排列的,所以在排序查詢中如果能利用索引,就能避免額外的排序操作。EXPLAIN分析查詢時,Extra顯示為Using index。
所有不是通過索引直接返回排序結果的操作都是Filesort排序,也就是說進行了額外的排序操作。EXPLAIN分析查詢時,Extra顯示為Using filesort,當出現Using filesort時對性能損耗較大,所以要盡量避免Using filesort
對于如下sql
select * from table where a = ? and b = ? order by c可以建立聯合索引(a, b, c)
如果索引中有范圍查找,那么索引有序性無法利用,如
select * from table where a > 10 order by b索引(a,b)無法排序。
放幾個例子
-- 使用了a列 where a = 3-- 使用了a b列 where a = 3 and b = 5-- 使用了a b c列 where a = 3 and c = 4 and b = 5-- 沒有使用索引 where b = 3-- 使用了a列 where a = 3 and c = 4-- 使用了a b列 where a = 3 and b > 10 and c = 7-- 使用了a b 列 where a = 3 and b like 'xx%' and c = 7union,or,in都能命中索引,建議使用in新版MySQL的or可以命中索引
select * from article where id = 1 or id = 2效率從高到低為union,in,or。in和union的效率差別可以忽略不計,建議使用in
負向條件索引不會使用索引,建議用in
負向條件有:!=、<>、not in、not exists、not like 等
-- 全表掃描 select * from article where id != 1 and id != 2知道id的所有取值范圍,可以改為類似如下形式
-- 走索引 select * from article where id in (0, 3, 4)建立覆蓋索引
眾所周知,表數據是放在一個聚集索引上的,而建立的索引為非聚集索引,非聚集索引的葉子節點存放索引鍵值,以及該索引鍵指向的主鍵。一般查找的過程是從非聚集索引上找到數據的主鍵,然后根據該主鍵到聚集索引上查找記錄,這個過程稱為回表,不清楚的看推薦閱讀。
如有下面這個sql
select uid, login_time from user where username = ? and passwd = ?可以建立(username, passwd, login_time)的聯合索引,由于 login_time的值可以直接從索引中拿到,不用再回表查詢,提高了查詢效率
經常更改,區分度不高的列上不宜加索引
更新會變更 B+ 樹,更新頻繁的字段建立索引會大大降低數據庫性能。
“性別”這種區分度不大的屬性,建立索引是沒有什么意義的,不能有效過濾數據,性能與全表掃描類似。
一般區分度在80%以上的時候就可以建立索引,區分度可以使用 count(distinct(列名))/count(*) 來計算
明確知道只會返回一條記錄,可以加limit1
當查詢確定只有一條記錄時,可以加liimit1,讓MySQL停止游標移動,提高查詢效率
select uid from user where username = ? and passwd = ?可改為
select uid from user where username = ? and passwd = ? limit 1對文本建立前綴索引
用郵箱登錄是一個常見的問題,如果對email整個字段建立索引,會讓索引變得大且慢
select username from user where email='xxx';這時我們可以索引開始的部分字符,這樣可以大大節約索引空間,從而提高索引效率,但這樣也會降低索引的區分度。索引的區分度是指,不重復的索引值和數據表的記錄總數的比值。索引的區分度越高則查詢效率越高,因為區分度高的索引可以讓MySQL在查找時過濾掉更多的行。
因此我們選擇足夠長的前綴保證較高的區分度,同時又不能太長(以便節約空間)
可以進行如下實驗
select count(distinct left(email, 5)) / count(*) as col5,count(distinct left(email, 6)) / count(*) as col6,count(distinct left(email, 7)) / count(*) as col7from user假設輸出依次為0.0305,0.0309,0.0310
查詢顯示當前綴長度達到7的時候,再增加前綴長度,區分度提升的幅度已經很小了,因此創建email(7)的前綴索引即可
需要注意的一點是,前綴索引不能使用覆蓋索引,因為從索引中獲取不到完整的數據,還得回表查詢
建立索引的列不為NULL
只要列中包含有 NULL 值都將不會被包含在索引中,復合索引中只要有一列含有 NULL值,那么這一列對于此復合索引就是無效的。
因此,在數據庫設計時,除非有一個很特別的原因使用 NULL 值,不然盡量不要讓字段的默認值為 NULL。
分頁查詢優化
MySQL 并不是跳過 offset 行,而是取 offset+N 行,然后返回放棄前 offset 行,返回 N 行,那當 offset 特別大的時候,效率就非常的低下,要么控制返回的總頁數,要么對超過特定閾值的頁數進行 SQL 改寫,單開一文來講
總結
以上是生活随笔為你收集整理的详解mysql什么时候不走索引的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python3的3D实战 -基于pand
- 下一篇: springboot配置跨mapper.