MySQL只學有用的--給字符串添加索引,order by性能優化, count()性能優化

怎麼給字符串字段添加索引

根據前一篇文章我們知道,兩個概念:

  1. 索引的長度不宜過長
  2. 前綴索引

根據這兩個概念,我們在給字符串字段設置索引的時候就可以設置索引的長度。如果不設置,默認索引會包含整個字符串。

舉個例子

select table user add index(email);
或者 
select table user add index(email(6));

第一個語句創建索引會包含整個字符串。第二個語句創建索引會只會包含前6個字符串。

在使用第二條語句的創建索引的時候,也要有一定的技巧,要注意索引的區分度,如果索引太短的話,字段的值大量重複,在搜索的時候就會出現大量回表操作。 因此索引的長度我們是需要計算的。

字符串索引計算長度步驟

  1. 首先,你可以使用下面這個語句,查詢這個列上有多少個值。
select count(distinct email) as sum from user;

然後,依次選取不同長度的前綴來看這個值,比如我們要看一下4~7個字節的前綴索引,可以用這個語句 :

select 
count(distinct left(email,4)) as l4,
count(distinct left(email,5)) as l5
count(distinct left(email,6)) as l6,
count(distinct left(email,7)) as l7,
from user;

當然,使用前綴索引很可能會損失區分度,所以你需要預先設定一個可以接受的損失比例,比如5%。 然後,在返回的L4~L7中,找出不小於L*95%的值,假設這裏L6和L7都滿足,你就可以選擇前綴長度爲6。

前綴索引對覆蓋索引的影響

看下面這個語句

select id,email from user where email= "[email protected]";

當我們查詢這個語句的時候,如果使用的email索引是全索引的話,這個時候是可以使用索引覆蓋的,因爲普通索引中的值是主鍵索引的值。而如果我們在創建索引的時候使用了前綴索引,就無法使用索引覆蓋了,會進行回表操作。
即使我們在創建索引的時候,指定的長度就是字段的長度也會進行回表操作。因爲innoDB會認爲我們使用了前綴索引。認識這個索引的值是不全的。

count統計的相關玩法

由於MySQL不同引擎使用的計算方法不一致,這裏只聊一聊InnoDB引擎。

InnoDB引擎count()原理分析

InnoDB引擎在執行count(*)的時候,需要把數據一行一行地從引擎裏面讀出來,然後累積計數。這是因爲即使是在同一個時刻的多個查詢,由於多版本併發控制(MVCC)的原因,InnoDB表“應該返回多少行”也是不確定的。

假設表t中現在有10000條記錄,我們設計了三個用戶並進行會話。

  • 會話A先啓動一個事務並查詢一次表的總行數。
  • 會話B啓動事務,插入一行記錄後,查詢表的總行數。
  • 會話C先啓動一個單獨的語句,插入一行記錄後,查詢表的總行數。

在這裏插入圖片描述
你會看到,在最後一個時刻,三個會話A,B,C會同時查詢表T的總行數,但拿到的結果卻不相同。

這和InnoDB的事務設計有關係,可重複讀是它的默認的隔離級別,在代碼上就是通過多版本併發控制,也就是MVCC來實現的。第一行記錄都要判斷自己是否對這個會話可見,因此對於count(*)請求來說,InnoDB只好把數據一行一行地讀出依次判斷,可見的行才能夠用於計算“基於這個查詢”的表的總行數。
MySQL會盡量的走普通索引,因爲普通索引的值是主鍵值,整體比較小。

對比一下count(*) count(主鍵)、count(字段)、count(1)

  1. count(主鍵ID),InnoDB引擎會遍歷整張表,把每一行的ID值都取出來,返回給server層。server層拿到ID後,判斷是不可能爲空的,就按行累加。
  2. count(1),InnoDB引擎遍歷整張表,但不取值。server層對於返回的每一行,放一個數字"1"進行,判斷是不可能爲空的,按行累加。

單看這兩個用法的差別的話,你能對比出來,count(1) 執行得要比count(主鍵ID)快。因爲從引擎返回ID會涉及到解析數據行,以及拷貝字段值的操作。

  1. count(字段), 需要一行一行的讀記錄,如果在定義字段的時候設置爲not null則直接累加,否則就需要先判斷是否爲null 纔會累加。

  2. count(*),並不會把全部字段取出來,而是專門做了優化,不取值,直接按行累加。

如果表中有普通索引,count(1)和count(*)都會選擇走一個最短的普通索引。

在這裏插入圖片描述
按照效率排序:
count(字段)<count(主鍵ID)<count(1)≈count()
建議直接使用count(
) 就好了。

Order By 排序

order by 排序算法有兩種rowId 和全字段排序。

全字段排序: InnoDB有一個sort_buffer,會將數據查詢出來放到sort_buffer中,在進行排序。sort_buffer的大小是由一個sort_buffer_size設置。
如果在排序的數據太大,sort_buffer內存中放不下,則不得不利用磁盤臨時文件輔助排序。就是將數據分別放入到磁盤中的臨時文件進行排序,在將這些小的有序文件,合併成一個大的有序文件,返回。

rowid排序: 只會將需要排序的字段和主鍵放到sort_buffer中,先排序在拿id去查詢出,需要返回的數據。

set max_length_for_sort_data = 16

max_length_for_sort_data是MySQL中專門控制用於排序的行數據的長度的一個參數。它的意思是,如果單行的長度超過這個值,MySQL就認爲單行太大,就會採用rowid這種算法。
以上都是InnoDB引擎自動的優化點。

利用索引

我們知道InnoDB的索引有這樣兩個特性

  1. 有序
  2. 索引覆蓋
    當我們查詢的SQL要查詢的字段是索引覆蓋的內容,排序的字段上也建立了索引,就會簡化整個過程。

我們先建立索引

alter table t add index city_user_age(city,name,age)

在這裏插入圖片描述
可以看到extra上少了 “Using filesort”, “Using index condition”. 多了一個"Using index",表示的就是使用了索引覆蓋,性能上會快很多。

如果只創建了city,name這兩個聯合索引的話就會出來這樣的情況。
在這裏插入圖片描述
發現少了"Using filesort",也是少了排序這個步驟直接返回結果,只是需要進行回表操作。 這樣也很快了。

小結

我們聊了String加索引的辦法使用前綴索引, 並聊了聊創建前綴索引的時候需要注意辨識度,要讓索引中值的重複度降低。

我們還聊了聊統計函數count(),我們需優先使用count(*), Mysql 會自動選擇一個最小的普通索引來進行查詢。

我們還聊了排序Order by, 我們在可能的情況下要優先利用索引覆蓋這個原則。如是不行的話,也儘可能的利用索引的有序性。

引用

《Mysql實戰45講》,count(1)和count(*)的對比

以上內容均爲讀書所得,如有錯誤請聯繫。

交個朋友好

以上內容均爲讀書所得, 更多有趣有料的科技資訊請關注公衆號。(交個朋友)

在這裏插入圖片描述

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