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

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

接上文:《架構設計:系統存儲(4)——塊存儲方案(4)》
1、MySQL概述

從本文開始我們將討論建立在塊存儲方案之上的關係型數據庫的性能優化方案和集羣方案。關係型數據庫的選型將以創業公司、互聯網行業使用最廣泛的MySQL數據爲目標,但是MySQL的安裝過程和基本使用方法等知識並不在我們討論的範圍內。後續幾篇文章我們首先討論影響單個MySQL節點性能的主要因素,然後介紹MySQL讀寫分離、數據表橫縱拆分的原理和技術方案。

MySQL數據庫目前已被Oracle收購,並發展處多個版本。目前使用最廣泛且免費的MySQL版本是MySQL Community(社區版),另外還有三個付費的MySQL版本MySQL Standard(MySQL標準版)、MySQL Enterprise(MySQL企業版)、MySQL Cluster(MySQL集羣版),這三個版本是按照CPU內核進行費用計算,並且價格由低到高。最後Oracle還提供了兩個微型的MySQL版本:MySQL Classic(經典版),這個版本的MySQL只提供了MyISAM存儲引擎但是安裝快速,佔用空間較少;MySQL Embedded(嵌入式版本),這個版本的競爭軟件是SQLite。雖然社區版本是免費的並且這個版本提供的功能也沒有企業級版本豐富,同樣的硬件條件下單節點性能也沒有企業基本版優秀。但是我們可以藉助社區版本自身提供的功能和一些第三方軟件配合使用,搭建起相對廉價且性能不俗的MySQL數據庫集羣。
2、數據庫引擎的選擇

MySQL數據庫中最重要的一個概念就是數據庫引擎,不同的數據庫引擎的工作原理存在很大差異最終造成MySQL數據庫服務的性能差異。例如如果數據庫引擎需要支持事務,就必須滿足事務的基本特性——AICD特性(AICD:原子性、隔離性、一致性和永久性。屬於基礎知識所以不在這裏贅述),那麼自然就需要一定處理機制來實現這些特性。這樣做的現實效果就是導致寫入同樣數據量的情況下,支持事務的數據庫引擎比不支持事務的數據庫引擎耗費更多的時間。這裏我們首先爲讀者列舉MySQL數據庫社區版中支持的數據庫引擎(部分):

MEMORY:MEMORY存儲引擎將表的數據完全存放在內存中。在MySQL數據庫的歷史版本中和該數據庫引擎類似的其它引擎是HEAP,後者曾是MySQL數據庫中訪問速度最快的數據庫引擎。但由於這兩種數據庫引擎完全工作在內存中,所以如果MySQL或者服務器重新啓動,數據庫引擎中保存的數據將會丟失。

BLACKHOLE:中文名“黑洞”,使用BLACKHOLE數據庫引擎的數據表不存儲任何數據,只根據數據庫操作過程記錄二進制日誌。它的主要作用是作爲MySQL主從複製的中繼器,並且可以在其上面添加業務過濾機制。

MyISAM:MyISAM數據庫引擎是MySQL數據庫默認的數據庫引擎。MyISAM使用一種表格鎖定的機制,來優化多個併發的讀寫操作(實際上就是使用的一種避免數據髒讀的機制)。但是這種機制對存儲空間的使用有一定的浪費。MyISAM還有一些有用的擴展,例如用來修復數據庫文件的MYISAMCHK工具和用來恢復浪費空間的MYISAMPACK工具。本文所介紹的MySQL數據庫相關技術將不涉及到這種數據庫引擎。

InnoDB:InnoDB數據庫引擎是在各種版本的MySQL數據庫中使用最廣泛的一種數據庫引擎,本文後續的介紹中如果沒有特別說明都默認是在說InnoDB數據庫引擎。InnoDB數據庫引擎使用日誌機制提供事務的支持。

3、基本I/O性能

要了解MySQL數據庫中的性能問題,就首先要搞清楚在客戶端向MySQL數據庫提交一個事務操作時後者到底做了些什麼事情,以及主要是怎麼做的。本節所描述的工作過程主要圍繞InnoDB數據庫引擎進行:

這裏寫圖片描述

上圖中筆者只畫出了InnoDB數據庫引擎在insert/update一個事務的過程中所涉及的重要工作區域,InnoDB的實際工作細節要比上圖所示的步驟複雜得多。上文已經說到InnoDB數據庫引擎是一個支持事務的數據庫引擎,那麼如何解決異常崩潰情況下的數據一致性問題就是它的設計中最重要的任務之一。InnoDB數據庫引擎採用日誌來解決這個問題,請注意這裏說的InnoDB數據庫引擎日誌,並不是MySQL數據庫全局的二進制日誌。InnoDB數據庫引擎日誌還有另外一個名字:重做日誌(redo log),這是因爲這部分日誌主要的作用就是在數據庫異常崩潰並重啓後進行InnoDB引擎中數據的恢復。

