MySQL8.0 · 優化器新特性 · Cost Model, 直方圖及優化器開銷優化

MySQL當前已經發布到MySQL8.0版本,在新的版本中,能夠看到MySQL以前被人詬病的優化器部分作了不少的改動,因爲筆者以前的工做環境是5.6,最近切換到最新的8.0版本,本文涵蓋了一些本人感興趣的和優化器相關的部分,主要包括MySQL5.7的cost model以及MySQL8.0的直方圖功能。mysql

本文基於當前最新的MySQL8.0.12版本,主要是講下cost model 和 histogram的用法和相關代碼sql

Cost Model

Configurable cost constants數據庫

爲何須要配置cost model常量 ? 咱們知道MySQL已經發展了好幾十年的歷史,可是在優化器中依然使用了hardcode的權重值來衡量io, cpu等資源狀況,而這些權重值其實是基於多年前甚至十來年前的經驗設定的。想一想看,這麼多年硬件的發展多麼迅速。幾十上百個核心的服務器不在少數甚至在某些大型公司大規模使用,ssd早就成爲主流,NVME也在崛起。高速RDMA網絡正在走入尋常百姓家。這一切甚至影響到數據庫系統的實現和變革。顯而易見,那些hardcode的權值已通過時了,咱們須要提供給用戶可定義的方式,甚至更進一步的,可以智能的根據硬件環境自動設定。json

MySQL5.7引入兩個新的系統表, 經過這兩個系統表暴露給用戶來進行更新,以下:api

clipboard.png

你能夠經過update語句來進行更新, 例如:數組

clipboard.png

能夠看到用法也很是簡單,上面包含了兩張表:server_cost及engine_cost,分別對server層和引擎層進行配置服務器

相關代碼:

全局cache Cost_constant_cache網絡

全局cache維護了一個當前的cost model信息, 用戶線程在lex_start時會去判斷其有沒有初始化本地指針,若是沒有的話就去該cache中將指針拷貝到本地session

初始化全局cache:架構

clipboard.png

從系統表讀取配置,適用於初始化和flush optimizer_costs並更新cache:

clipboard.png

因爲用戶能夠動態的更新系統表,執行完flush optimizer_costs後,有可能老的版本還在被某些session使用,所以須要引用計數,老的版本ref counter被降爲0後才能被釋放

線程cost model初始化

• Cost_model_server

在每一個線程的thd上,掛了一個Cost_model_server的對象THD::m_cost_model, 在lex_start()時,若是發現線程的m_cost_model沒有初始化,就會去獲取全局的指針,存儲到本地:

clipboard.png

可見thd不建立本身的cost model, 只引用cache中的指針

Table Cost Model

struct TABLE::m_cost_model, 類型:Cost_model_table

其值取自上述thd中存儲的cost model對象

Cost_estimate

統一的對象類型cost_estimate來存儲計算的cost結果,包含四個維度:

clipboard.png

將來

目前來看,除非根據工做負載,通過充分的測試才能得出合理的配置值,但如何配置,什麼是合理的值,我的認爲應該是能夠自動調整配置的。關鍵是找出配置和硬件條件的對應關係。 這也是咱們將來能夠努力的一個方向。

reference:

  1. Cost Model官方文檔
  2. 官方博客1:The MySQL Optimizer Cost Model Project
  3. 官方博客2: A new dimension to MySQL query optimizations
  4. Optimizer Cost Model Improvements in MySQL 5.7.5 DMR

5.Slide: MySQL Cost Model

Related Worklog:

WL#7182: Optimizer Cost Model API

WL#7209: Handler interface changes for new cost model

WL#7276: Configuration data base for Optimizer Cost Model

WL#7315 Optimizer cost model: main memory management of cost constants

WL#7316 Optimizer cost model: Command for online updating of cost model constants

Histogram

直方圖也是MySQL一個萬衆期待的功能了,這個功能實際上在其餘數據庫產品中是很常見的,能夠很好的指導優化器選擇執行路徑。利用直方圖存儲了指定列的數據分佈。MariaDB從很早的10.0.2版本支持這個功能, 而MySQL在最新的8.0版本中也開始支持

使用

MySQL裏使用直方圖是經過ANALYZE TABLE語法來執行:

clipboard.png

舉個簡單的例子:

clipboard.png

直方圖統計信息存儲於InnoDB數據詞典中,能夠經過information_schema表來獲取

clipboard.png

從column_statistics表的定義能夠看到,有一個名爲mysql.column_statistics系統表,但被隱藏了,沒有對外暴露

如下舉個簡單的例子:

clipboard.png

從輸出的json能夠看到,在執行了上述語句後產生的直方圖,有4個bucket,數據類型爲Int, 類型爲equi-height,即等高直方圖(另一種是等寬直方圖,即SINGLETON)。每一個Bucket中,描述的信息包括:數值的上界和下界, 頻率以及不一樣值的個數。經過這些信息能夠得到比較精確的數據分佈狀況,從而優化器來根據這些統計信息決定更優的執行計劃。

若是列上存在大量的重複值,那麼MySQL也可能選擇等寬直方圖,例如上例,咱們將列k上的值更新爲一半10一半爲20, 那麼出來的直方圖數據以下:

clipboard.png

如上,對於SINGLETON類型,每一個bucket只包含兩個值:列值,及對應的累計頻率(即百分之多少的數據比當前Bucket裏的值要小或相等)

注意這裏的sampling-rate, 這裏的值爲1,表示讀取了表上全部的數據來進行統計,但一般對於大表而言,咱們可能不但願讀太多的數據,由於可能產生過分的內存消耗,所以MySQL還提供了一個參數histogram_generation_max_mem_size來限制內存的使用上限。

若是表上的DML很少,那直方圖基本是穩定的,但頻繁寫入的話,那咱們就可能須要去按期更新直方圖,MySQL自己不會去主動更新。

優化器經過histogram來計算列的過濾性,大多數的謂詞均可以使用到。具體參閱官方文檔

關於直方圖影響查詢計劃,這篇博客 及 這篇博客

相關代碼

代碼結構:

以MySQL8.0.12爲例,主要代碼在sql/histogram目錄下:

clipboard.png

建立及存儲histogram:

處理histogram的相關函數和堆棧以下:

clipboard.png

使用histogram

使用的方式就比較簡單了:

首先在表對象TABLE_SHARE中,增長成員m_histograms,其結構爲一個unordered map,key值爲field index, value爲相應的histogram對象

獲取列值過濾性的相關堆棧以下:

clipboard.png

MySQL支持多種操做類型對直方圖的使用,包括:

clipboard.png

經過直方圖,咱們能夠根據列上的條件判斷出列值的過濾性,來輔助選擇更優的執行計劃。在沒有直方圖以前咱們須要經過在列上創建索引來得到相對精確的列值分佈。但咱們知道索引是有很大的維護開銷的,而直方圖則能夠靈活的按需建立。

reference

WL#5384 PERFORMANCE_SCHEMA, HISTOGRAMS

WL#8706 Persistent storage of Histogram data

WL#8707 Classes/structures for Histograms

WL#8943 Extend ANALYZE TABLE with histogram support

WL#9223 Using histogram statistics in the optimizer

其餘

優化rec_per_key

相關worklog:

WL#7338: Interface for improved records per key estimates

WL#7339 Use improved records per key estimate interface in optimizer

MySQL經過rec_per_key 接口來估算記錄的個數(暗示每一個索引Key對應的記錄個數),但在早前版本中這個數字是整數,對於小數會取整,不能表示準確的rec_per_key,從而影響到索引的選擇,所以在5.7版本中,將其記錄的值改爲了float類型

引入數據cache狀態計算開銷

相關worklog:

WL#7168 API for estimates for how much of table and index data that is in memory buffer

WL#7170: InnoDB buffer estimates for tables and indexes

WL#7340 IO aware cost estimate function for data access

在以前的版本中,優化器是沒法知道數據的狀態,是不是cache在內存中,仍是須要從磁盤讀出來的,缺少這部分信息,致使優化器統一認爲數據屬於磁盤的來計算開銷。這可能致使低效的執行計劃。

相關代碼:

server層新增api,用於獲取表或索引上有百分之多少的數據是存儲在cache中的

clipboard.png

而在innodb層,增長了一個全局變量buf_stat_per_index (對應類型爲buf_stat_per_index_t) 來維護每一個索引在內存中的leaf page個數, 其內部實現了一個lock-free的hash結構,Key值爲(m_space_id) << 32 | m_index_id), 在讀入page時或者內存中建立新page時, 若是對應的page是leaf page,就遞增計數;當從page hash中移除時,則遞減計數。

爲了減小性能的影響,計數器是經過lock-free hash的結構存儲的,對應的結構爲ut_lock_free_hash_t。

基本的實現思路是:hash是一個定長的數組,數組元素爲(key, val), 根據Key計算一個hash值再模上array size, 找到對應的槽位, 若是槽位被佔用了,則向右查找一個空閒的slot。

當數組滿了的時候,會建立一個新的更大的數組,在數據還沒Move到這個新hash以前,全部的search都須要查詢兩個數組。當全部的記錄到遷移到新數組,而且沒有線程訪問老的數組時,就能夠把老的hash刪除掉了。

在hash中存儲的counter自己,也考慮到多核和numa架構,避免同時更新引發的cpu cache失效。在大量core的場景下這個問題可能很明顯。Innodb封裝計數操做到類ut_lock_free_cnt_t中,使用數組維護counter, 按照cpu no做爲index更新,須要獲取counter值時則累加數組中的值。

這個Lock free hash並非個通用場景的hash結構:例如處理衝突的時候,可能佔用其餘key的槽位,hash不夠用時,須要遷移到新的array中。實際上mysql自己實現了一個lf_hash,在擴展Hash時無需遷移數據,有空單獨開篇博客講一下。

你能夠從information_schema.innodb_cached_indexes表中讀取到每一個索引cache的page個數。

當定義好接口,而且Innodb提供相應的統計數據後,優化器就能夠利用這些信息來計算開銷:

• Cost_model_table::page_read_cost

• Cost_model_table::page_read_cost_index

本文做者:zhaiwx_yinfeng

閱讀原文

本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索