架構設計:系統存儲(6)——MySQL數據庫性能優化(2)

轉自http://blog.csdn.net/yinwenjie/article/details/52515316

==============================
(接上文《架構設計:系統存儲(5)——MySQL數據庫性能優化(1)》)
3-3、突破I/O性能

爲了解決上一節中提到的I/O性能問題,本文這裏基於之前介紹的塊存儲方案的知識,列出這個問題的幾種解決方案。除了根據I/O吞吐量要求對MySQL數據庫特別是InnoDB引擎的配置參數進行更改以外,本文提到的硬件層解決方法所需要花費的資金和能夠得到的I/O性能和擴展能力基本上成正比。
3-3-1、對MySQL中的I/O相關參數進行調整

上一節我們已經對InnoDB數據庫引擎(以下簡稱InnoDB引擎)進行事務操作時的I/O過程進行了簡單說明,主要介紹了Log flush和Pages flush兩個過程。如果我們需要挖掘正式生產環境上MySQL數據庫服務的性能潛力,那麼對MySQL數據庫服務中的默認參數進行更改就是必須要做的事情。在進行配置修改之前我們先來看看如何查看當前MySQL數據庫特別是InnoDB引擎的工作狀態:

# 通過執行以下命令,我們可以查看當前InnoDB引擎的工作狀態
show engine innodb status;

執行後可以得到類似如下的執行結果(已省去一部分與本文沒有涉及到的知識點所相關的狀態描述信息):

// master thread是InnoDB引擎中優先級最高的線程
// 這個線程存在的主要作用是控制InnoDB中各種I/O操作的策略,驅動其它read/write thread
// 並根據當前的InnoDB的InnoDB Log Buffer、InnoDB Buffer Pool區域狀態和配置參數決定處理邏輯
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 0 srv_active, 0 srv_shutdown, 79085 srv_idle
srv_master_thread log flush and writes: 79085
......

//這些是負責進行磁盤讀寫的I/O線程,日誌和數據Page的讀寫操作最終都是靠它們完成
// 它們的數量可以通過innodb_write_io_threads參數和innodb_read_io_threads參數進行設置
--------
FILE I/O
--------
I/O thread 0 state: waiting for completed aio requests (insert buffer thread)
I/O thread 1 state: waiting for completed aio requests (log thread)
I/O thread 2 state: waiting for completed aio requests (read thread)
......
I/O thread 8 state: waiting for completed aio requests (read thread)
I/O thread 9 state: waiting for completed aio requests (read thread)
I/O thread 10 state: waiting for completed aio requests (write thread)
......
I/O thread 13 state: waiting for completed aio requests (write thread)
Pending normal aio reads: 0 [0, 0, 0, 0, 0, 0, 0, 0] , aio writes: 0 [0, 0, 0, 0] ,
 ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
Pending flushes (fsync) log: 0; buffer pool: 0
524 OS file reads, 5 OS file writes, 5 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
......

// 這些信息反饋了InnoDB引擎中的日誌標識
---
LOG
---
// 當前生成的最大日誌LSN值
Log sequence number 9902833
// 當前已完成日誌信息向磁盤同步的最大日誌LSN值
Log flushed up to   9902833
// 當前已完成數據信息向磁盤同步的最大日誌LSN值
Pages flushed up to 9902833
// 當前已檢查點同步的最大日誌LSN值。小於和等於這是LSN值的日誌在異常重啓後都無需進行“重做”。
Last checkpoint at  9902833
// 當前InnoDB中待寫的日誌操作、待寫的檢查點操作
0 pending log writes, 0 pending chkp writes
8 log i/o's done, 0.00 log i/o's/second

// 這些信息反饋了InnoDB引擎中Buffer Pool內存空間的狀態
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 4395630592; in additional pool allocated 0
Dictionary memory allocated 99235
// 當前Buffer Pool的大小
Buffer pool size   262136
// 當前Buffer Pool的可用大小
Free buffers       261619
// Page數量
Database pages     506
Old database pages 0
Modified db pages  0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 506, created 0, written 1
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
// 狀態信息的這行反饋了Page的預讀信息,以及預讀信息的未命中剔除情況
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 506, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
......

