18-MySQL DBA筆記-MySQL Server調優

第18章 MySQL Server調優
本章將爲讀者介紹針對MySQL Server的優化,這也是DBA最熟悉的領域之一。
首先咱們介紹MySQL的主要參數,而後,講述常見硬件資源的優化。
咱們假設讀者已經具有了足夠的基礎知識,因此,本章將更多的針對一些特定的主題進行敘述。

18.1 概述
衡量數據庫性能的指標,通常衡量數據庫的性能有兩個指標:響應時間和吞吐率。
響應時間又包括等待時間和執行時間。
咱們進行優化的主要目的是下降響應時間,提升吞吐率。
下面咱們來看下MySQL是如何執行優化和查詢的? 大體的步驟以下所示。
1)客戶端發送SQL語句給服務器。
2)若是啓用了Query Cache,那麼MySQL將檢查Query Cache,若是命中,就返回Query Cache裏的結果集,不然,執行下一個步驟。
3)MySQL Parser解析SQL,MySQL優化器生成執行計劃。
4)MySQL查詢處理引擎執行此執行計劃。
MySQL性能優化顯然是要對以上的部分環節或全部環節進行優化,儘可能下降各個環節的時間,以提升吞吐率。
對於性能的優化,正確的策略是衡量各個環節的開銷,優化開銷大的環節,而不是使用網上的一些所謂的參數調優和腳本調優,
由於他們並非針對你的特定狀況而進行的調優,只是一些泛泛的建議,每每幫助不大。
對於客戶端來講,發送SQL語句的開銷通常很小,若是是響應緩慢的網絡,網絡延時較高,那麼能夠考慮使用長鏈接或鏈接池等手段進行加速,
或者一次發送多條語句,或者使用存儲過程等手段減小網絡包的往返次數。
本書主要聚焦於後面的3個步驟,咱們須要關注Query Cache是如何加速響應;如何進行查詢優化,生成良好的執行計劃;
實際查詢處理過程當中對於I/O、CPU、內存等資源的使用是怎樣的。
咱們須要儘可能確保高效地利用資源,突破資源的限制。
以前的開發篇和運維篇章已經講述了許多基礎知識,這裏再也不贅述,本章我將主要從系統資源和MySQL參數設置的角度,講述一些咱們須要關注的優化點。 node

18.2 MySQL的主要參數
本節將列舉一些主要的參數,下面將詳細介紹各個參數。
1.innodb_buffer_pool_size
一個簡單的策略是若是數據庫很大,遠遠超過內存,那麼應設置儘量大的緩衝池(buffer pool)。
若是數據庫較小,通常來講,緩衝池的大小設置爲稍大於數據庫的10%就能夠了,大於10%是由於MySQL不僅是緩存數據頁,還有一些額外的開銷。
若是咱們使用ps命令檢查MySQL實際佔用的內存,就會發現實際分配的內存會比咱們設定的內存要大一些。
更合適的策略是衡量你的熱點數據的大小,若是設置的緩衝區能容納絕大部分的熱點數據,沒有產生過多的物理讀,那麼這個設置就是比較合理的設置。
須要注意的是,不要設置得過大,由於咱們須要給操做系統和其餘程序預留內存,增長的負荷也會致使更多的內存使用,
咱們還可能須要預留內存以用於文件緩存,好比InnoDB的日誌文件、MySQL的二進制日誌文件等。
要注意32位系統的內存限制,超過了內存限制,可能會致使實例崩潰,系統宕機。
通常計算MySQL須要多少內存比較難,也難以預測,比較可靠的方式是查看目前的生產環境的內存消耗。
MySQL所消耗的內存還和鏈接數有關,每一個鏈接所消耗的內存總量將依賴於負荷,
若是查詢很複雜,那麼它會消耗更多的內存,若是隻是簡單的查詢,平時基本上是sleep的狀態,那麼它實際佔用的內存就不多,
因此,對於鏈接數多的業務,你應該實際觀察下,操做系統下實際佔用的內存和你設置的InnoDB緩衝池之間的區別,
以衡量是否要調整設置,設置一個更安全一點的參數值,以避免鏈接數暴漲消耗了過多的內存。
圖18-1描述了存儲的訪問層次,對於圖18-1中所示的架構,上一層應儘量緩存下一層的「熱點」數據,也就是說,咱們須要平衡好內存和磁盤的成本,儘可能避免磁盤訪問,
對於MySQL來講,內存的主要部分就是InnoDB緩衝池,優化好了InnoDB緩衝池的訪問,就成功了一大半,
通常採起的策略是利用空間和時間的局部性,頻繁地訪問,應該儘量去訪問內存而不是磁盤,
因爲數據的訪問可能會很複雜,也許1%的緩存未命中(cache miss)須要幾十乃至上百GB的內存來避免,而不只僅是cache miss對應的數據大小,
因此咱們還要充分理解數據庫的物理設計和邏輯設計,設計出合理的方案,減小cache miss致使的物理讀。
須要清楚的一個道理是,通常而言,數據庫系統的專用存儲系統比操做系統的存儲系統高效得多。
InnoDB緩衝池就是如此,而MyISAM仍然是須要OS來緩存數據的,加速訪問,因此每每表現得不那麼好。 mysql

2.innodb_flush_method
這個選項只在Unix系統上有效。
若是這個選項被設置爲fdatasync(默認值),那麼InnoDB將使用fsync()來刷新數據和日誌文件。
若是被設置爲O_DSYNC,那麼InnoDB將使用O_DSYNC來打開並刷新日誌文件,但使用fsync()來刷新數據文件。
若是指定了O_DIRECT(在一些GNU/Linux版本上可用),那麼InnoDB將使用O_DIRECT來打開數據文件,並使用fsync()來刷新數據和日誌文件。
筆者的建議是設置innodb_flush_method=O_DIRECT,由於這樣設置能夠避免雙重緩衝,讓數據庫跳過文件系統緩衝直接和設備進行交互,
須要留意的是,若是你的磁盤作了RAID,那麼你必須使用帶電池的RAID卡。 sql

