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

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

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

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

根據這兩個概念,咱們在給字符串字段設置索引的時候就能夠設置索引的長度。若是不設置,默認索引會包含整個字符串。算法

舉個例子sql

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個字節的前綴索引,能夠用這個語句 :svg

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= "xiaowang@gmail.com";

當咱們查詢這個語句的時候,若是使用的email索引是全索引的話,這個時候是可使用索引覆蓋的,由於普通索引中的值是主鍵索引的值。而若是咱們在建立索引的時候使用了前綴索引,就沒法使用索引覆蓋了,會進行回表操做。
即便咱們在建立索引的時候,指定的長度就是字段的長度也會進行回表操做。由於innoDB會認爲咱們使用了前綴索引。認識這個索引的值是不全的。優化

count統計的相關玩法

因爲MySQL不一樣引擎使用的計算方法不一致,這裏只聊一聊InnoDB引擎。spa

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(*)的對比

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

交個朋友好

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

在這裏插入圖片描述