MySQL 查詢索引失效及如何進行索引優化

2022-03-11 23:17:46 字數 3971 閱讀 8951

我們都知道建立索引的目的是快速從整體集合中選擇性地讀取滿足條件的一部分集合。mysql中一張表是可以支援多個索引的。但是,你寫sql語句的時候,並沒有主動指定使用哪個索引。不知道你有沒有碰到過這種情況,一條建立了索引的sql語句在查詢過程中卻沒有使用索引,或是一條本來可以執行的很快的語句,卻由於mysql選錯了索引,而導致查詢速度變得很慢?充分優化和利用索引能夠大大提高資料的查詢效率,但是在實際的應用中mysql可能並不總會選擇合適且效率高的索引。 那麼我們今天就一起來討論下 mysql 索引以及索引的優化,首先我們來看乙個案例,下面是一張建表的sql如下:

create

table

`t_test3` (

`id`

bigint(11) not

null

, `name`

varchar(32) default

null,

primary

key(`id`),

key`t_test_name` (`name`)

) engine

=innodb default charset=utf-

8;

使用以下的sql檢視對應的執行計畫:

事實上,在建立表的sql中我們是對name這一列建立了索引,為何在執行計畫的時候沒有使用索引呢?

要找到這個原因,我們需要首先了解下sql在mysql中的執行過程,mysql 的整個架構可以分為 server 層 和儲存引擎層2個部分。server 層 包括聯結器,查詢快取,分析器,優化器,執行器等模組; 儲存引擎層 負責資料的儲存與提取。其架構模式是外掛程式式的,支援innodb、myisam、memory等多個儲存引擎,預設的是innodb。可以在建表的時候使用engine = memory來指定儲存引擎 。

其中server 層執行步驟如下:

第一步聯結器:通過賬號和密碼連線到對應的資料庫上,聯結器負責與客戶端建立連線,獲取許可權,維持和管理連線。連線分為長連線和短連線,長連線是指連線成功後,客戶端不斷有請求,則一直使用同乙個連線。短連線:處理幾個請求後,斷開連線,之後的請求需要重新連線。

第二步查詢快取:建立連線之後,mysql拿到乙個查詢請求後,會先查詢快取中之前是否執行過這條語句,如果查詢快取命中,則查詢結果直接返回給客戶端,如果查詢快取不命中,就會繼續後面的執行階段。完成以後,執行結果會被存入查詢快取中。大多數情況下不建議使用查詢快取。如果快取命中,mysql不需要執行後面的複雜操作,就可以直接返回結果,效率很高,但是查詢快取失效非常頻繁,只要有對乙個表的更新,這個表的所有查詢快取都會被清空,因此可能你費力地把結果快取起來,還沒使用,就被乙個更新全部清空了。除非你的業務是一張靜態表,很長時間才會更新一次,這種情況下可以使用查詢快取。

第四步優化器:優化器是資料庫的乙個核心子系統,你也可以把他理解為 mysql 資料庫中的乙個核心模組或者乙個核心功能模組。優化器的目的是按照一定原則來得到它認為的目標sql在當前情形下最有效的執行路徑,優化器的目的是為了得到目標sql的執行計畫。經過分析器,mysql就知道你要做什麼了。sql 在執行的過程中經過優化器,並由優化器生成 sql 的執行計畫。

傳統關係型資料庫裡面的優化器分為cbo和rbo兩種:

第五步執行器:開始執行的時候,首先會判斷此次連線是否有對應的操作許可權,如果沒有,則返回沒有許可權的錯誤。如果有許可權,則開啟表繼續執行。開啟表的時候,執行器會根據表的引擎定義,去使用這個引擎提供的介面。

比如下面這條sql語句執行器流程是這樣的:

select * from t_test3 where name = 'a';
1.呼叫innodb引擎介面獲取這個表的第一行,判斷name的值是不是a,如果不是則跳過,如果是則將這行存在結果集中。

2.呼叫引擎介面獲取下一行,重複相應的判斷邏輯,直到取到最後一行資料

3.執行器將遍歷過程中所有滿足條件的行組成的記錄集作為結果集返回給客戶端。

通過了解sql執行的過程以及優化器,發現mysql採用的是第二種基於成本的優化器,它會根據sql執行的成本選擇合適的路徑。所以可以推斷出上面sql執行計畫沒有採用對應列的索引原因。當我在表中插入一萬條資料的時候,再重新檢視對應的執行計畫時,其如下:

此時,該sql的查詢型別會使用range型別及使用name對應的索引進行查詢。