3.innodb_log_file_size
日誌組裏每一個日誌文件的大小。
早期的版本中,在32位的計算機上,日誌文件的合併大小必須小於4GB。默認是5MB。
不太好去肯定innodb_log_file_size這個參數的大小,早期MySQL版本的配置文件裏建議的InnoDB緩衝大小的25%是沒有什麼道理的,
首先InnoDB緩衝不必定就設置對了,並且InnoDB事務日誌通常和你的日誌寫入量、寫入頻率有關係,和你的緩衝池大小不存在必然的關係。
MySQL的災難恢復分爲redo和undo兩個過程,redo即找到日誌文件裏記錄的已經更改了可是並未寫入數據文件的記錄,而後應用這些日誌。
undo即回滾那些沒有提交的操做,undo的時候,數據庫已經能夠訪問了,可是undo的那部分數據還不能更改。
MySQL在切換事務日誌的時候,可能會進行一次「check point」的操做,將部分數據寫入到磁盤,也就是說,要確保咱們的緩存裏比日誌還舊的數據寫入了磁盤。
其餘時刻也可能發生「check point」。
若是數據庫宕機,MySQL從新啓動,那麼,它會去事務日誌裏找到「check point」的標記信息。
在這個「check point」標記以前的數據, 咱們能夠認爲都已經寫入到了磁盤。
那麼,咱們就只須要執行這個「check point」以後的全部操做,應用這些日誌到數據庫便可。
事務日誌不能太小,不然可能會致使性能問題。
事務日誌是循環寫的,先寫第一個日誌文件,再寫第二個日誌文件,而後又會去寫第一個日誌文件,而在覆蓋舊的日誌以前,須要確保咱們的緩存裏比日誌還舊的數據已經寫入磁盤。
若是事務日誌太小,那麼磁盤的I/O操做就可能會變得很頻繁,由於MySQL必須寫入一些髒數據到數據文件中。
一次性刷新大量數據,可能會致使性能降低。
事務日誌也不能太大了,由於這個時刻,咱們的「check point」會不怎麼頻繁,那麼MySQL的災難恢復可能須要更長的時間,由於它須要應用更多的日誌。
生產環境的恢復速度將取決於應用日誌的進度,一個1GB的事務日誌,若是要所有應用,有可能須要應用半個小時以上來執行恢復。
咱們能夠配置事務日誌能夠寫入半個小時到1個小時的日誌。這樣對於大部分應用已經足夠了。
過小了,會頻繁切換日誌;太大了,可能會致使故障恢復的時間過長。個人經驗值是256~512MB。
日誌的寫入量能夠查看變量innodb_os_log_written。咱們能夠每隔一分鐘查看一次,統計每分鐘寫入的日誌量。
下面的命令將會每分鐘檢查一第二天志的寫入量。
mysqladmin extended -uroot -pxxxxxxxx -r -i 60 -c 3 |grep "innodb_os_log_written"
若是由上面的命令得知每分鐘寫入量爲10MB,那麼咱們配置能夠連續寫45分鐘的日誌。
默認有2個日誌,那麼每一個日誌的大小=10*45/2=225,大約等於 256MB,那麼咱們能夠配置innodb_log_file=256MB。
若是得出的結論是InnoDB日誌須要幾個GB那麼大,那麼極可能是不正常的,你要深究爲何會寫入這麼大的日誌,爲何有這麼多/大的變動,你可能須要在應用層就規避這種狀況。 數據庫

4.innodb_flush_log_at_trx_commit
當innodb_flush_log_at_trx_commit被設置爲0時,日誌緩衝將每秒一次被寫到日誌文件中,而且對日誌文件進行磁盤操做的刷新,可是在事務提交時不進行任何操做。
當這個值爲1(默認值)時,在每一個事務進行提交時,日誌緩衝將被寫到日誌文件,且把對日誌文件的變動刷新到磁盤中。
當設置爲2時,在每一個事務進行提交時,日誌緩衝將被寫到文件,但不會對日誌文件進行到磁盤操做的刷新,對日誌文件的刷新每秒發生一次。
咱們能夠看到,設置爲2比設置爲0更安全。
咱們的生產環境通常推薦設置爲innodb_flush_log_at_trx_commit=2,由於它能夠兼顧效率和必定的安全性,理想狀況下,最多可能丟失1秒的事務。
若是設置爲1, 則對於性能的影響會很大,由於每次提交事務,都會伴隨着磁盤I/O的操做,須要把數據刷新到磁盤,I/O可能會成爲瓶頸,
對於高安全性的數據,在可以知足I/O性能的前提下,能夠考慮將其設置爲1。緩存

5.sync_binlog
這個參數是設置,每當寫了sync_binlog次二進制日誌後,把日誌實際刷新到磁盤中,默認值是0,不與硬盤同步。
絕大部分公司的生產環境廣泛使用的是auto commit模式(自動事務提交),每次寫一個語句,就會寫一次二進制日誌,若是不是自動事務提交,那麼每一個事務將寫入一次二進制日誌。
若是設置爲1,那麼最多丟失1條記錄(事務),這是最安全的選擇。
生產環境的推薦設置是8~20,這樣能夠兼顧效率和安全,
若是設置爲1,你可能會碰到I/O瓶頸,你須要選用更好的SSD設備,或者使用帶電池的RAID卡來緩解I/O瓶頸,優化文件系統也是一個選項,ext4和xfs就比ext3的表現要好得多。
生產實踐證實,sync_binlog會對事務吞吐率有比較大的影響。
事務日誌、數據文件、二進制日誌文件是須要同步的。
數據庫能夠看做一個巨大的同步機,各個組件之間存在複雜的通訊和同步等待,若是sync_binlog操做較慢,那麼可能對整個系統的吞吐率形成嚴重的影響。
有一個相關的參數innodb_support_xa咱們須要瞭解。i
nnodb_support_xa設置爲1時,這個變量容許InnoDB支持XA事務,即Distributed(XA)Transactions分佈式事務,MySQL部分支持XA事務。
通常互聯網公司的業務不須要分佈式事務,並且應該儘可能避免,那麼,是否是要禁用innodb_support_xa呢?
並非像一些人理解的那樣,沒有分佈式事務,就不要這個特性,MySQL內部會使用XA來協調存儲引擎和二進制日誌,以確保災難恢復功能工做正常,因此應該開啓它。
MySQL存儲引擎各自獨立,互不知曉其餘引擎的狀態,所以,跨引擎的事務能夠看做一個分佈式的事務,且須要一個第三方來協調它,這個第三方就是MySQL Server。
咱們能夠把「二進制日誌」看做一個「存儲引擎」。MySQL Server須要協調二進制日誌的寫入和InnoDB事務的寫入。
生產環境爲了安全和複製,必須開啓binlog和innodb_support_xa。
若是將sync_binlog參數設置爲1,那麼存儲引擎和二進制日誌須要徹底同步。
若是二進制日誌所在的磁盤存在性能問題,那麼也會影響到咱們的事務提交。
生產繁忙的系統,有時常常會看到許多commit慢查詢,就是由於二進制日誌的寫入瓶頸致使了InnoDB事務的提交緩慢。 安全

