摘要
我們將一條查詢SQL提交給MySQL之后,MySQL在進行真正的查詢操作之前通常會經歷兩個階段:SQL解析和查詢優化。在SQL解析過程中,MySQL會將SQL解析為一個樹狀結構,而在查詢優化階段,MySQL會決定以什么方式進行查詢,那么MySQL以什么方式進行查詢的抉擇依據是什么呢?答案就是這篇文章要介紹的MySQL統計信息,因為我廠的MySQL實際使用的是Percona分支,因此本文相關的實驗知識是基於Percona分支的。
帶着問題
MySQL統計的信息包括什么內容?是用來做什么的?
MySQL統計信息基於表和索引,表和索引是要變化的,那么MySQL是如何保證數據的時效性的?
MySQL的統計機制有什么問題?統計策略如何選擇?
MySQL統計信息
存儲方式
MySQL獲取統計信息之后如何放在那兒呢?統計信息的保存方式有兩種,一種是容失性保存,另一種是持久化保存。
持久化存儲
innodb_stats_persistent參數用於控制采樣信息是否持久化,innodb_stats_persistent=ON的時候,MySQL會將統計信息進行持久化存儲,這樣當機器數據庫重啟之后,統計信息依然有效,對於InnoDB存儲引起來說,統計信息分別存儲在mysql庫的下面兩張表中:
innodb_table_stats
innodb_index_stats
innodb_table_stats存儲表維度的統計信息,innodb_index_stats存儲索引維度的統計信息。在持久化存儲的情況下,當設置為自動更新統計信息的時候且表中有超過10%的數據被更新的時候會執行統計信息的重新計算,而且重新統計不是立即執行的,而是等了一段時間,這個值在MySQL中被定義為MIN_RECAL_INTERVAL=10(秒)。
易失性存儲
當innodb_stats_persistent=OFF的時候,MySQL統計信息存儲在內存之后,很顯然當重啟數據庫的時候,這些信息會丟失。在易失性存儲的情況下,統計信息重新計算的時機和持久化存儲方式是不同的,我們來看看哪些條件會觸發該情況下統計信息的重新計算:
執行ANALYZE TABLE命令
執行如下命令:SHOW TABLE STATUS, SHOW INDEX。
在innodb_stats_on_metadata選項開啟的情況下查詢INFORMATION_SCHEMA.TABLES表或INFORMATION_SCHEMA.STATISTICS表
通過--auto-rehash參數開啟客戶端連接,--auto-rehash參數導致InnoDB表被打開,InnoDB表被打開導致統計信息被重新計算
表被第一次打開
距離上次統計之后,表的1/16的數據被更新
了解在什么方式下統計信息會被重新計算對於數據庫的使用優化是有幫助的,比如我們可以破壞一些條件而讓事情向着對我們有力的一面發展。
統計內容
MySQL統計信息包括哪些內容呢?MySQL分別從表維度和索引維度構建統計信息。
表統計信息
innodb_table_stats表存儲的是表維度的統計信息,innodb_table_stats表有6個字段,他們的各字段相關定義以及含義如下表所示:
字段名
字段類型
字段含義
database_name verchar(64) 統計信息所屬表的數據庫名
table_name verchar(64) 統計信息所屬的表名
last_update timestamp 統計信息最后一次更新的時間
n_rows bigint(20) unsigned 表所包含的行數
clustered_index_size bigint(20) unsigned 聚集索引的頁的數量
sum_of_other_index_size bigint(20) unsigned 其他索引所占的頁的數量
我找了一張我們現存的表測試一下:
如上圖所示,CL_CommunityNavStatInfo表當前的記錄數為5281。
上面我們看到表中實際有5281行數據,但是統計出來的是5228行數據,這是因為什么呢?這個問題留在精度問題部分進行討論。
索引統計信息
innidb_index_stats表存儲的是索引維度的統計信息,innodb_index_stats表有8個字段,他們的各字段相關定義以及含義如下表所示:
字段名
字段類型
字段含義
database_name varchar(64) 統計信息所屬表的數據庫名
table_name varchar(64) 統計信息所屬表名
index_name varchar(64) 統計信息所屬索引名
last_update timestamp 統計信息更新的時間
stat_name varchar(64) 統計信息名稱
stat_value bigint(20) unsigned 統計值
sample_size bigint(20) unsigned 采樣大小
stat_description varchar(64) 統計描述信息
我們依然使用上面測試用到的CL_CommunityNavStatInfo表進行測試,先看看CL_CommunityNavStatInfo表的索引定義:
CL_CommunityNavStatInfo表建立了三個索引,我們通過innodb_index_stats表來看看這三個索引的統計信息:
上圖為表CL_CommunityNavStatInfo所有索引的統計信息,比如最后一行,size代表主鍵聚集所以所占頁數大小為161,葉子節點所占空大小為128頁,id的區分度為5228,這個數字其實也是統計的表的行數,sample_size為20表示采樣頁數。
精度問題
采樣大小
上面提到了sample_size這個數字,其實MySQL的統計數據是基於采樣數據估算的,而采樣的大小是用戶可控的,默認值為20,我們可以通過修改采樣大小來控制統計信息的精確性,同時這也會影響性能。比如我們用下面命令將采樣大小調整為200:
SET global innodb_stats_persistent_sample_pages=200;
200是我們隨表挑的一個大於所有數據頁數的數字,這樣保證統計信息基於全量數據統計,通過ANALYZE TABLE CL_CommunityNavStatInfo;命令重新統計之后,再來看看統計信息:
是不是無比的准確?再繼續看看索引的統計信息:
現在的統計信息已經是基於全量的數據統計了,雖然數據准確了,但是我們同時也損失了一部分的性能。
統計時機
定時輪訓
統計時機關心的是什么時候進行統計信息的更新。innodb_stats_auto_recalc參數用於控制是否讓MySQL自行在需要的時候更新統計信息,當它的值為ON的時候,統計信息的重新計算是異步的,MySQL有一個線程專門用來做這個事情,這個線程每隔10秒鍾回去看看要不要進行統計,否則我們需要使用ANALYZE TABLE命令來保證統計信息的時效性。那么我們是選擇將統計信息的更新權利完全霸占還是將其授權給MySQL讓它自行更新呢?這個問題留給讀者思考。
總結
本文分別從MySQL統計信息的存儲、內容、精度和統計時機方面對MySQL統計信息進行了一定的學習,了解了MySQL統計信息的相關知識,我認為我們至少可以解決一些實際問題了。比如:
我們當前應用的數據源MySQL關於統計方面的配置有沒有問題?
我們是否可以試着通過調整采樣大小來控制統計信息的精確度?從而影響SQL優化器的決策?
我們是否可以通過統計信息來估算表中數據所占用的存儲空間?
……
等等。