閱讀916 返回首頁    go 阿裏雲 go 技術社區[雲棲]


innodb的統計信息對執行計劃預估的影響實例

實為吾之愚見,望諸君酌之!聞過則喜,與君共勉

第一節 innodb引擎統計信息

mysql會依據innodb表的數據變化閾值來自動收集和計算表的統計信息(innodb_stats_auto_recalc)以供優化器使用,統計信息的收集是先通過獲取一部分符合條件的索引頁中的leaf page(是leaf page,不是non-leaf page)的數據,然後通過對這些采集的leaf page計算估計出不同值的數量,進而估算出的信息,信息采集的準確度除了和數據本身的構成有關,還與采集page數量有關,數量越多,采集精度越準確,在mysql5.6中引入了Persistent Optimizer Statistics來解決之前的Non-Persistent Optimizer Statistics帶來的一些問題,可以使用innodb_stats_persistent_sample_pages/innodb_stats_sample_pages控製采集精度,innodb_stats_sample_pages已經不推薦使用。innodb_stats_persistent_sample_pages參數是全局的,如果想單獨指定某個表的采集page數量,可以使用STATS_SAMPLE_PAGES選項,采集信息結果不準確甚至過度不準確會影響執行計劃的生成,造成語句的執行出現問題以至於影響數據庫的正常運行,這時可能就需要手動指定采集page數量來收集準確的統計信息,矯正執行計劃。查看mysql的統計信息(5.6)可以從mysql.innodb_table_stats and mysql.innodb_index_stats以及information_schema.INNODB_SYS_TABLESTATS獲取

第二節 準備數據和對比測試

2.1 建測試表

CREATE TABLE `MOCK_DATA` (

  `autoid` int(11) NOT NULL AUTO_INCREMENT,

  `id` int(11) DEFAULT NULL,

  `first_name` varchar(50) DEFAULT NULL,

  `last_name` varchar(50) DEFAULT NULL,

  `email` varchar(50) DEFAULT NULL,

  `gender` varchar(50) DEFAULT NULL,

  `ip_address` varchar(20) DEFAULT NULL,

  PRIMARY KEY (`autoid`),

  KEY `first_name` (`first_name`),

  KEY `id` (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=16580327 DEFAULT CHARSET=latin1

其中autoid是clustered index,first_name和id是secondary index,且id是1至1000連續的數字循環插入,

即id列隻有1至1000這些數字


2.2 生成測試數據

使用mockaroo臨時生成16384000行測試數據:

a7912d9f8b22c01e383e7bbf0004a61683938df0

 

2.3 查詢統計信息表

00d8a9bfff5338b1d33ed8a2c68451cb88818e5d

Index_name為id的stat_value為15185,即以index列為索引,在16384000的數據裏有15185個不同值(實際是1至1000個不同值),index_name索引有18368個索引頁(nonleaf page+leaf page),有15985個葉子頁(leaf page)

3d7b86312d6d92a864d75b98e0cff7c00eed49d7

b9b91a0cb123028aa3f0fb65f2a8085cc3998c58

該表經過統計,預估有16312847行數據,primary index有82736個索引頁,除primary index外其他索引一共有40128個索引頁(正好是innodb_index_stats中first_name和id索引頁的和)

16d09123492d82fb3683156f353bea2ec1696c6f

Index name為id的索引其Cardinality是30377(與innodb_index_stats中的stat_value的distinct value的數值不同)

 

如上獲取的一些統計信息是在innodb_stats_persistent_sample_pages20的情況下,手動analyze table MOCK_DATA生成的

 

2.4 執行查詢

執行如下語句:

SET optimizer_trace="enabled=on";

EXPLAIN EXTENDED select count(1) from MOCK_DATA where id=1

865764e6f8c9ffdb91a937ea44bb7ce02560ad9a

查看INFORMATION_SCHEMA.OPTIMIZER_TRACE表的優化器追蹤信息:

9b67678e40debb0d3f4b1d3e7501880efc73a986

fc030c447a079c21fc0583db70ad1e77c9a66399

以上是正常情況下的執行計劃,下麵進行修改統計信息數據,模擬統計信息對執行計劃的影響在哪裏

2.5 修改統計信息

隻修改innodb_table_stats的n_rows變為10,同樣執行:

SET optimizer_trace="enabled=on";

EXPLAIN EXTENDED select count(1) from MOCK_DATA where id=1

如下:

bf3c867c2bf8d3cb02b12e4a356cc2858502097c

執行計劃:

eaad84ff0495ea8279ac6b778ff07828c067256a

和之前的對比,執行計劃有明顯的變化,再次查看INFORMATION_SCHEMA.OPTIMIZER_TRACE表的優化器追蹤信息:

a7a6b615e6a8abfee5ab36afd9d2b5a6dbd6d79a

abeef24daf1917c32b21b3c2cef1e4f8afae72dd

通過對比發現,雖然生成的執行計劃使用的索引和access type沒有錯誤,但是在生成過程中的cost與之前相比已經變化明顯了。不準確的統計信息有很大可能對優化器的cost預估產生影響。所以我們可能有時候需要手工的進行統計信息的收集,除了統計信息還有很多情況會對optimizer的執行計劃生成產生影響,比如索引的數量,索引數據的分布等等

第三節 統計信息收集

統計信息收集最常用的是analyze table  和optimize table,一般情況下這兩個操作是有效的,但是也有少數情況analyze table和optimize table完全失效(獲取不了準確的統計信息),即使我們知道表和索引的數據分布並非如此,我們也無法使用analyze和optimize來獲取,此時可能就需要更精確的收集,拿上麵的表舉例子,其有16384000行數據,使用analyze 和optimize 在innodb_stats_persistent_sample_pages20的情況下,對數據量大的表和索引預估可能並不完全準確(16384000已經比較準確了),如果我們需要其預估完全準確的話(正常情況下不需要完全準確,會加重統計信息采集時間),我們可以對innodb表嚐試如下兩種方式:

1, 調大innodb_stats_persistent_sample_pages的值,然後再執行analyze table

2,單獨設置該表的STATS_SAMPLE_PAGES數量

2.1 調整innodb_stats_persistent_sample_pages

通過如下信息:

00d8a9bfff5338b1d33ed8a2c68451cb88818e5d

因為隻有一個Primary key有92634和leaf page,沒有其他的unique key,這裏

分別設置innodb_stats_persistent_sample_pages6000092634和92635,

然後與默認的20進行對比,如下:

1),set global innodb_stats_persistent_sample_pages=60000:

1292ba56633473e37d5bc27fc7b66eea019d5bfa

2),set global innodb_stats_persistent_sample_pages=92634:

f04dea1cb0fbac04b5748ecded093b1220ab59ae

3),set global innodb_stats_persistent_sample_pages=92635:

8b35f22db1e0810d2f7dd20c6604d02931f907fe

通過對比,當設置為92635(leaf page+1)時,數量才可以完全的準確,此時的mysql.innodb_index_stats表如下,已經很準確了,如下

c9456af4e244f97f9f3fb9bde60227878752e551

2.2 調整STATS_SAMPLE_PAGES

同樣分別設置STATS_SAMPLE_PAGES6000092634和92635

1),STATS_SAMPLE_PAGES=60000

9516a00679878160d98ef466ed8ab9af7f35513d

2),STATS_SAMPLE_PAGES=92634以及STATS_SAMPLE_PAGES=92634

06b78c45649a52002303a6c0d11b5cf104a5533d

3),調整STATS_SAMPLE_PAGES設置為 65535,該參數最大為65535(STATS_SAMPLE_PAGES最大值,文檔未標明,測試所得,應該是代碼限製)

f78576bafd669e59ce46eb47a65013e29961dfb4

未能達到innodb_stats_persistent_sample_pages的效果,當STATS_SAMPLE_PAGES為65535時,此時innodb_index_stats的信息如下:

c5a5708af3e1ca214f856dbc148d962c75aa6502

2.3 問題延伸

如果該表除了primary key,還有一個unique key時?sample page也需要這麼多嗎?

2.4 測試結果

通過測試,發現默認采集20個leaf page一般情況是比較準確的,故正常情況下,我們是不需要手工幹預的,可以交給mysql根據數據量的變化自動統計,太精確的采集page數量過多會造成係統的負擔,隻有當明確的得知統計信息錯誤(表中的數據分布並非如此),而且默認采集page數量使用analyze和optimize無法獲取更精確的統計信息時可以嚐試這樣做

 

問題:為何STATS_SAMPLE_PAGES最大值代碼裏限製為65535暫時不清楚為何,測試所得其最大為65535

最後更新:2017-08-18 17:02:28

  上一篇:go  【技術幹貨】聽阿裏雲CDN安防技術專家金九講SystemTap使用技巧
  下一篇:go  PostgreSQL SQL 語言:並行查詢