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

MySQL 索引一般是被用於提升 WHERE 條件的數據行匹配時的搜索速度,在索引的使用過程當中,存在一些使用細節和注意事項。javascript

原文地址:服務端指南 數據存儲篇 | MySQL(04)索引使用的注意事項
博客地址:blog.720ui.com/java

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

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

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

爲了使用索引,防止執行全表掃描,能夠進行改造。sql

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 是個不錯的方案。

(完)

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

相關文章
相關標籤/搜索