6.innodb_thread_concurrency
InnoDB試着在InnoDB內部保持操做系統線程的數量少於或等於這個參數給出的限制。
官方建議是將其設置爲處理器數目加磁盤數之和,對於高併發事務,也許你應該把這個值設置得更大一些。
對於一些資源等待異常的狀況,後來的事務會被已經在等待隊列中的事務卡住,你能夠經過臨時增大這個值,讓更多的事務併發執行。 性能優化

7.innodb_max_dirty_pages_pct
這是一個範圍從0到100的整數。默認是90。
InnoDB中的主線程試着從緩衝池寫數據,使得髒頁(沒有被寫的頁面)的百分比不超過這個值。
能夠運行以下命令進行修改:SET GLOBAL innodb_max_dirty_pages_pct = value;
生產環境建議將其設置爲更小的值:50~75。 服務器

8.read_buffer_size
每一個線程連續掃描時爲掃描的每一個表分配的緩衝區的大小(字節)。
若是進行屢次連續掃描,可能還須要增長該值,默認值爲131072。
只有當查詢須要的時候,才分配read_buffer_size指定的所有內存。 網絡

9.read_rnd_buffer_size
排序後,按照排序後的順序讀取行時,則經過該緩衝區讀取行,以免搜索硬盤。
將該變量設置爲較大的值能夠改進ORDER BY的性能。
可是,這是爲每一個客戶端分配的緩衝區,所以你不該該將全局變量設置爲較大的值。
相反,只爲須要運行大查詢的客戶端更改會話變量便可。 數據結構

10.sort_buffer_size
每一個排序線程分配的緩衝區的大小。增長該值能夠加快ORDER BY或GROUP BY操做。
查詢須要排序的時候(如filesort)才分配sort_buffer_size指定的內存,不要設置得過大,不然小的排序也須要大的內存。
在咱們肯定須要進行大的排序操做的時候,咱們能夠在會話級別定義大的排序sort_buffer_size。

11.myisam_sort_buffer_size
當運行REPAIR TABLE命令修復表、運行CREATE INDEX命令建立索引或運行ALTER TABLE命令修改表結構時,排序過程當中需分配的緩衝區,能夠在會話級別進行設置。
12.query_cache_size
爲緩存查詢結果分配的內存的數量,默認值是0,即禁用查詢緩存。
請注意:即便將query_cache_type設置爲0也將分配query_cache_size設置的內存。從新定義大小會清除原來緩存的結果集。
對於寫操做很頻繁的應用,能夠禁用它,以消除失效Query Cache的開銷,這樣可能得到性能上的提高。禁用的辦法是設置 query_cache_size=0。
建議生產環境中將其設置爲64MB~256MB,不要太大,對於絕大部分業務,256MB就已經足夠了。
若是要啓用QueryCache,那麼須要同時設置query_cache_type=1。

13.join_buffer_size
用於徹底鏈接(當不使用索引的時候使用鏈接操做)的緩衝區的大小。給不能利用索引的鏈接使用的。
多表鏈接須要多個join buffer,因此一個查詢可能要用到多個join_buffer_size。
14.max_connections 容許的並行客戶端鏈接數目。
15.max_connect_errors
若是中斷與主機的鏈接超過了該數目,則該主機會阻塞後面的鏈接。
你能夠用FLUSH HOSTS語句解鎖鎖定的主機。默認值過小了,能夠設置在5000以上。
16.skip-name-resolve
不要解析客戶端鏈接的主機名,只使用IP。
若是你要使用該項,那麼受權表中的全部Host列值必須爲IP號或localhost。
生產環境中必須設置這個參數,不然反向解析緩慢時,會致使MySQL鏈接緩慢,出現嚴重的性能問題。

18.3 MySQL內存優化
18.3.1 如何避免使用swap
這裏咱們僅僅討論Linux系統下的swap(交換),其餘系統,如Solaris,會有一些區別。
簡單地說,swap指的是將最近不常使用的內存移動到下一級存儲裏(硬盤),在須要的時候,再載入到主內存中。
swap空間通常是指咱們磁盤上的預先配置的一個分區,也能夠是文件,用於將內存中的數據交換到磁盤上。
物理內存和swap空間之和就是咱們可用的虛擬內存的大小。
當咱們的內存不夠了或應用程序消耗了太多的內存,操做系統會把不須要當即使用的數據傳輸到磁盤,以釋放內存空間,
若是之後須要了,再從磁盤上覆制回內存,這樣一個過程也稱爲交換(swap out/swap in)。
經過這樣一個交換的動做,增長了實際可用的內存,能夠提升系統的吞吐能力,可是數據的交換若是太頻繁,就會大大增長磁盤的延時時間,可能會致使嚴重的性能問題。
通常來講,數據庫負載,須要儘可能避免使用到swap。
咱們能夠使用free、vmstat、sar等命令查看 swap使用的統計信息。
經過free命令,若是咱們看到了一小部分swap空間被使用,那麼這通常是正常的,不須要額外關注,咱們須要關注的是是否有正在進行的swap in/swap out操做。
一些人建議將swap分區設置爲物理內存的大小,對於Linux系統來講,這個建議有必定的意義,爲了避免浪費過多的硬盤空間,建議使用以下的策略。
若是MEM<2GB,那麼SWAP=MEM×2,不然SWAP=MEM+2GB。
對於內存很是大的系統,如32GB、64GB,咱們能夠使用0.5×內存大小。
MySQL避免使用swap的一些方法以下。
(1)設置memlock
可在參數文件中設置memlock,將MySQL InnoDB buffer鎖定到內存,永不使用swap,
但這是有風險的,若是內存不夠大,MySQL會被操做系統的OOM機制殺掉。
若是由於物理內存故障致使內存總量變少,那麼它可能還會致使系統沒法順利啓動,由於MySQL會不斷申請內存。
(2)使用大內存頁
能夠設置MySQL使用Linux系統的大內存頁(操做系統和MySQL都須要設置),Linux系統的大內存頁是不會被交換出去的。
(3)設置vm.swappiness
能夠設置vm.swappiness=0,以減小使用swap的可能。
swappiness參數,它能夠在運行時進行調優。
這個參數決定了,將應用程序移動到交換空間而不是移動到正在減小的高速緩存和緩衝區中的可能性,
下降 swappiness能夠提升交互式應用程序的響應能力,可是會下降系統的整體吞吐量。
(4)禁用NUMA或調整NUMA
在生產環境中你可能會碰到在沒有內存壓力的狀況下,也發生swap in/swap out的狀況,致使不定時出現的性能問題。
尤爲是在使用了大的buffer pool size的狀況下,這通常是由於使用了NUMA技術,須要考慮禁用NUMA或更改程序分配內存的方式,
numactl命令能夠實現這個目的,使用方式爲:numactl --interleave all command,例如,/usr/bin/numactl --interleave=all mysqld,詳情請參考18.3.2節NUMA。
注意:
不要去禁用swap,並非全部內核在swap分區被禁用的狀況下都能工做得很好,這可能會致使服務異常,
某個服務在禁用swap的時候可以工做得很好,並不表明全部程序都能很好地工做。
並且內存不夠的機率更高了,當使用了過多的內存時,程序更容易被操做系統的OOM機制殺掉。
咱們須要意識到,swap分區爲咱們處理問題留了一個緩衝,給咱們爭取到了處理問題的時間,
因此咱們不要把swap分區設置得太小,相對於你所得到的收益,「浪費」一些磁盤空間是值得的。

18.3.2 NUMA
從系統架構來講,目前的主流企業服務器能夠分爲3類:SMP(Symmetric Multi Processing,對稱多處理架構)、
NUMA(Non-Uniform Memory Access,非一致存儲訪問架構)和MPP(Massive Parallel Processing,海量並行處理架構)。
下面咱們來看下SMP和NUMA架構。
1.SMP
如圖18-2所示的是一個SMP系統。
在這樣的系統中,全部的CPU共享所有資源,如總線、內存和I/O系統等,多CPU之間沒有區別,都可平等地訪問內存和外部資源。
由於CPU共享相同的物理內存,每一個CPU訪問內存中的任何地址所須要的時間也是相同的,
所以SMP也被稱爲一致存儲器訪問結構(Uniform Memory Access,UMA),尤爲是在和NUMA架構對比的時候。
對於SMP服務器而言,每個共享的環節均可能是瓶頸所在。
因爲全部處理器都共享系統總線,因此當處理器的數目增多時,系統總線的競爭衝突也會加大,系統總線成爲了性能瓶頸,
因此其擴展性有限,這種架構已經被逐步淘汰,但在CPU內部還有應用,單個CPU的全部核共享訪問該CPU的本地內存。

2.NUMA
如圖18-3所示的是NUMA系統。
在這種架構中,每顆CPU有本身獨立的本地內存,CPU節點之間經過互聯模塊進行鏈接,訪問本地內存的開銷很小,延時比訪問遠端內存(系統內其餘節點的內存)小得多。
這也是非一致存儲訪問NUMA的由來。
綜上所述能夠得知,NUMA對內存訪問密集型的業務更有好處,NUMA系統提高了內存訪問的局部性,從而提升了性能。
關於CPU信息,咱們能夠查看/proc/cpuinfo。
對於NUMA的訪問統計,咱們能夠使用numastat命令進行檢查,也能夠查看/sys/devices/system/node/node*/numastat文件。
如圖18-4所示,NUMA使用了default策略,這將致使內存分配的不均衡,numastat命令的輸出以下。
各項輸出的含義以下:
numa_hit:在此節點分配內存並且成功的次數。
numa_miss:因爲內存不夠,在此節點分配內存失敗轉而在其餘節點分配內存的次數。
numa_foreign:預期在另外一個節點分配內存,但最終在此節點分配的次數。
interleave_hit:交錯分佈策略分配內存成功的次數。
local_node:一個運行在某個節點的進程,在同一個節點分配內存的次數。
other_node:運行在其餘節點的進程,在此節點分配內存的次數。
在Linux上NUMA API支持4種內存分配策略,具體以下:
缺省(default):老是在本地節點分配(分配在當前線程運行的節點上)。
綁定(bind):分配到指定節點上。
交織(interleave):在全部節點或指定的節點上交織分配。
優先(preferred):在指定節點上分配,失敗後在其餘節點上分配。
綁定和優先的區別是,在指定節點上分配失敗時(如無足夠內存),綁定策略會報告分配失敗,而優先策略會嘗試在其餘節點上進行分配。
強制使用綁定有可能會致使前期的內存短缺,並引發大量換頁。
咱們能夠檢查程序具體的內存分配信息,假設pid是mysqld的進程ID,經過查看/proc/pid/numa_maps這個文件,咱們能夠看到全部mysqld所作的分配操做。
各字段的顯示以下:
2aaaaad3e000 default anon=13240527 dirty=13223315
swapcache=3440324 active=13202235 N0=7865429 N1=5375098
各字段及其解析以下:
2aaaaad3e000:內存區域的虛擬地址。實際上能夠把這個看成該片內存的惟一ID。
default:這塊內存所用的NUMA策略。
anon=number:映射的匿名頁面的數量。
dirty=number:因爲被修改而被認爲是髒頁的數量。
swapcache=number:被交換出去,可是因爲被交換出去,因此沒有被修改的頁面的數量。這些頁面能夠在須要的時候被釋放,可是此刻它們仍然在內存中。
active=number:「激活列表」中的頁面的數量。
N0=numberand N1=number:節點0和節點1上各自分配的頁面的數量。
咱們能夠使用numactl命令顯示可用的節點。
numactl --hardware
available: 2 nodes (0-1)
node 0 size: 64570 MB
node 0 free: 8556 MB
node 1 size: 64640 MB
node 1 free: 1982 MB
node distances:
node 0 1
0: 10 20
1: 20 10
如上命令告訴咱們,系統有兩個CPU節點:node0、node1。每一個節點分配了64GB的內存。
distance衡量了訪問內存的成本,系統認爲訪問本地節點內存的成本是10,訪問遠端內存的成本是20。
NUMA架構存在的一個問題是:
對於NUMA架構,Linux默認的內存分配方案是優先在請求線程當前所處的CPU的本地內存上嘗試分配空間,通常是node0。
若是內存不夠,系統就會把node0上已經分配的內存交換出去,以釋放部分node0的內存,儘管node1上還有剩餘的內存,可是系統不會選擇向node1去申請內存。
顯然,swap的成本遠比訪問遠端內存的成本高,這將致使不定時地出現性能問題。
解決辦法具體以下:
1)關閉NUMA。
若是是單機單實例,則建議關閉NUMA,關閉的方法有以下兩種。
硬件層,在BIOS中設置關閉。
OS內核,啓動時設置numa=off。
可用相似以下的方式進行修改。
[root@db1000 ~]# cat /proc/cmdline
ro root=LABEL=/ rhgb quiet
vi /etc/grub.conf
kernel /vmlinuz-2.6.18-164.el5 ro root=LABEL=/ rhgb quiet numa=off
確認NUMA是否關閉,檢查numactl--show的輸出信息。
[root@db1000 home]# /usr/bin/numactl --show
policy: default
preferred node: current
physcpubind: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
cpubind: 0
nodebind: 0
membind: 0
關閉以前這個命令會顯示多個節點的信息,輸出結果以下所示。
policy: default
preferred node: current
physcpubind: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
cpubind: 0 1
nodebind: 0 1
membind: 0 1
而關閉以後則只會顯示一個節點的信息,nodebind項只有一個值0。咱們也能夠檢查啓動信息dmesg | grep-i numa。
2)使用numactl命令將內存分配策略修改成interleave(交叉)或綁定CPU
可經過修改單實例啓動腳本mysql.server或多實例啓動腳本mysqld_multi,例如,修改msyqld_multi腳本(MySQL 5.1)320行
將$com="$mysqld"更改成$com="/usr/bin/numactl--interleaveall$mysqld";
也能夠修改啓動腳本和參數,綁定MySQL的各個實例到固定的CPU節點,筆者更推薦使用這種方式。
下面的例子,在節點0的CPU上運行名爲program的程序,而且只在節點0和1上分配內存。
numactl --cpubind=0 --membind=0,1 program
下面的例子,在節點1上運行$MYSQLD程序,只在節點內分配內存。
numactl --cpunodebind=1 --localalloc $MYSQLD
3)設置參數memlock。
MySQL進行初始化啓動的時候,就已經預先把InnoDB緩衝池的內存鎖住了,即設置參數memlock等於1,
設置這個參數,也有必定的風險,若是內存不夠,可能會致使系統啓動不正常,由於MySQL Server會不斷申請內存。
4)使用大內存頁。
還有一些其餘的輔助手段。
配置vm.zone_reclaim_mode=0使得內存不足時傾向於向其餘節點申請內存。
echo-15>/proc/<pid_of_mysqld>/oom_adj,將MySQL進程被OOM_killer強制kill的可能性調低。

18.4 MySQL CPU優化
系統的性能通常取決於系統全部組件中最弱的短板,CPU、內存、I/O、網絡均可能會成爲瓶頸所在。
現實中,通常是CPU瓶頸或I/O瓶頸,I/O瓶頸也多是因爲內存不夠所致使的。
CPU的瓶頸通常是大量運算和內存讀取所致使的,好比加密操做、索引範圍查找、全表掃描等。
生產環境中出現CPU瓶頸每每是由於大量的索引範圍查找或鏈接了太多表。
I/O瓶頸每每是由於內存已經不能保存住數據庫的熱數據,所以讀寫操做必須訪問實際的物理磁盤,從而致使過多的物理讀。
實際生產環境中,更多的會碰到I/O瓶頸,而不是CPU瓶頸,你能夠使用top或mpstat判斷數據庫服務器是否存在CPU瓶頸。
因爲MySQL在多CPU主機上的擴展性有限,不能充分利用多CPU的主機,因此生產中可能會在同一個主機上部署多個實例。
有時咱們會綁定MySQL實例到某個CPU節點上。
若是想要優化性能,那麼咱們更傾向於選取速度更快的CPU,而不是增長CPU。
從理論上來講,若是操做比較集中於一些資源對象,瓶頸可能是由於鎖和隊列等待,那麼這個時候應該選取更強勁的CPU。
而若是操做分散於諸多不相干的資源上,那麼併發程度能夠更高,能夠傾向於使用更多的CPU,但可否使用更多的CPU、 併發多線程執行操做,還要受制於存儲引擎的設計。
就目前來講,InnoDB的擴展性仍是不佳。
下面咱們來看看CPU的高級特性。
PC Server上有一種節能模式,通常是處於關閉的狀態,這種電源管理技術能夠在負載低的時候,調低CPU的時鐘速度,下降能耗,
但這種技術並不能和突發的負荷協做得很好,有時會來不及調整時鐘以響應忽然的高併發流量。
還有另一種電源管理技術,它經過分析當前CPU的負載狀況,智能地徹底關閉一些用不上的核心,而把能源留給正在使用的核心,並使它們的運行頻率更高,從而進一步提高性能。
相反,須要多個核心時,應動態開啓相應的核心,智能調整頻率。
這樣,就能夠在不影響CPU的TDP(熱功耗設計)的狀況下,把核心工做頻率調得更高。
這種加速技術可能會破壞咱們的性能規劃,由於系統的行爲並非「線性」的了。

18.5 MySQL I/O優化
18.5.1 概述
咱們的生產環境通常是OLTP應用,I/O瓶頸通常來自於隨機讀寫,隨機讀的消除和寫的緩解主要靠緩存,因此咱們要確保MySQL的緩衝區可以緩存大部分的熱點數據。
固然,也沒有必要緩存全部的熱點數據,能夠接受必定的緩存未命中(cache miss)。
注意,傳統的一個調優方法是基於命中率進行調優,更靠譜的方案是基於緩存未命中的狀況進行調優,雖然有時命中率很高了,
但只要緩存未命中次數達到必定的頻率,你就會碰到I/O瓶頸。
數據庫引擎比操做系統或RAID更瞭解數據,可以更高效地訪問數據,文件系統和RAID層面的預讀要關掉,由於它們幫不上什麼忙,應該交給數據庫以更智能地判斷數據的讀取。
內存的隨機讀寫速度比硬盤的隨機讀寫速度快了幾個數量級,因此若是有I/O的性能問題,那麼添加內存會是最簡便的方案。
數據庫緩衝是調優的重點,咱們須要確保數據庫緩衝可以緩存大部分的熱點數據,
理論上來講,若是數據庫緩衝已經不夠了,那麼文件系統或RAID緩衝也沒有什麼用,由於它們要小得多,且不瞭解數據,
緩存應該考慮在更接近用戶的地方進行優化,因爲應用比數據庫更瞭解數據,
因此對於高併發的業務,客戶端/應用程序的本地內存或緩存服務(如 Memcached)會比MySQL更有效率,提供更好的擴展性。
順序讀寫不管是在內存仍是在磁盤中,都比隨機讀寫更快。通常是不用考慮特殊的緩存策略。
對於機械硬盤,因爲磁盤的工做原理,順序讀寫的速度比隨機讀寫速度快得多,咱們須要着重優化隨機讀寫,儘可能減小隨機讀,以提升吞吐。
對於SSD,雖然順序讀寫也很快,相對而言,隨機讀寫並無差太多,並且優化隨機讀寫也不是那麼迫切,
可是仍是有必要優化大量隨機讀寫的SQL,由於隨着訪問量的上升,貢獻大量隨機讀寫的SQL,將會很快致使整個系統出現瓶頸。
隨機讀寫每每來自質量不高的SQL,這些SQL每每是由於索引策略不佳或錶鏈接過多,從應用層優化或進行索引優化,會更有效果,也更具可行性。
文件碎片也可能會致使更多的隨機I/O,儘管數據庫是順序訪問數據的,可是I/O卻不是順序的,
MySQL自身並無提供工具來檢查數據文件是否碎片不少,咱們也不建議頻繁地進行表的重建和優化,
可是在進行了大批量數據操做以後,好比大量刪除數據以後,在不影響服務的前提下,優化一下表(OPTIMIZE TABLE)仍是可取的。
對於OLAP應用,I/O調優和OLTP有些類似,也是要先考慮應用調優和SQL調優,儘可能減小I/O操做,若是必需要執行大量的I/O操做,那麼應該儘可能將其轉換爲順序讀寫。

18.5.2 選擇合適的I/O大小
通常來講,MySQL的塊大小是操做系統塊的整數塊,你能夠經過命令getconf PAGESIZE來檢查操做系統的塊大小,更大的I/O大小,意味着更大的吞吐,
尤爲是對於傳統的機械硬盤,一次更大的I/O,意味着不須要進行屢次I/O,能夠減小尋道的時間。
對於數據庫,因爲每每是一些隨機記錄的檢索,所以並不須要一次性讀取大量的記錄,因此一次I/O不須要太大。
許多人把默認的數據庫的塊大小調整爲8KB,以得到更高的性能。

18.5.3 日誌緩衝如何刷新到磁盤
對於數據庫的I/O性能調整,須要在性能和數據的安全性上求得平衡。
若是生產環境有嚴重的I/O性能問題,那麼它每每是由程序的不良設計形成的。
一個應用級別的SQL調整,可能就能解決了問題。而從操做系統的I/O層面可能就會無解。
InnoDB使用了數據緩衝和事務日誌,數據緩衝大小、日誌大小、日誌緩衝,InnoDB如何刷新數據和緩衝,都會對性能產生影響。
InnoDB的髒數據並非立刻寫入數據緩衝(數據文件)的,而是會先寫日誌緩衝(日誌文件),將髒數據暫時保留在數據緩衝區中,
這是一種常見的數據庫持久化的技術,這些日誌記錄了數據變動,能夠用來作故障恢復。
數據的讀寫通常是隨機讀寫,而日誌的寫入,是順序寫入,日誌寫入的要效率高得多,經過延緩數據的持久化,能夠將數據更高效率地寫入到磁盤中。
InnoDB在緩衝區滿的狀況下會將日誌緩衝區刷新到磁盤,通常不須要調整日誌緩衝區的大小(innodb_log_buffer_size),除非有不少有BLOB字段的記 錄,
innodb_log_buffer_size的大小默認是1MB,建議是1~8MB。
咱們經過配置innodb_flush_log_at_trx來控制如何將日誌緩衝刷新到磁盤,innodb_flush_log_at_trx的值可設置爲0、一、2,默認爲1。
設置爲1的狀況下,每一個事務提交都要寫入磁盤,這是最安全的作法,而設置爲其餘值時,可能會丟失事務。
通常機械磁盤受磁盤旋轉和尋道的限制,最多隻能達到幾百次IO/每秒,因此這個設置會嚴重下降事務併發,
若是你數據庫的安全性要求很高,那麼設置innodb_flush_log_at_trx爲1,這時你可能要把日誌文件放在更好的磁盤設備上,如SSD設備或帶電池的磁盤陣列上。
若是將innodb_flush_log_at_trx設置爲2,那麼每次事務提交時會將日誌緩衝寫到操做系統緩存中,但不實際刷新到磁盤中,每秒再刷新日誌緩衝到磁盤中,
這樣作能夠減輕I/O負荷,若是不存在極端的狀況,理論上宕機最多隻會丟失最近1秒的事務。
若是innodb_flush_log_at_trx設置爲0,那麼每秒都會將日誌緩衝寫到日誌文件中,且將日誌文件刷新到磁盤,但在事務提交的時候並不會將日誌緩衝寫到日誌文件中,
通常不建議將其設置爲0,在設爲0時,若是mysqld進程崩潰,那麼停留在日誌緩衝區的數據將被丟失,所以你會丟失事務。
當爲2時,進程雖然會崩潰,但每次事務提交,都寫入了日誌,只是暫時沒有被刷新到磁盤,因此不會丟失事務,
由於操做系統負責把這些數據寫入文件,固然,若是宕機了,那麼你的數據仍是會被丟失的。
設置爲1將會更安全,但每次事務提交時都會伴隨磁盤I/O,受機械硬盤的尋道和旋轉延遲限制,可能會成爲系統瓶頸,在確承認以知足I/O性能的前提下,可將 其設置爲1。
建議在生產環境中將innodb_flush_log_at_trx設置爲2。

18.5.4 事務日誌
若是日誌文件裏記錄的相關數據並未寫入數據文件,那麼這個日誌文件是不能被覆蓋的。
日誌文件若是太小,那麼可能會過多地檢查點操做,增長I/O操做,而若是日誌文件過大,則會增長實例崩潰的恢復時間。
通常建議在生產系統中將其大小設置爲256~512MB,以平衡恢復時間和性能。
你也能夠定量分析實際須要的事務日誌大小,
方法是衡量一段時間(如0.5~2個小時)內寫入的日誌記錄(innodb_os_log_written)的大小,你所分配的多個日誌的總計大小應能確保保留此段時間的日誌。
事務日誌通常沒有必要和數據文件分離,除非你有許多(20+)盤。若是隻有幾個盤,卻專門使用獨立的盤來存放二進制日誌、事務日誌,則有些浪費。
在有足夠多的盤的狀況下,磁盤I/O分離纔有意義,否則成本就會過高了,且沒法充分利用有限的資源。
建議將日誌文件和數據文件放在同一個盤/卷的另外一個緣由是日誌文件和數據文件放在一塊兒,能夠作LVM快照。

18.5.5 二進制日誌
若是分離二進制日誌和數據文件,可能會帶來一點性能上的提高,但分離的主要目的不是性能,而是爲了日誌的安全。
若是沒有帶電池的RAID卡,那麼分離就是有必要的。
若是有帶電池的RAID卡,那麼通常狀況下就沒有必要進行分離,即便有許多順序日誌寫入,RAID卡也能夠合併這些操做,最終只會看到很少的一些順序I/O。
若是將二進制日誌存放在獨立的盤上,那麼即便咱們的數據文件損壞了,咱們也能夠利用備份和日誌作時間點恢復。

18.5.6 InnoDB如何打開和刷新數據、日誌文件
InnoDB有幾種方式和文件系統進行交互,默認是以fdatasync的方式讀寫文件的,生產環境中推薦設置爲O_DIRECT。
如下將簡單介紹這兩種方式:
fdatasync:默認InnoDB使用fsync()刷新數據和日誌。使用默認設置沒有什麼問題,但也許發揮不了你硬件的最高性能。
O_DIRECT:對於數據文件,MySQL Server也是調用fsync()刷新文件到磁盤的,可是不使用操做系統的緩存和預讀機制,以免雙重緩衝,
若是你有帶電池的RAID卡,則能夠配合這個選項一塊兒使用。
注意:RAID卡須要開啓寫緩存,默認策略是Write Back。

18.5.7 InnoDB共享表空間和獨立表空間
InnoDB表空間不只僅能夠存儲表和索引數據,還有UNDO(能夠理解爲數據前像)、insert buffer、double write buffer等其餘內部數據結構。
目前有兩種表空間的管理方式,共享表空間和獨立表空間。
默認的是共享表空間的管理方式,InnoDB表空間的管理比較簡單,並無Oracle那樣豐富的特性。
若是使用默認的共享表空間的話,數據和索引就是放在一塊兒的,全部數據都存儲在innodb_data_file_path參數設置的數據文件裏。
咱們能夠經過innodb_data_file_path設置多個InnoDB數據文件,通常將最後一個文件設置爲可自動擴展的,以減小數據文件的大小,你也能夠將數據文件分離到不一樣的磁盤中。
因爲數據文件不能收縮,因此使用共享表空間存在的一個嚴重的問題是空間的釋放。
若是你增長了數據文件,那麼你還須要重啓數據庫實例,這些都加大了管理開銷。
當自動擴展的數據文件被填滿之時,每次擴展默認爲8MB,咱們能夠調整爲更大的值,如32MB、64MB,這個選項能夠在運行時做爲全局系統變量而改變。
由於每次分配小空間,代價都會比較大,因此預分配一個較大的文件是有道理的。
另外一種方式是獨立表空間,咱們須要將innodb_file_per_table設置爲1。
這個選項能夠將每一個InnoDB表和它的索引存儲在它本身的文件中,因爲每一個表都有本身的表空間,因此又稱爲獨立表空間。
UNDO、各類數據字典等其餘數據仍然存儲在共享表空間內。
你能夠經過操做系統命令比較直觀地看到數據大小,也方便刪除表釋放空間,
因此許多有經驗的DBA都設置MySQL實例爲獨立表空間, 從而能夠更方便地釋放空間和減小文件系統的I/O爭用。
InnoDB也支持在裸設備上存儲,經過這種方式,你也許能夠獲得少量的性能提高,但因爲管理難度比較大,所以不多有人使用這種方式管理數據庫文件。

18.5.8 UNDO暴漲的可能性
有時咱們的共享表空間會暴漲,實際上是因爲UNDO空間發生了暴漲,UNDO空間暴漲的緣由主要有以下兩點。
存在長時間未提交的事務,由於未提交的事務須要使用發佈查詢時刻的UNDO的數據,因此共享表空間內的這部分UNDO數據不能被清除,將會積累得愈來愈多。
也許是負載過高,清理線程還來不及清除UNDO,這種狀況下,性能將會急劇降低。
18.5.9 關於double write buffer
InnoDB可以使用double write buffer來確保數據安全,以免塊損壞。
double write buffer是表空間的一個特殊的區域,可順序寫入。
當InnoDB從緩衝池刷新數據到磁盤時,它首先會寫入double write buffer,而後寫入實際的數據文件。
InnoDB檢查每一個頁塊的校驗和,以判斷是否壞塊,
若是寫入double write buffer的是壞塊,那麼顯然尚未寫入實際數據文件,那麼就用實際數據文件的塊來恢復double write buffer。
若是寫入了double write buffer,可是數據文件寫的是壞塊,那麼就用double write buffer的塊來重寫數據文件,這也是MySQL災難恢復的一個基本步驟。
若是操做系統自己支持寫入安全,不會致使壞塊,那麼咱們能夠禁用這個特性。