對於上一小節提到的重要參數信息,可以採用如下的設定規則:

innodb_log_file_size:單個日誌文件的大小不宜過小,例如設置爲500MB。由於InnoBD引擎對日誌文件採用順序寫的操作方式,所以不必擔心日誌文件的操作消耗比數據文件操作更多的性能。

innodb_log_files_in_group:該參數控制了文件組中日誌文件的總數。設置爲2-5的範圍都不會有太大影響。更重要的是讀者應該清楚innodb_log_file_size * innodb_log_files_in_group就是InnoDB引擎在磁盤上可用日誌空間的總大小。

innodb_log_buffer_size:這個參數決定了InnoDB引擎可使用的日誌內存空間。只要沒有類似插入blob類型數據的操作(也不建議有這樣的操作),這個內存空間都不需要設置得太大。5MB-10MB是一個推薦的設置值,不過這個參數還是要和innodb_flush_log_at_trx_commit參數配合使用。

innodb_flush_log_at_trx_commit:該參數可以說是InnoDB引擎日誌操作策略部分最重要的設置參數之一。在上一篇文章(《架構設計:系統存儲(5)——MySQL數據庫性能優化(1)》)的3-1節中,我們已經介紹過該參數的三個值和它們代表的策略意義。如果您將innodb_flush_log_at_trx_commit設置爲0,代表着InnoDB引擎將會按照1秒鐘的週期進行日誌從內存到磁盤的同步。這時innodb_log_buffer_size的值就不能過小,因爲在一個同步週期內如果待刷新的日誌超過了innodb_log_buffer_size設置的大小,InnoDB就會強制執行同步操作。如果您的Linux操作系統使用的是帶有日誌功能的文件系統並且日誌功能是開啓的,那麼還是建議將該參數設置爲2。

innodb_buffer_pool_size:這個參數調整分配給InnoDB引擎使用的可用數據內存區域的大小。實際上這個數據區域不止包括了本文中一直強調的Page Cache區域,它還有很多數據區域。例如InnoDB中用來進行查詢排序的Sort Buffer區域。建議的設置大小是MySQL數據庫服務所在物理服務器上總內存的60%——80%(文件系統的Cache Memory/Buffer Memory等其它程序還要使用)。8GB的物理服務器可設置6GB的InnoDB Buffer Pool可用內存區域。注意,當MySQL數據庫啓動時並不是立刻就會佔據所有數據區域。

innodb_buffer_pool_instances:本小節和本文中多處位置都提及到innodb_buffer_pool_size參數以及它的含義。這個參數值在生產環境下一般設置得都比較大(例如4GB、8GB、12GB、24GB等等)。但是由於髒數據刷盤的週期性,在I/O性能強勁的物理機器上可能就會存在I/O間歇性低谷。爲了將I/O操作一直保持在一定的工作效能上,也爲了發揮CPU的計算性能,InnoDB引擎允許將innodb_buffer_pool劃分爲多個獨立的運行實例,當InnoDB需要讀取新的Page時,它們會按照一定的算法被分配到某個獨立運行的buffer pool instance中。這些buffer pool instance有各自獨立的LRU算法隊列、獨立計算髒頁比例,並且獨立進行髒頁刷新。innodb_buffer_pool_instances參數在具有較高I/O性能並且具有較大innodb_buffer_pool_size設定值的物理設備上能夠對I/O性能產生非常明顯的影響。如果您採用的是固態磁盤或者磁盤陣列作爲MySQL服務器的硬件層存儲介質,那麼建議1-2GB的innodb_buffer_pool就分配一個獨立的運行實例(這樣算下來12GB的buffer pool可以設置6-12個運行實例,注意進行生產環境下的實測調整哦^-^)。但如果您只是使用的機械磁盤又或者innodb_buffer_pool_size的值並不大,那麼將innodb_buffer_pool_instances參數設置爲1就可以了。

innodb_io_capacity:該參數控制着InnoDB Buffer Pool數據內存區域進行磁盤同步時每次可以同步的髒頁數量。在磁盤I/O性能不足時,如果innodb_io_capacity參數值過大就會造成I/O阻塞,並且造成InnoDB引擎性能較大的降低。但如果您使用的是固態硬盤或者RAID磁盤陣列,就可以將innodb_io_capacity參數默認的200設置大一些,例如設置成500——800)。

innodb_adaptive_flushing:該參數一定要打開,保證髒頁的同步週期由InnoDB引擎根據實時I/O性能情況自行控制同步頻率(實際上只有兩種頻率:1秒或者10秒)。

innodb_max_dirty_pages_pct:該參數默認爲75,一般情況下無需更改。另外innodb_io_capacity_max參數表示當髒頁數量在InnoDB Buffer Pool內存中的比例超過了innodb_max_dirty_pages_pct參數設置的上限後,就按照innodb_io_capacity_max設置的髒頁數量強制進行髒頁的刷新(建議採用默認值即可)。但是設想一下這個問題:什麼情況下最可能使髒頁在內存中的佔比超過上限呢?當然是InnoDB引擎的事務不斷快速執行,並且I/O性能又不足以快速完成同步。這時InnoDB引擎將停止事務的執行,並且進行強制刷新。所以,當問題真正發生時innodb_io_capacity_max參數設置得再大也不可能解決I/O擁堵的問題,反而可能使問題更嚴重。
3-3-2、對MySQL中的其它參數進行調整

innodb_page_size:該參數決定了InnoDB引擎中每一頁的大小。每一個page包含多條row數據,更大的page size意味着內存中存儲的每頁信息
有更多的數據條數。由於文件系統和底層硬件設置的結構,所以該值都爲4KB的整數倍(默認值爲16KB,可選值爲4KB、8KB、16KB)。注意如果您
需要更改這個參數值,那麼就必須在MySQL數據庫初始化啓動時,就加入到my.cnf配置文件中。否則一旦創建了用戶數據表,再對這個參數進行修
改,MySQL數據庫就會報錯。

innodb_read_io_threads:該參數設置InnoDB數據庫中的負責從磁盤上讀取數據的線程數量,另外這些線程還負責在預讀選項開啓時承擔起預讀
的工作任務。innodb_read_io_threads的建議值爲CPU的內核數量。

 innodb_write_io_threads:該參數設置InnoDB數據庫中負責將髒頁同步到磁盤上的線程數量。innodb_write_io_threads的建議值爲CPU的
 內核數量。

innodb_read_ahead_threshold:該參數表示InnoDB引擎中的順序預讀閥值。在buffer pool中的page也有一個組織結構:64個page組成一個
extent結構。當InnoDB發現在一個extent結構中**已經連續讀取**N個page,那麼InnoDB會接着將另外64 - N個後續的page讀入到buffer 
pool中。順序預讀在“連續讀”性能較高的硬件設備上,對性能的影響非常小。所以如果讀者使用了I/O性能比較強勁的固態磁盤環境或者磁盤陣列
環境,則建議直接關閉該功能(設置爲0即可)。

innodb_random_read_ahead:該參數表示是否開啓隨機預讀,默認是關閉的。

innodb_flush_neighbors:既然InnoDB引擎提供Page的預讀功能,當然就提供預寫功能。該參數表示當Buffer Pool中的髒頁被同步到磁盤時
,是否一起刷新和這個髒頁臨近的頁信息。該參數在I/O性能比較強勁的固態磁盤環境或者磁盤陣列環境下,對性能提升並不明顯。所以建議在這樣
的情況下直接關閉這個功能(設置爲0即可)。

sort_buffer_size:後文介紹數據庫查詢優化時會討論到這個參數。該參數對數據庫引擎的查詢性能,特別是有對結果進行排序要求的查詢性能
影響非常大。

join_buffer_size:後文介紹數據庫查詢優化時會討論到這個參數。該參數對數據庫引擎的查詢性能,特別是有各種join連接要求的查詢性能影響
非常大。

binlog_cache_size:在MySQL數據庫中處理InnoDB層存在“重做日誌”以外,在數據庫管理層還有一個獨立工作的二進制日誌模塊。這個日誌模塊
的工作方式和“重做日誌”的工作方式相似,它們採用的辦法都是在內存中進行日誌數據變更,然後再按照一定的策略週期性/直接同步到磁盤上。
binlog_cache_size參數設置的就是可供二進制日誌在內存中進行暫存的空間大小。需要注意的是:binlog_cache_size和innodb_buffer_
pool_size不同的是,前者的大小以MySQL數據庫的客戶端連接爲單位。也就是說MySQL數據庫會爲兩個獨立的數據庫客戶端連接分別分配獨立運行
的binlog cache空間。正式環境的數據庫中爲每一個數據庫連接設置的binlog cache空間不需要太大,當然這還要考慮實際的客戶端請求頻度和
數據類型,還要考慮下面將介紹的sync_binlog參數設定。該參數建議的幾個設置值爲:32768(32KB爲默認值,沒有特別的要求可以保留該設
置)、65536(64KB)、131072(128KB)、262144(KB)、524288(512KB)、1048576(1MB)以內。

sync_binlog:在MySQL數據庫中除了InnoDB的“重做日誌”需要同步以外,二進制日誌也需要進行同步。這個參數是指MySQL數據庫在內存中進行X
次二進制日誌操作後,就將內存中的二進制日誌同步到磁盤中。

3-3-3、調整後的參數情況

以下是一組可以在配置有固態硬盤和磁盤陣列的正式MySQL數據庫環境下使用的配置項參考,主要是爲讀者總結InnoDB引擎中和I/O性能相關的重要參數(只和I/O性能有關,因爲後續的文章中還會介紹其他參數)。讀者在進行參數配置是還是需要按照自己團隊的生產環境情況,對配置項進行調整(這些參數信息都在MySQL數據庫的my.cnf主配置文件中進行設置):

# 設置單個日誌文件的大小爲500MB
innodb_log_file_size = 524288000
# 設置日誌文件組中有兩個日誌文件
innodb_log_files_in_group = 2
# 設置日誌內存區域爲10MB
innodb_log_buffer_size = 10485760
# 設置日誌數據同步策略爲“2”
innodb_flush_log_at_trx_commit = 2
# 設置buffer pool的大小爲8GB
innodb_buffer_pool_size = 8G
# 設置正常情況下每一次髒頁到磁盤的同步數量爲800個
# (當然讀者要確定磁盤I/O性能夠用,否則改大這個值有害無益)
innodb_io_capacity = 800
# 打開InnoDB提供的自監控頻率
innodb_adaptive_flushing = on
# 已經介紹過三次了,不再贅述
innodb_max_dirty_pages_pct = 75
innodb_io_capacity_max = 2000

# 設置InnoDB的buffer pool區域一共有8個獨立運行的實例
innodb_buffer_pool_instances = 8
# 設置每一個數據頁“page”的大小爲16KB。爲4KB的整數倍
innodb_page_size = 16384
# 設置每次二進制日誌操作都提交到文件系統的Cache中
sync_binlog = 0
# 也可設置二進制日誌在內存區域每操作1000次後,就進行磁盤同步
#sync_binlog = 1000
# 關閉順序預讀
innodb_read_ahead_threshold = 0
# 關閉隨機讀
innodb_random_read_ahead = off
# 關閉臨近寫
innodb_flush_neighbors = 0

配置完成後可以通過以下命令查看當前MySQL數據庫和InnoDB引擎中相關的配置參數(爲節約篇幅,已省去一部分查詢結果):

# 查詢配置參數
show variables like 'innodb%';
# 或者查詢全局工作狀態也行
# show global status like 'innodb%';