當資料量比較小的時候,會使用all型別進行查詢對應資料,當資料量比較大時,查詢資料量增大時,會採用range型別,並使用對應列的索引進行查詢。這便涉及到了資料庫查詢索引的離散度。 離散度,外文 measures of dispersion,是指通過隨機地觀測變數各個取值之間的差異程度,用來衡量風險大小的指標。離散度在不超過全表的10%-15%的前提下索引才可以顯示索引所具有的價值。當離散度超過該值的情況下全表掃瞄可能反倒比索引掃瞄更有效。我們所追求的目標就是建立全表掃瞄所無法比擬的有效索引。 比如當我們對一張學生表資訊中對性別新增索引,性別只有兩種值,會產生大量的重複,離散度較小,使用性別索引會增加查詢開銷,使得在使用性別的索引查詢時可能比沒有性別索引的查詢更慢。

基於資料庫索引的離散度,可以參考以下兩個建議進行建立索引:

1). 在允許的情況下,對具有較好離散度的列單獨建立索引,這樣可以提高該索引的使用彈性;

2). 對於離散度較差的列,通過對多列進行合理的組合來建立組合索引,雖然這樣做在很大程度上降低了各個列的使用彈性,但是卻可以發揮多個列的綜合效應。

在實際應用的過程中,mysql索引失效的情形很多。例如: 在where條件的like關鍵字匹配的字串以」%「開頭,這種情況下,索引是不會起到作用的;where條件中使用or關鍵字來連線多個查詢條件,如果有乙個條件沒有使用索引,那麼其他的索引也不會起作用;多列索引的第乙個字段沒有使用,那麼這個多列索引也不會起作用。 使用in查詢時,in查詢條件超過資料庫表的一半的時候也會失效。

根據這些情況,我們必須選擇對索引有正確的理解,並不是建立索引就能增加查詢速度。根據使用索引的特性,對建立索引的一些技巧總結如下:

1). 首先資料量小的表不需要建立索引,因為資料量小的表即使建立索引也不會有大的用處,還會增加額外的索引開銷 。

2). 不經常引用的列不要建立索引,因為不常用,即使建立了索引也沒有多大意義 。

3). 經常頻繁更新的列不要建立索引,因為肯定會影響插入或更新的效率 。

4). 盡量避免在 where 子句中使用 != 或者 <> 操作符,查詢引用會放棄索引而進行全表掃瞄。

5). 資料型別越小越簡單的索引更好。越小越簡單的資料型別通常在磁碟、記憶體和cpu快取中需要的空間更少,處理起來更快。

6). 盡量避免null: 在mysql中,含有空值的列很難進行查詢優化,因為它們使得索引、索引的統計資訊以及比較運算更加複雜。可以採用0、乙個特殊的值或者乙個空串代替空值 。

在實際應用的過程中,mysql並不總會選擇合理的索引進行查詢,此時便可以使用force index(index name)來強制告訴mysql選擇哪乙個索引。使用一下sql查詢:

desc select * from t_test3 force index (t_test_name) where name in ('a','b');
其對應的執行計畫與上圖的執行計畫相同,採用的是sql中指定的索引。

因此我們在一些情況下首先可以適當的使用force index(indexname) 強制告訴mysql使用什麼索引。force index( index name )指令可以指定本次查詢使用哪個索引!一條sql只會用到乙個索引,mysql優化器會計算出乙個合適的索引,但是這個索引不一定是最好的。force index()指令可以避免mysql優化器用到了乙個低效的索引,並可以提高sql的執行效率。

Mysql 之索引優化及索引失效

1.最佳左字首法則 如果索引了多列,要遵守最左字首法則.指的是查詢從索引的最左前列開始並且不跳過索引中的列.2.不在索引上左任何操作 計算,函式,自動or手動 型別轉換 會導致索引失效而轉向全表掃瞄 3.儲存引擎不能使用索引中範圍條件右邊的列 4.盡量使用覆蓋索引 只訪問索引的查詢 索引列和查詢列一...

mysql索引失效 常見mysql索引失效條件

使用索引的一般語句 1 where條件中有or,除非or的所有欄位都有索引,只要有乙個沒有索引,就不走索引 explain select from jf user ju where ju.user id or ju.superior1 yyy user id是主鍵,superior1是普通索引,結果...

mysql 主鍵失效 MySQL索引(索引失效)

索引 索引也是一張表,該錶儲存了主鍵與索引字段,並指向實體表的記錄。myisam儲存引擎,資料檔案 索引檔案 表結構檔案分開儲存 innodb儲存引擎,資料和索引儲存在乙個檔案中 b tree索引 hash索引 hash索引 只有memory儲存引擎支援 查詢一條記錄的速度非常快 b tree索引 ...