在平時被問及最多的問題就是關於MySQL資料庫效能最佳化方面的問題,所以最近打算寫一個MySQL資料庫效能最佳化方面的系列文章,希望對初中級MySQL DBA以及其他對MySQL效能最佳化感興趣的朋友們有所協助。
資料庫屬於IO密集型的應用程式,其主職責就是資料的管理及儲存工作。而我們知道,從記憶體中讀取一個資料庫的時間是微秒層級,而從一塊普通硬碟上讀取一個IO是在毫秒層級,二者相差3個數量級。所以,要最佳化資料庫,首先第一步需要最佳化的就是IO,儘可能將磁碟IO轉化為記憶體IO。本文先從MySQL資料庫IO相關參數(緩衝參數)的角度來看看可以通過哪些參數進行IO最佳化:
•query_cache_size/query_cache_type (global)
Query cache作用於整個MySQL Instance,主要用來緩衝MySQL中的ResultSet,也就是一條SQL語句執行的結果集,所以僅僅只能針對select語句。當我們開啟了 Query Cache功能,MySQL在接受到一條select語句的請求後,如果該語句滿足Query Cache的要求(未顯式說明不允許使用Query Cache,或者已經顯式申明需要使用Query Cache),MySQL會直接根據預先設定好的HASH演算法將接受到的select語句以字串方式進行hash,然後到Query
Cache中直接尋找是否已經緩衝。也就是說,如果已經在緩衝中,該select請求就會直接將資料返回,從而省略了後面所有的步驟(如SQL語句的解析,最佳化器最佳化以及向儲存引擎請求資料等),極大的提高效能。
當然,Query Cache也有一個致命的缺陷,那就是當某個表的資料有任何任何變化,都會導致所有引用了該表的select語句在Query Cache中的快取資料失效。所以,當我們的資料變化非常頻繁的情況下,使用Query Cache可能會得不償失。
Query Cache的使用需要多個參數配合,其中最為關鍵的是query_cache_size和query_cache_type,前者設定用於緩衝ResultSet的記憶體大小,後者設定在何情境下使用Query Cache。在以往的經驗來看,如果不是用來緩衝基本不變的資料的MySQL資料庫,query_cache_size一般256MB是一個比較合適的大小。當然,這可以通過計算Query Cache的命中率(Qcache_hits/(Qcache_hits+Qcache_inserts)*100))來進行調整。query_cache_type可以設定為0(OFF),1(ON)或者2(DEMOND),分別表示完全不使用query
cache,除顯式要求不使用query cache(使用sql_no_cache)之外的所有的select都使用query cache,只有顯示要求才使用query cache(使用sql_cache)。
•binlog_cache_size (global)
Binlog Cache用於在開啟了二進位日誌(binlog)記錄功能的環境,是MySQL用來提高binlog的記錄效率而設計的一個用於短時間內臨時緩衝binlog資料的記憶體地區。
一般來說,如果我們的資料庫中沒有什麼大事務,寫入也不是特別頻繁,2MB~4MB是一個合適的選擇。但是如果我們的資料庫大事務較多,寫入量比較大,可與適當調高binlog_cache_size。同時,我們可以通過binlog_cache_use以及binlog_cache_disk_use來分析設定的binlog_cache_size是否足夠,是否有大量的binlog_cache由於記憶體大小不夠而使用臨時檔案(binlog_cache_disk_use)來緩衝了。
•key_buffer_size(global)
Key Buffer可能是大家最為熟悉的一個MySQL緩衝參數了,尤其是在MySQL沒有更換預設儲存引擎的時候,很多朋友可能會發現,預設的MySQL設定檔中設定最大的一個記憶體參數就是這個參數了。key_buffer_size參數用來設定用於緩衝MyISAM儲存引擎中索引檔案的記憶體地區大小。如果我們有足夠的記憶體,這個快取區域最好是能夠存放下我們所有的MyISAM引擎表的所有索引,以儘可能提高效能。
此外,當我們在使用MyISAM儲存的時候有一個及其重要的點需要注意,由於MyISAM引擎的特性限制了他僅僅只會緩衝索引塊到記憶體中,而不會緩衝表資料庫塊。所以,我們的SQL一定要儘可能讓過濾條件都在索引中,以便讓緩衝協助我們提高查詢效率。
•bulk_insert_buffer_size (thread)
和key_buffer_size一樣,這個參數同樣也僅作用於使用MyISAM儲存引擎,用來緩衝批量插入資料的時候臨時緩衝寫入資料。當我們使用如下幾種資料寫入語句的時候,會使用這個記憶體地區來緩衝批量結構的資料以協助批量寫入資料檔案:
insert … select …
insert … values (…) ,(…),(…)…
load data infile… into… (非空表)
•innodb_buffer_pool_size(global)
當我們使用InnoDB儲存引擎的時候,innodb_buffer_pool_size參數可能是影響我們效能的最為關鍵的一個參數了,他用來設定用於緩衝InnoDB索引及資料區塊的記憶體地區大小,類似於MyISAM儲存引擎的key_buffer_size參數,當然,可能更像是Oracle的db_cache_size。簡單來說,當我們操作一個InnoDB表的時候,返回的所有資料或者去資料過程中用到的任何一個索引塊,都會在這個記憶體地區中走一遭。
和key_buffer_size對於MyISAM引擎一樣,innodb_buffer_pool_size設定了InnoDB儲存引擎需求最大的一塊記憶體地區的大小,直接關係到InnoDB儲存引擎的效能,所以如果我們有足夠的記憶體,盡可將該參數設定到足夠打,將儘可能多的InnoDB的索引及資料都放入到該快取區域中,直至全部。
我們可以通過(Innodb_buffer_pool_read_requests – Innodb_buffer_pool_reads) / Innodb_buffer_pool_read_requests * 100%計算快取命中率,並根據命中率來調整innodb_buffer_pool_size參數大小進行最佳化。
•innodb_additional_mem_pool_size(global)
這個參數我們平時調整的可能不是太多,很多人都使用了預設值,可能很多人都不是太熟悉這個參數的作用。innodb_additional_mem_pool_size設定了InnoDB儲存引擎用來存放資料字典資訊以及一些內部資料結構的記憶體空間大小,所以當我們一個MySQL Instance中的資料庫物件非常多的時候,是需要適當調整該參數的大小以確保所有資料都能存放在記憶體中提高訪問效率的。
這個參數大小是否足夠還是比較容易知道的,因為當過小的時候,MySQL會記錄Warning資訊到資料庫的error log中,這時候你就知道該調整這個參數大小了。
•innodb_log_buffer_size (global)
這是InnoDB儲存引擎的交易記錄所使用的緩衝區。類似於Binlog Buffer,InnoDB在寫交易記錄的時候,為了提高效能,也是先將資訊寫入Innofb Log Buffer中,當滿足innodb_flush_log_trx_commit參數所設定的相應條件(或者日誌緩衝區寫滿)之後,才會將日誌寫到檔案(或者同步到磁碟)中。可以通過innodb_log_buffer_size 參數設定其可以使用的最大記憶體空間。
註:innodb_flush_log_trx_commit參數對InnoDB Log的寫入效能有非常關鍵的影響。該參數可以設定為0,1,2,解釋如下:
0:log buffer中的資料將以每秒一次的頻率寫入到log file中,且同時會進行檔案系統到磁碟的同步操作,但是每個事務的commit並不會觸發任何log buffer 到log file的重新整理或者檔案系統到磁碟的重新整理操作;
1:在每次事務提交的時候將log buffer 中的資料都會寫入到log file,同時也會觸發檔案系統到磁碟的同步;
2:事務提交會觸發log buffer到log file的重新整理,但並不會觸發磁碟檔案系統到磁碟的同步。此外,每秒會有一次檔案系統到磁碟同步操作。
此外,MySQL文檔中還提到,這幾種設定中的每秒同步一次的機制,可能並不會完全確保非常準確的每秒就一定會發生同步,還取決於進程調度的問題。實際上,InnoDB能否真正滿足此參數所設定值代表的意義正常Recovery還是受到了不同OS下檔案系統以及磁碟本身的限制,可能有些時候在並沒有真正完成磁碟同步的情況下也會告訴mysqld 已經完成了磁碟同步。
•innodb_max_dirty_pages_pct (global)
這個參數和上面的各個參數不同,他不是用來設定用於緩衝某種資料的記憶體大小的一個參數,而是用來控制在InnoDB Buffer Pool中可以不用寫入資料檔案中的Dirty Page的比例(已經被修但還沒有從記憶體中寫入到資料檔案的髒資料)。這個比例值越大,從記憶體到磁碟的寫入操作就會相對減少,所以能夠一定程度下減少寫入操作的磁碟IO。
但是,如果這個比例值過大,當資料庫Crash之後重啟的時間可能就會很長,因為會有大量的交易資料需要從記錄檔恢複出來寫入資料檔案中。同時,過大的比例值同時可能也會造成在達到比例設定上限後的flush操作“過猛”而導致效能波動很大。
上面這幾個參數是MySQL中為了減少磁碟物理IO而設計的主要參數,對MySQL的效能起到了至關重要的作用。