18.5.10 數據庫文件分類
能夠考慮把二進制日誌文件、InnoDB數據文件的物理文件分佈到不一樣的磁盤中,
這樣作主要考慮的是把順序I/O和隨機I/O進行分離。
你也能夠把順序I/O放到機械硬盤上,把隨機I/O放到SSD上,若是有帶電池的RAID卡且開啓了寫緩存,那麼順序I/O的操做通常是很快的。
具體如何放置文件,還須要綜合考慮性能、成本和維護性等多個因素。
筆者的作法是,若是沒有性能問題,就把全部文件都放在一個盤上,這樣維護起來將會更方便。
以下是按照順序I/O和隨機I/O對數據庫文件作了下分類。
(1)隨機I/O
表數據文件(*.ibd):啓用了獨立表空間(innodb_file_per_table=1)。
UNDO區域(ibdata):UNDO裏存儲了數據前像,MySQL爲了知足MVCC,須要讀取存儲在UNDO裏的前像數據,這將致使隨機讀,
若是你要運行一個須要很長時間的事務或一個時間很長的查詢,那麼可能會致使不少隨機讀,由於長事務或未提交的事務將有更多的可能性讀取前像數據。
(2)順序I/O
事務日誌(ib_logfile*)。
二進制日誌(binlog.xxxxxxx)。
double write buffer(ibdata)。
insert buffer(ibdata)。
慢查詢日誌、錯誤日誌、通用日誌等。

18.5.11 什麼時候運行OPTIMIZE TABLE
有些人會建議定時運行一些OPTIMIZE TABLE之類的命令,以優化性能,這點與Oracle相似,也總會有些人建議你定時運行重建索引的操做。
通常來講,除非在進行了大量會影響數據分佈的操做以後,好比刪除了大量的數據、導入數據等,通常狀況下是不須要重整表的。
定時地運行OPTIMIZE TABLE命令不現實,還可能會致使生產系統的不可用。
OPTIMIZED TABLE命令會優化InnoDB主鍵的物理組織,使之有序、緊湊,可是其餘索引仍然會和之前同樣未被優化。
哪個索引對性能更重要呢?也許歷來沒有基於主鍵的查詢條件。
其實,數據、索引的分佈也是須要一個過程的,隨着時間的演變,天然而然會達到一個平衡。
強制優化以後,過一段時間,它又會回到原來的很差不壞的狀態。
因此MySQL 5.1的官方文檔中才會建議:若是您已經刪除了表的一大部分,
或者若是您已經對含有可變長度行的表(含有VARCHAR、BLOB或TEXT列的表)進行了不少更改,則應使用OPTIMIZE TABLE。

18.5.12 MySQL磁盤空間
磁盤空間若是出現瓶頸,每每是由於數據庫規劃失誤,前期沒有進行足夠的調研,也有小部分緣由是由於業務發展得太快了,數據呈現爆炸式增加。
大部分業務,通常預留1到2年的數據增加空間就已經足夠了,若是你預計數據將來會有一個海量的規模,那麼提早進行分庫分表則是有必要考慮的。
你須要儘量地瞭解佔據數據庫整體空間比重較大的一些數據,清楚哪些表是能夠被清理或歸檔的,
許多狀況下,咱們並不須要這麼多的數據,或者許多數據是不須要保留好久的,是徹底能夠清除的,
你越瞭解數據,就越可以和研發團隊一塊兒制定合理的數據保留策略。
在系統上線以前,你就須要制訂好將數據進行批量清理和歸檔的方案,能夠使用按期任務刪除數據,你也能夠利用分區表刪除舊的歷史數據。
當數據庫實例的數據變得很大,單臺機器已經很難保存全部數據的時候,你能夠考慮將實例、數據庫分離到其餘的機器。
因爲處理器和高速緩存存儲器速度的提高超過磁盤存儲設備速度的提高,許多業務將受磁盤空間所累。
一些業務擁有海量數據,但大部分都是冷數據,你又不能進行簡單的歸檔處理,這個時候數據壓縮就派上用場了。
目前的數據庫主機,CPU資源每每過剩,數據壓縮能夠減小數據庫的大小,減小I/O和提升吞吐量,而壓縮僅僅只會消耗部分CPU成本。
MySQL 5.5開始提供了InnoDB表壓縮的功能,在MySQL 5.6中InnoDB表壓縮的功能獲得了進一步的完善,真正能夠用於生產環境了。
對於真正海量高併發的應用,內存爲王,你應該在內存中儘量地保證熱點數據和索引,更多的索引和數據能夠放在一個內存塊中,
那麼查詢的響應也將更快,表是壓縮的也意味着你須要更少的存儲空間和更小、更少的I/O操做。
對於MySQL 5.五、5.6,你須要配置爲獨立的表空間才能使用表的壓縮功能,對於MySQL 5.7,你也能夠不使用獨立表空間。
因爲固態硬盤通常比傳統機械硬盤要小,且成本更高,因此壓縮對固態硬盤尤爲有意義。
不一樣的內容壓縮率將會不同,若是你須要將表修改成壓縮表,那麼你須要在更改以前進行測試驗證,以確認壓縮率和轉換表的時間,
通常來講,設置 KEY_BLOCK_SIZE爲8KB能夠適用於大部分狀況,8KB意味着將每一個頁壓縮爲8KB,你也能夠將標準的16KB頁壓縮爲4KB或2KB,
但可能會致使過多的性能損耗而壓縮率並不能獲得提高。

小結:本章講述了調優將會涉及的MySQL參數及在使用MySQL的過程當中,內存、CPU、I/O的優化。筆者不推薦讀者對生產環境的參數作大的調整,也不推薦使用各類不經常使用的手段去優化硬件資源的利用率,壓榨硬件的性能。保持一個維護性更好的數據庫,使用通用的參數,可讓工做變得更簡單些,筆者認爲這纔是更重要的。可是,做爲DBA,必定要熟悉各類調優的手段,由於你可能會碰到極端的場景。

相關文章
相關標籤/搜索