mysql索引測試——重復數據字段不宜作為索引字段


因為工作的項目涉及千萬級數據的單表內容的統計查詢。所以對於此sql的效率做了一個測試比較,並在測試過程中發現了索引相關的問題:

情況說明:

  原始表單(記A表)為業務接口調用記錄表,每個用戶的每次調用接口都會產生一條記錄。一天產生一張表,單表規模可能達到千萬。

  記錄的信息包括用戶(uuid),調用的接口(method),接口耗時時間(time_cost)等…,

要求:

  統計出一天內(A表一天生成一張)A表中每個用戶(uuid)的每個接口(method)對應的調用的次數及最大和最小消耗時間(max_cost,min_cost)。

  另A表建有一個method列的簡單索引

  在未訪問A表之前,只知道總共有哪些接口(接口總共10個左右),不知道存在哪些用戶

 

思路:

1、 將uuid和function分組排列,一步到位直接出結果

2、 單獨對每個接口只按uuid排列,總共多少接口統計多少次

 

單表數據:單表600w數據,1w個uuid隨機,method 6個隨機,time_cost 0~1000內隨機

1、將uuid和function分組排列,一步到位直接出結果

select uuid, method, count(1) call_times,min(time_cost) min_cost,max(time_cost) max_cost from A_yyyymmdd group by uuid, method into outfile '/var/lib/mysql-files/a1.txt';

sql語句執行花費:11.51s

語句后面的 into outfile ‘/var/lib/mysql-files/a.txt’表示將語句執行結果導出到文件,具體導出的目錄權限可通過mysql安全控制變量secure_file_priv查看

通過show variables like '%secure%'可查看:

 

2、單獨對每個接口只按uuid排列,總共多少接口統計多少次

select uuid, count(1) call_times,min(time_cost) min_cost,max(time_cost) max_cost from A_yyyymmdd where method= ‘useradd’ group by uuid into outfile '/var/lib/mysql-files/a1.txt';

統計單個接口的花費:48.61s

對於此結果有點詫異,按照道理,group by 單列應該比group by 組合列效率更高

 

3、考慮是否where產生的影響,那么把where去掉看下,只觀察語句執行效率。

select uuid, count(1) call_times,min(time_cost) min_cost,max(time_cost) max_cost from A_yyyymmdd group by uuid into outfile '/var/lib/mysql-files/a1.txt';

花費:5.20s

group by只消耗了5.20s,但總的卻是21.09左右,也就是說在where子句的時候花費更多的事件。

 

4、留where子句,去掉group by觀察語句執行效率:

select uuid from A_yyyymmdd where method= ‘useradd‘ into outfile '/var/lib/mysql-files/a1.txt';

耗時:45.5s

果然where子句消耗的時間太高。

 

5、查看where子句執行計划,看索引是否生效

explain select uuid from A_yyyymmdd where method= 'useradd' into outfile '/var/lib/mysql-files/a7.txt';

發現已經有用上索引,用上索引花費45s,有點疑惑,查看group by uuid的語句(花費5.2s)是否用上索引

explain select uuid, count(1) call_times,min(time_cost) min_cost,max(time_cost) max_cost from A_yyyymmdd group by uuid into outfile '/var/lib/mysql-files/a1.txt';

發現group by uuid的語句是全表掃描,沒有使用索引,反而只需要花費5.2s。

用到索引的卻要花費45.5s。

 

6、對where語句忽略索引,執行看看執行花費:

explain select uuid from A_yyyymmdd ignore index(aaa_method_call_times_record_method_IDX) where method= ‘useradd’ into outfile '/var/lib/mysql-files/a1.txt';

where語句不使用索引,全表掃描,執行語句:

select uuid from A_yyyymmdd ignore index(aaa_method_call_times_record_method_IDX) where method= 'useradd' into outfile '/var/lib/mysql-files/a1.txt';

耗時:3.69s

同樣一個where語句 使用索引的45.5s,忽略索引的3.69s。

 

7、通過網上了解到這樣結果是因為索引建立的不合適,涉及到數據庫引擎的索引原理:

A表索引是建立在method上的,在此場景中,表中method數據存在大量重復的。

我默認數據庫引擎是InnoDB

 

數據庫中聚集索引只有一個,默認主鍵。其他用戶創建的索引都是非聚集索引(二級索引)。非聚集索引存儲了對主鍵的引用,即通過索引確定葉子節點之后,還需要再次根據主鍵去查詢數據。(所以會查詢兩次)。如果非聚集索引重復率高(即一個同樣的值有多個主鍵),那么條件會確定近乎一半或1/3,或1/5的主鍵值,然后在一個一個去聚集索引查詢。這樣開銷會大,

 

如本次場景中數據:method 為6個隨機,總共600w數據。

當method為索引時,每個method值對應約100w主鍵,

索引查詢過程首先查詢非聚集索引,從中查到約100w的主鍵引用,然后根據主鍵一個一個去聚集索引中查詢數據,即查詢聚集索引要查詢約100w次。而全表掃描,只需一次掃描全部即可得出結果。此場景中索引的開銷已經遠遠大於全表掃描的開銷了。

 

如果索引后開銷低於全表掃描,那么使用索引。如果索引導致了比全表掃描更糟糕的結果,那么還不如全表掃描。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM