什麼是走索引?

索引是一種利用某種規則的數據結構與實際數據的關係加快數據查找的功能。我們的數據庫中存儲有大量的內容,而索引能夠通過數據節點,根據特定的規則和算法快速查找到節點對應的實際文件的位置。簡單來說索引就像書的目錄,能夠幫助我們準確定位到書籍具體的內容。

最近在學習索引的時候遇到了一個問題,下面我們通過重現的方式來看一下。

首先建立一個如下測試表:

CREATE TABLE `simple_table` (
  `id` int NOT NULL AUTO_INCREMENT,
  `c1` datetime DEFAULT NULL,
  `c2` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `c2__idx` (`c2`),
  KEY `fun_c1_idx` ((cast(`c1` as date)))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

fun_c1_idx: 是mysql8開始支持的函數索引

然後往這個表裏隨機插入1000 條數據。

select * from simple_table where date(c2) = '2022-01-01';

可以看到上面的這條 SQL 語句不能走索引。因爲索引樹中存儲的是列的實際值和主鍵值,所以對條件字段做函數操作是會讓索引失效的。簡單來說就是,如果拿 ‘2022-01-01’ 去匹配,將無法定位到索引樹中的值。因此正確選擇是放棄走索引,選擇全表掃描。

我們再看下一條 SQL。

select id,c2 from simple_table where date(c2) = '2022-01-01';

與第一條不同,這條 SQL 只返回了部分列,而且這些列都在索引中了。然後我們用 explain 分析一下這條 SQL 的執行計劃,判斷它能否走索引:

上圖可以明顯看到 key 值爲c2__idx,即走了索引。

這裏就很奇怪,不是說對條件字段做函數操作是會讓索引失效嗎,爲什麼這裏又走了索引?

這就是我當時在學習時遇到的問題,後來我發現是因爲我沒有搞清楚“走索引”的意思。大家都知道索引能加快查詢,但是索引能加快查詢的原因你知道麼?答案是減少了查詢的次數。

現在我們回到上面的 SQL,可以看到雖然key 值爲c2__idx,但是 rows 值爲 1000。也就是掃描了掃描全表,即 c2__idx的所有記錄。但是由於c2__idx已經包含了所有需要查詢的列,優化器才選擇了走這個索引。

最後再來思考一個問題,使用了索引是否一定快?這個問題我們通過一個具體例子看一下:

select * from simple_table;
select * from simple_table where id > 0;

不需要 explain 分析直接肉眼觀察就能看到第一條 SQL 沒有走索引,第二條 SQL 使用了主鍵索引。可以看到沒有使用索引的速度快一些,這是因爲雖然使用了索引,但是還是從主鍵索引的最左邊的葉節點開始向右掃描整個索引樹,進行了全表掃描,這讓索引失去了意義。

總結一下:查詢是否使用索引,只是表示一個 SQL 語句的執行過程;而是否爲慢查詢,是由它執行的時間決定的,也就是說是否使用了索引和是否是慢查詢兩者之間沒有必然的聯繫。我們在使用索引時,不應只關注是否起作用,而應該關心索引是否減少了查詢掃描的數據行數,掃描行數減少效率纔會得到提升。對於一個大表,不止要創建索引,還要考慮索引過濾性,過濾性好,執行速度纔會快。

推薦閱讀

Base64 編碼知識,一文打盡!

Golang 常見設計模式之單例模式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章