索引下推:
ICP過濾的條件可以不限于用于索引查找(index lookup)的字段。只要存儲引擎在掃描當前索引時能夠訪問到該字段的值,就可以用它來過濾。
索引可以分為聚簇索引和非聚簇索引
沒有索引下推:
當使用聚簇索引的時候,b+樹的葉子結點就是數據項,但是如果沒有索引下推的話,即使數據中已經可以拿到其余的數據項,但是仍然不會進行過濾
例如where name = 張% and age < 30,只在name上有聚簇索引
即使在存儲引擎根據name查詢得到的數據是完整的數據,可以拿到age字段的值,但是仍然不會對age條件進行過濾
使用非聚簇索引的時候,b+樹的葉子結點是主鍵索引的值,例如一個聯合索引(a,b,c),如果只匹配到a,那么即使where條件中有c的條件,也不會在這個時候進行過濾,只會根據a的索引得到所有主鍵id,然后返回給服務器,服務器再根據主鍵id去查詢所有數據,返回到服務層再進行過濾,這個過程就是回表
有索引下推:
會將where的條件下推到存儲引擎層,存儲引擎層就會利用自己所能獲得的數據更多的進行過濾
同樣上面兩個例子:
如果是聚簇索引的話,存儲引擎可以得到完整的數據,就可以在這個時候根據age字段進行過濾,將過濾之后的數據返回給服務器層
如果是非聚簇索引的話,在聯合索引(a,b,c)中雖然只匹配到a的索引,但是可以獲得c字段的值,也可以通過where中c的值進行過濾,返回更少的主鍵id給服務器層去進行回表
索引失效:
就是明明可以使用索引來提升效率但是沒用上索引
1.索引類型不匹配
例如索引age的數據類型是整數類型,但是查詢的時候使用的是age = '20'
這樣就會導致隱式類型轉換,導致無法使用age上的索引
2.使用函數表達式
age上有索引,但是使用的是where f(age) = 30,索引無法生效
3.進行運算
age有索引,但是where age + 20 < 30,這樣的運算實際也會導致索引失效
雖然后面mysql優化可以給帶運算的字段加索引,但是不是一定生效的
4.like查詢且以通配符(如 %
或 _
)開頭時
name上有索引,但是like '%張'會導致name上的索引失效,但是like '張%'可以用上索引
5.or條件
or兩側是同一個索引:索引生效
or兩側是多個索引:優化器通常會評估索引之間的效率,選擇其中一個索引使用或者全表搜索,如果有索引合并的話,會分別將索引查找的集合進行union合并
6.not in和not like
NOT IN
的否定性質使其難以利用索引的有序性進行快速定位(B+樹的優勢在于快速找到“等于”或“在某個范圍內”的值),但它仍然可以利用索引進行掃描,但是不是利用索引查詢
not in的時候如果范圍內有null的話,會導致查不到數據
7.在聯合索引中,如果前導索引是范圍查詢,后續索引不會生效
比如聯合索引(a,b),如果是where a>10 and b = 20;只會匹配到索引a,索引b不會生效
對not in的改進:
例如:
- 表?
users
:有 10000 個用戶,id
?是主鍵(有索引)。 - 表?
banned_users
:有 10 個被封禁的用戶,user_id
?列有索引
1.使用not in
SELECT * FROM users
WHERE id NOT IN (SELECT user_id FROM banned_users);
如果banned_users中存在一條user_id為null的記錄的話,那這條查詢就會出問題,查不到任何數據
2.使用not exists
SELECT * FROM users u
WHERE NOT EXISTS (SELECT 1 FROM banned_users b WHERE b.user_id = u.id
);
實際上對user表也是全表檢索的,但是如果banned_users的user_id有索引的話可以用上這個的索引
3.使用left join
SELECT u.*
FROM users u
LEFT JOIN banned_users b ON u.id = b.user_id
WHERE b.user_id IS NULL;
左連接以左邊的表為主,把右邊的表按照u.id=b.user_id拼過來,如果不存在b.user_id的話,就是null