爲了提高MySQL數據庫的性能,InnoDB數據庫引擎的數據操作過程基本上都在內存中完成,然後通過一定的策略(後文會詳細介紹)將InnoDB Log Buffer內存區域中的日誌數據同步到磁盤上的InnoDB File Log Group區域。InnoDB File Log Group區域主要用於存儲InnoDB數據庫引擎的日誌文件,它由多個大小相同的日誌文件構成並且這些文件都採用順序讀寫。innodb_log_file_size參數將決定每個文件的大小,而innodb_log_files_in_group參數將決定整個日誌組中有多少個日誌文件。

當MySQL數據庫完成初始化過程後這些日誌文件將會按照參數的設置值,在磁盤上預佔一個連續的磁盤空間。這樣做的現象就是雖然數據庫中還沒有任何數據,但是日誌文件的總大小就已經是 innodb_log_file_size * innodb_log_files_in_group所得到的數值了:

// InnoDB數據庫引擎 日誌文件示例
....
total 1.0G
-rw-rw---- 1 mysql mysql 500M May  4 06:09 ib_logfile0
-rw-rw---- 1 mysql mysql 500M May  4 06:09 ib_logfile1
....

這樣做的目的是保證了後續同步日誌數據的操作都是順序寫,而不是隨機寫。當日志數據寫到最後一個文件的末尾時,下一條日誌數據又會重新從第一個日誌文件的開始位置進行寫入。
3-1、I/O 性能問題的產生

InnoDB Log Buffer內存空間中的四個標識指針是InnoDB數據庫引擎日誌處理部分最重要元素,它們分別是:Log sequence、Log flushed、Pages flushed和Last checkpoint,這四個標識涉及到InnoDB在崩潰重啓時不同的數據恢復策略,以及I/O性能優化中的關鍵原理。這四個標識實際上是四個數值它們共享一個數值池(名叫LSN,日誌序列號,其總長度是64位無符號整數),代表當前InnoDB對事務操作的處理狀態。並且它們數值有以下特點:

Log sequence >= Log flushed >= Pages flushed >= Last checkpoint

每當InnoDB接收到一個完整數據庫insert/update請求事務後,就會創建一個新的LSN。新的LSN = 舊的LSN + 本次寫入的日誌大小。這條最新的日誌將會使用Log sequence進行標記,並且如果出現接收到多個事務請求的情況下,InnoDB也會按照一個既定的順序對這些日誌進行排序,然後依次生成新的LSN。這一步驟是完全在內存中進行的,所以不存在I/O性能問題。

接下來Mysql就會開始執行這個事務中的各種細節操作。InnoDB數據庫引擎專門有一個InnoDB Buffer Pool內存空間用來進行數據更改或數據新增。其大小由innodb_buffer_pool_size參數控制,其數據來源於innoDB data file並且以Page的形式存在於InnoDB Buffer Pool中。當日志中有insert操作時則生成新的Page;當日志中有update操作時,InnoDB會檢查該數據是否已經存在於Page Cache中,如果存在(命中)就直接更新這個Page Cache中的內容,如果不存在(未命中)就會繼續從InnoDB data file中讀取原始數據到InnoDB Buffer Pool中然後再更新。這裏要注意幾個問題:

還記得我們在討論磁盤設備時提到的“預讀”技術嗎?這個技術的思路是,如果某個區域的數據被讀取和使用那麼在不久的將來與其相鄰的區域也將會被讀取和使用。所以爲了提高讀取效率,磁盤控制芯片會將磁盤上目標塊和其相鄰的若干塊一起讀取出來。InnoDB數據庫引擎同樣使用了這個思路,即讀取某個Page時將會同時讀取臨近的Page,但是是否能起到提到I/O性能的目的還是要分不同的運行環境(後文進行說明)。

