在平時被問及最多的問題就是關于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的性能起到了至關重要的作用。