服務端指南 資料儲存篇 | MySQL(04) 索引使用的注意事項

樑桂釗發表於2017-04-13

MySQL 索引通常是被用於提高 WHERE 條件的資料行匹配時的搜尋速度,在索引的使用過程中,存在一些使用細節和注意事項。

原文地址:服務端指南 資料儲存篇 | MySQL(04)索引使用的注意事項
部落格地址:blog.720ui.com/

不要在列上使用函式和進行運算

不要在列上使用函式,這將導致索引失效而進行全表掃描。

select * from news where year(publish_time) < 2017複製程式碼

為了使用索引,防止執行全表掃描,可以進行改造。

select * from news where publish_time < '2017-01-01'複製程式碼

還有一個建議,不要在列上進行運算,這也將導致索引失效而進行全表掃描。

select * from news where id / 100 = 1複製程式碼

為了使用索引,防止執行全表掃描,可以進行改造。

select * from news where id = 1 * 100複製程式碼

儘量避免使用 != 或 not in或 <> 等否定操作符

應該儘量避免在 where 子句中使用 != 或 not in 或 <> 操作符,因為這幾個操作符都會導致索引失效而進行全表掃描。

儘量避免使用 or 來連線條件

應該儘量避免在 where 子句中使用 or 來連線條件,因為這會導致索引失效而進行全表掃描。

select * from news where id = 1 or id = 2複製程式碼

多個單列索引並不是最佳選擇

MySQL 只能使用一個索引,會從多個索引中選擇一個限制最為嚴格的索引,因此,為多個列建立單列索引,並不能提高 MySQL 的查詢效能。

假設,有兩個單列索引,分別為 news_year_idx(news_year) 和 news_month_idx(news_month)。現在,有一個場景需要針對資訊的年份和月份進行查詢,那麼,SQL 語句可以寫成:

select * from news where news_year = 2017 and news_month = 1複製程式碼

事實上,MySQL 只能使用一個單列索引。為了提高效能,可以使用複合索引 news_year_month_idx(news_year, news_month) 保證 news_year 和 news_month 兩個列都被索引覆蓋。

複合索引的最左字首原則

複合索引遵守“最左字首”原則,即在查詢條件中使用了複合索引的第一個欄位,索引才會被使用。因此,在複合索引中索引列的順序至關重要。如果不是按照索引的最左列開始查詢,則無法使用索引。

假設,有一個場景只需要針對資訊的月份進行查詢,那麼,SQL 語句可以寫成:

select * from news where news_month = 1複製程式碼

此時,無法使用 news_year_month_idx(news_year, news_month) 索引,因為遵守“最左字首”原則,在查詢條件中沒有使用複合索引的第一個欄位,索引是不會被使用的。

覆蓋索引的好處

如果一個索引包含所有需要的查詢的欄位的值,直接根據索引的查詢結果返回資料,而無需讀表,能夠極大的提高效能。因此,可以定義一個讓索引包含的額外的列,即使這個列對於索引而言是無用的。

範圍查詢對多列查詢的影響

查詢中的某個列有範圍查詢,則其右邊所有列都無法使用索引優化查詢。

舉個例子,假設有一個場景需要查詢本週釋出的資訊文章,其中的條件是必須是啟用狀態,且釋出時間在這周內。那麼,SQL 語句可以寫成:

select * from news where publish_time >= '2017-01-02' and publish_time <= '2017-01-08' and enable = 1複製程式碼

這種情況下,因為範圍查詢對多列查詢的影響,將導致 news_publish_idx(publish_time, enable) 索引中 publish_time 右邊所有列都無法使用索引優化查詢。換句話說,news_publish_idx(publish_time, enable) 索引等價於 news_publish_idx(publish_time) 。

對於這種情況,我的建議:對於範圍查詢,務必要注意它帶來的副作用,並且儘量少用範圍查詢,可以通過曲線救國的方式滿足業務場景。

例如,上面案例的需求是查詢本週釋出的資訊文章,因此可以建立一個news_weekth 欄位用來儲存資訊文章的周資訊,使得範圍查詢變成普通的查詢,SQL 可以改寫成:

select * from news where     news_weekth = 1 and enable = 1複製程式碼

然而,並不是所有的範圍查詢都可以進行改造,對於必須使用範圍查詢但無法改造的情況,我的建議:不必試圖用 SQL 來解決所有問題,可以使用其他資料儲存技術控制時間軸,例如 Redis 的 SortedSet 有序集合儲存時間,或者通過快取方式快取查詢結果從而提高效能。

索引不會包含有NULL值的列

只要列中包含有 NULL 值都將不會被包含在索引中,複合索引中只要有一列含有 NULL值,那麼這一列對於此複合索引就是無效的。

因此,在資料庫設計時,除非有一個很特別的原因使用 NULL 值,不然儘量不要讓欄位的預設值為 NULL。

隱式轉換的影響

當查詢條件左右兩側型別不匹配的時候會發生隱式轉換,隱式轉換帶來的影響就是可能導致索引失效而進行全表掃描。下面的案例中,date_str 是字串,然而匹配的是整數型別,從而發生隱式轉換。

select * from news where date_str = 201701複製程式碼

因此,要謹記隱式轉換的危害,時刻注意通過同型別進行比較。

like 語句的索引失效問題

like 的方式進行查詢,在 like "value%" 可以使用索引,但是對於 like "%value%" 這樣的方式,執行全表查詢,這在資料量小的表,不存在效能問題,但是對於海量資料,全表掃描是非常可怕的事情。所以,根據業務需求,考慮使用 ElasticSearch 或 Solr 是個不錯的方案。

(完)

更多精彩文章,盡在「服務端思維」微信公眾號!

服務端指南 資料儲存篇 | MySQL(04) 索引使用的注意事項

相關文章