當InnoDB完成InnoDB Buffer Pool中的數據操作後,更改後數據所涉及到的Page將和此時存儲在磁盤上的數據不一樣,這樣的Page稱爲髒頁。如何控制髒頁將是保持數據一致性的關鍵,InnoDB數據庫引擎的做法是首先向InnoDB File Log Group日誌文件中寫入這個事務的日誌信息。這裏的寫入策略由三種,通過innodb_flush_log_at_trx_commit參數可以進行控制:

    innodb_flush_log_at_trx_commit = 0時,InnoDB將按照1秒鐘爲單位向磁盤寫入這個階段所有已完成的事務日誌信息。這裏的寫入成功並不是說寫入到Linux操作系統的Page Cache中就算成功,而是需要等待操作系統真正寫到了物理磁盤上的通知(具體請參見之前講解文件系統的文章)。這意味着即使InnoDB Buffer Pool中的數據操作是成功的,但是一旦數據庫系統異常崩潰,那麼業務系統將會丟失前1秒內寫入的數據:因爲沒有磁盤介質上的日誌就無法在異常重啓後恢復數據信息。

    innodb_flush_log_at_trx_commit = 1時,InnoDB按照完成一個日誌操作就向磁盤寫入事務日誌信息的方式來工作(執行一個事務就寫入一個事務日誌)。同樣,這裏的寫入成功同樣是要等待操作系統返回真正寫入了物理磁盤的通知。

    innodb_flush_log_at_trx_commit = 2時,InnoDB按照完成一個日誌操作就向磁盤寫入日誌信息的方式來工作。但是,這種工作模式下InnoDB不會等待操作系統返回物理磁盤上寫入成功的通知,就會繼續工作。實際上這個時候,數據一般還存在於Linux操作系統的cache memory區塊中,所以這種模式下最好使用帶有日誌功能的文件系統,並且確認開啓了文件系統的日誌功能。

InnoDB數據庫引擎在這一步驟的最後一個動作是更改Log flushed標識指針值爲當前最後完成刷新動作的事務日誌LSN值。實際上執行完這個步驟,一個事務處理操作纔算真正成功。

但是涉及數據變動的髒頁還沒有更新到磁盤上,爲什麼事物的處理就可以算作成功了呢?這是因爲即使這個時候數據庫異常崩潰了,就憑存儲在磁盤上的完整日誌我們也可以重做數據。好吧,最好還是要同步髒頁是吧。在第三個步驟InnoDB數據庫引擎將會把最近Log flush時所涉及到的髒頁(最舊髒頁)更新到磁盤上。當完成髒頁向磁盤的同步操作後,InnoDB數據庫引擎將會更新Pages flushed標識點的LSN值,表示這個LSN值所代表的事務(以及之前的事務)都已經完成了內存和磁盤上的數據同步動作。當InnoDB數據庫引擎進行髒頁更新時,將會按照一定的週期策略批量提交髒頁到Linux操作系統的cache memory區塊中。每一次批量提交的髒頁數量由innodb_io_capacity參數決定。

不同版本InnoDB數據庫引擎支持的pages flush策略是不一樣的,但最基本的規則沒有變化,就是週期性刷新。從Mysql version 5.6開始InnoDB數據庫引擎向管理者提供了一個innodb_adaptive_flushing參數,當這個參數設置爲“no”時InnoDB數據庫引擎將檢測髒頁在InnoDB Buffer Pool中的比例,以及即時I/O狀態等情況來決定pages flush的週期。如果髒頁在InnoDB Buffer Pool中的比例達到了由innodb_max_dirty_pages_pct(默認爲75)參數設置的百分比閥值,這時InnoDB數據庫引擎將按照innodb_io_capacity_max(默認值2000)參數設置的數量將這寫髒頁一起同步到磁盤。

當磁盤I/O性能不足且innodb_io_capacity設置過大時,會導致產生較長的I/O隊列造成I/O請求阻塞,一旦累積到innodb_max_dirty_pages_pct閥值,又會產生更長的I/O阻塞隊列;反之則會造成物理服務器的I/O性能沒有被去完全使用。所以innodb_io_capacity的設置非常重要,特別是當讀者在硬件層採用SSD固態硬盤和高速磁盤陣列時。

Checkpoint是InnoDB數據庫引擎中最後一個標識點。這個標識點代表着當數據庫異常崩潰重啓後,小於或者等於這個標識點LSN值的所有日誌信息、數據信息都無需進行重做檢查。而LSN值大於Checkpoint的所有事務都需要重做,只是重做策略將視LSN值所在標識區域的不同而不同:

![這裏寫圖片描述](https://img-blog.csdn.net/20161210094349704?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ2l0aHViXzMzNzM2OTcx/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)

    當代表事務的LSN數值在Log sequence——Log flushed範圍內時(不包括Log flushed),說明在數據庫崩潰時內存中的事務並沒有處理完,這部分事務操作將在恢復時被丟棄。

    當代表事務的LSN數值在Log flushed——Pages flushed範圍內時(不包括Pages flushed),說明數據庫崩潰時磁盤上已經擁有這些事務完整的日誌記錄。InnoDB數據庫引擎將讀取這些日誌數據,並繼續執行下去,直到代表這些事務的LSN值被標記爲Checkpoint(或者小於Checkpoint標識的LSN值)。這裏要注意,在數據庫崩潰時處於這個範圍內的某些事務可能已經完成了一部分的數據同步動作,但是肯定是不完整的。所以即使是這樣的事務也要重新進行磁盤同步,才能保證數據的一致性。

    實際上在MySQL version 5.5的早期版本,InnoDB數據庫引擎中只有三個標識:Log sequence、Log flushed和Checkpoint。也就是說當髒頁成功同步到磁盤後,就會直接更新Checkpoint標識的LSN值。後續版本的MySQL數據庫增加了Pages flushed標識點,這樣做的目的是保證Checkpoint和Pages flush的更新可以擁有獨立的週期,從而降低其帶來的性能消耗。

3-2、I/O 性能問題要點

Log flush和Pages flush

從上一小節的描述中,我們大致知道了在InnoDB數據庫引擎中一個事務的處理過程中有兩個步驟存在I/O操作:Log flush和Pages flush。

Log flush的過程是將完成的事務日誌寫入到日誌文件中,由於InnoDB數據庫引擎中日誌文件的組織方式,所以Log flush中對磁盤的操作是順序寫。並且技術團隊還可以通過innodb_flush_log_at_trx_commit參數來調整InnoDB Log Buffer到InnoDB File Log Group的同步策略,這有助於進一步提高Log flush性能。這也就是爲什麼實際環境中往往將MySQL數據庫(大部分關係型數據庫都適用)直接建立在塊存儲方案上,而不是建立在文件存儲方案或者對象存儲方案上的原因。

Pages flush的過程就沒有那麼幸運了,InnoDB數據庫引擎不可能事先知道數據庫會存放哪些數據,也不可能知道下次的update操作和select操作的目標數據存放在哪個區域。所以InnoDB數據庫引擎針對Page的讀取和更新都只能基於隨機讀寫。那麼Pages flush過程就需要在如何保持I/O性能這問題上想更多的解決辦法。

    例如在讀取Page時,採用“預讀”思路將目標Page所臨近的Page一起讀取出來;在寫入Page時將目標Page所臨近的Page一起寫入(“臨近寫”)。“預讀”策略可以通過innodb_read_ahead_threshold參數進行設置,並通過read thread完成,另外 innodb_flush_neighors參數可以控制是否開啓“臨近寫”策略。總的來說“預讀”/“臨近寫”在默認情況下都是開啓的,但“預讀”/“臨近寫”思路本身就需要一定的準確性,低命中率的“預讀”反而會降低InnoDB的I/O性能。還有一種“隨機預讀”,它在MySQL version 5.6版本中默認就是關閉的,並且在隨後的版本中將會慢慢廢除,所以這裏就不再介紹了。

    例如將向磁盤提交Page的動作設計爲週期性且批量進行,並且始終保持InnoDB Buffer Pool內存區域的髒頁(Dirty Page)在一定的比例,這些策略主要由innodb_io_capacity、innodb_max_dirty_pages_pct、innodb_io_capacity_max等參數控制。

    例如通過調整Innodb_Buffer_Pool_size參數獲得更大的InnoDB Buffer Pool內存區域,存儲更多的Page。實際上Innodb Buffer Pool區域不僅包括我們已經介紹的Page Cache數據部分,還包括其它的數據區塊。例如爲了快速定位B+樹索引的Hash Index結構。調整Innodb_Buffer_Pool_size參數將會使這些數據區域都享受到內存容量帶來的優勢——至少不會頻繁地發生內容空間的強制清理。

基礎硬件條件

按照本專題之前文章介紹的塊存儲方案來看(《架構設計:系統存儲(1)——塊存儲方案(1)》、《架構設計:系統存儲(2)——塊存儲方案(2)》),如果存儲MySQL數據的底層硬件介質就只是一塊機械磁盤,那麼無論怎樣優化MySQL的其它各參數,MySQL實際對磁盤的順序I/O速度理論上也只有100MB/S左右。這還是不計算硬件層校驗、不計算不同文件系統處理耗時等等時間,所以實際I/O速度只會更慢。另外如果採用單塊機械磁盤存儲MySQL的數據,那麼磁盤空間的擴容也是一個問題。目前市場上能買到的容量最大的單塊機械磁盤,它的存儲空間也只有10TB。當這部分容量使用完後想要進行擴容就基本上是就一個不可能完成的任務了。最後這種存儲方式還有安全性的問題,單塊機械磁盤在持續的高I/O環境下是很容易損壞的,只要是有一定資金支持的公司,機械磁盤本身就看做耗材。

所以即使是初創型公司的線上生產環境,本文也不推薦使用單塊機械磁盤存儲任何需要持久保存的業務數據。如果是因爲資金問題,則推薦使用一些雲服務商提供的現成PaaS環境,原因是這些PaaS
環境本身就支持數據恢復功能,且利用雲服務商已經建設好的價格不菲的硬件/軟件環境,MySQL數據庫的I/O性能和計算性能暫時還不會成爲業務系統的瓶頸。

3-3、突破I/O性能

=================================================
(接下文)

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