......
innodb_adaptive_flushing                ON
innodb_adaptive_flushing_lwm            10
innodb_adaptive_hash_index              ON
innodb_adaptive_max_sleep_delay         150000
innodb_additional_mem_pool_size         8388608
innodb_api_disable_rowlock              OFF
innodb_api_enable_binlog                OFF
innodb_api_enable_mdl                   OFF
innodb_buffer_pool_instances            8
innodb_buffer_pool_size                 8589934592
innodb_disable_sort_file_cache          OFF
innodb_doublewrite                      ON
innodb_fast_shutdown                    1
innodb_file_per_table                   ON
innodb_flush_log_at_timeout             1
innodb_flush_log_at_trx_commit          2   
innodb_flush_neighbors                  0
innodb_flushing_avg_loops               30
innodb_io_capacity                      800
innodb_io_capacity_max                  2000
innodb_lock_wait_timeout                50
innodb_locks_unsafe_for_binlog          OFF
innodb_log_buffer_size                  10485760
innodb_log_compressed_pages             ON
innodb_log_file_size                    524288000
innodb_log_files_in_group               2
innodb_log_group_home_dir               ./
innodb_lru_scan_depth                   1024
innodb_max_dirty_pages_pct              75
innodb_max_dirty_pages_pct_lwm          0
innodb_mirrored_log_groups              1
innodb_online_alter_log_max_size        134217728
innodb_open_files                       2000
innodb_page_size                        16384
innodb_random_read_ahead                OFF
innodb_read_ahead_threshold             0
innodb_read_io_threads                  8
innodb_write_io_threads                 4
innodb_sort_buffer_size                 1048576
innodb_table_locks                      ON
innodb_use_native_aio                   ON
innodb_use_sys_malloc                   ON
innodb_version                          5.6.22
......

3-3-4、提供更優異的硬件方案

使用磁盤陣列替代單塊磁盤

這是最基本的硬件層改造方式,目前大多數廠商提供的PC Server基本上都集成了RAID控制器。所以這樣做一般不需要額外增加購買硬件設備的費用。在MySQL官網上並沒有明確推薦使用哪一種磁盤陣列模式,但是從搭建磁盤陣列支持MySQL的實際引用情況來看,更多是使用RAID 10陣列模式(另外磁盤陣列的整體性能和陣列控制芯片有很大關係)。RAID 10陣列模式可以在提升了整個系統I/O性能的基礎上兼顧了存儲的安全性。爲了使用RAID 10磁盤陣列模式,讀者至少需要爲準備4塊磁盤。其中2/4的磁盤容量用來存儲數據冗餘,另外2/4的磁盤容量用來分散存儲數據。對於RAID 10磁盤陣列模式的詳細工作方式介紹,讀者可以參看另一篇文章(《架構設計:系統存儲(2)——塊存儲方案(2)》)

使用固態硬盤進一步替代機械磁盤

以上解決方案中,每一個機械磁盤的I/O性能將會成爲整個RAID 10磁盤陣列的性能瓶頸(不考慮陣列控制芯片的處理性能)。所以如果技術團隊還有多餘的資金支持那麼下一步要做的就是將構成RAID 10磁盤陣列多個機械磁盤全部替換成固態磁盤。如下圖所示:

這裏寫圖片描述

進一步使用外置磁盤陣列櫃 + 光纖結構

USB3.0接口的理論帶寬只有600MB/S,而且PC Server內置的磁盤陣列控制器由於服務器內部空間的限制,也存在磁盤數量擴展困難的問題。如果讀者確認生產環境的某個物理服務器將以I/O讀寫操作爲主,且I/O性能將成爲其上工作的應用軟件的瓶頸。那麼這時最好的硬件方案就是直接採用外置企業級磁盤陣列櫃 + 光纖接口的方式搭建硬件層支持。

目前主流的光纖線路帶寬爲16Gb/s,這遠遠高於USB3.0 6Gb/s的理論帶寬、高於SAS 12Gb/s的理論帶寬。另外單個企業級磁盤陣列櫃可容納的磁盤數量就已經很高了(例如IBM Storwize V5000 單櫃提供24個磁盤位,單櫃支持最大72TB存儲容量),並且這些企業級盤櫃一般支持擴展成多櫃。這兩種特性有效解決了硬件層面磁盤I/O速度和容量的問題,但代價就是這些IT基礎折本的價格一般比較昂貴,技術團隊所在企業需要有比較寬裕的項目/產品建設預算。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章