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數據庫集羣。sql
當今MySQL正在被普遍的使用,支撐着無數互聯網應用的數據層,不少抱着拿來主義的人,每每用的不得要領,出現這樣或那樣的性能問題,這是由於忽視了MySQL在應用的過程當中須要的持續調優過程,包括MySQL參數調優、架構部署調優、硬件調優、SQL調優等等。數據庫
MySQL數據庫中最重要的一個概念就是數據庫引擎,不一樣的數據庫引擎的工做原理存在很大差別最終形成MySQL數據庫服務的性能差別。例如若是數據庫引擎須要支持事務,就必須知足事務的基本特性——AICD特性(AICD:原子性、隔離性、一致性和永久性),那麼天然就須要必定處理機制來實現這些特性。這樣作的現實效果就是致使寫入一樣數據量的狀況下,支持事務的數據庫引擎比不支持事務的數據庫引擎耗費更多的時間。性能優化
這裏列舉MySQL數據庫社區版中支持的一部分數據庫引擎:服務器
MEMORY:MEMORY存儲引擎將表的數據徹底存放在內存中。在MySQL數據庫的歷史版本中和該數據庫引擎相似的其它引擎是HEAP,後者曾是MySQL數據庫中訪問速度最快的數據庫引擎。但因爲這兩種數據庫引擎徹底工做在內存中,因此若是MySQL或者服務器從新啓動,數據庫引擎中保存的數據將會丟失。架構
BLACKHOLE:中文名「黑洞」,使用BLACKHOLE數據庫引擎的數據表不存儲任何數據,只根據數據庫操做過程記錄二進制日誌。它的主要做用是做爲MySQL主從複製的中繼器,而且能夠在其上面添加業務過濾機制。併發
MyISAM:MyISAM數據庫引擎是MySQL數據庫默認的數據庫引擎。MyISAM使用一種表格鎖定的機制,來優化多個併發的讀寫操做(實際上就是使用的一種避免數據髒讀的機制)。可是這種機制對存儲空間的使用有必定的浪費。MyISAM還有一些有用的擴展,例如用來修復數據庫文件的MYISAMCHK工具和用來恢復浪費空間的MYISAMPACK工具。oracle
InnoDB:InnoDB數據庫引擎是在各類版本的MySQL數據庫中使用最普遍的一種數據庫引擎,本文後續的介紹中若是沒有特別說明都默認是在說InnoDB數據庫引擎。InnoDB數據庫引擎使用日誌機制提供事務的支持。工具
對於數據庫的優化,須要從整個計算機體系的發展角度考慮。在今天CPU、GPU、內存頻率、總線帶寬都獲得快速發展,使得計算能力獲得質的提高,可是存儲機制(除了機械硬盤、固態硬盤、磁盤陣列),沒有更加革命性的變化。而傳統的關係型數據庫,在保證ACID的原則下,持久化是保證一致性的重要手段。所以,如何大幅度提高IO的性能,是數據庫優化的重點之一。性能
要了解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所獲得的數值了:
這樣作的目的是保證了後續同步日誌數據的操做都是順序寫,而不是隨機寫,以提升IO的效率。當日志數據寫到最後一個文件的末尾時,下一條日誌數據又會從新從第一個日誌文件的開始位置進行寫入。
InnoDB Log Buffer內存空間中的四個標識指針是InnoDB數據庫引擎日誌處理部分最重要元素,它們分別是:Log sequence、Log flushed、Pages flushed、Last checkpoint,這四個標識涉及到InnoDB在崩潰重啓時不一樣的數據恢復策略,以及I/O性能優化中的關鍵原理。
這四個標識,其實是四個數值。它們共享一個數值池(名叫LSN,log sequence number,日誌序列號,其總長度是64位無符號整數),表明當前InnoDB對事務操做的「檢查點或持久點」,而且它們之間的檢查點數值關係特徵以下:
Log sequence 大於等於 Log flushed 大於等於 Pages flushed 大於等於 Last checkpoint
分配LSN。每當InnoDB接收到一個完整數據庫insert/update請求事務後,就會建立一個新的LSN。新的LSN = 舊的LSN + 本次寫入的日誌大小。這條最新的日誌將會使用Log sequence進行標記,而且若是出現接收到多個事務請求的狀況下,InnoDB也會按照一個既定的順序對這些日誌進行排序,而後依次生成新的LSN。這一步驟是徹底在內存中進行的,因此不存在I/O性能問題。
處理事務中的操做,寫入InnoDB File Log Group日誌文件。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完成InnoDB Buffer Pool中的數據操做後,更改後數據所涉及到的Page將和此時存儲在磁盤上的數據不同,這樣的Page稱爲髒頁。
如何控制髒頁是保持數據一致性的關鍵。InnoDB數據庫引擎的作法是:首先向InnoDB File Log Group日誌文件中寫入這個事務的日誌信息(寫入策略由innodb_flush_log_at_trx_commit參數決定);而後InnoDB數據庫引擎在這一步驟的最後一個動做是更改Log flushed標識指針值爲當前最後完成刷新動做的事務日誌LSN值。實際上執行完這個步驟,一個事務處理操做纔算真正成功。
將髒頁寫入磁盤。上面兩個步驟,涉及數據變更的髒頁尚未更新到磁盤上,爲何事物的處理就能夠算做成功了呢?這是由於即便這個時候數據庫異常崩潰了,就憑存儲在磁盤上的完整日誌咱們也能夠重作數據。可是最好仍是儘快同步髒頁吧。在第三個步驟InnoDB數據庫引擎將會把最近Log flush時所涉及到的髒頁(最舊髒頁)更新到磁盤上。當完成髒頁向磁盤的同步操做後,InnoDB數據庫引擎將會更新Pages flushed標識點的LSN值,表示這個LSN值所表明的事務(以及以前LSN的事務)都已經完成了內存和磁盤上的數據同步動做。
當InnoDB數據庫引擎進行髒頁更新時,將會按照必定的週期策略批量提交髒頁到Linux操做系統的cache memory區塊中。不一樣版本InnoDB數據庫引擎支持的pages flush策略是不同的,但最基本的規則沒有變化,就是週期性刷新。每一次批量提交的髒頁數量由innodb_io_capacity_max參數決定。
從Mysql version 5.6開始InnoDB數據庫引擎向管理者提供了一個innodb_adaptive_flushing參數,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值所在標識區域的不一樣而不一樣:
當表明事務的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的更新能夠擁有獨立的週期,從而下降其帶來的性能消耗。
咱們大體知道了在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性能。
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參數將會使這些數據區域都享受到內存容量帶來的優點——至少不會頻繁地發生內容空間的強制清理。