mysql insert 不需要日志_MySQL数据库性能优化(1)「转」
1、MySQL概述
從本文開始我們將討論建立在塊存儲方案之上的關(guān)系型數(shù)據(jù)庫的性能優(yōu)化方案和集群方案。關(guān)系型數(shù)據(jù)庫的選型將以創(chuàng)業(yè)公司、互聯(lián)網(wǎng)行業(yè)使用最廣泛的MySQL數(shù)據(jù)為目標(biāo),但是MySQL的安裝過程和基本使用方法等知識并不在我們討論的范圍內(nèi)。后續(xù)幾篇文章我們首先討論影響單個(gè)MySQL節(jié)點(diǎn)性能的主要因素,然后介紹MySQL讀寫分離、數(shù)據(jù)表橫縱拆分的原理和技術(shù)方案。
MySQL數(shù)據(jù)庫目前已被Oracle收購,并發(fā)展處多個(gè)版本。目前使用最廣泛且免費(fèi)的MySQL版本是MySQL Community(社區(qū)版),另外還有三個(gè)付費(fèi)的MySQL版本MySQL Standard(MySQL標(biāo)準(zhǔn)版)、MySQL Enterprise(MySQL企業(yè)版)、MySQL Cluster(MySQL集群版),這三個(gè)版本是按照CPU內(nèi)核進(jìn)行費(fèi)用計(jì)算,并且價(jià)格由低到高。最后Oracle還提供了兩個(gè)微型的MySQL版本:MySQL Classic(經(jīng)典版),這個(gè)版本的MySQL只提供了MyISAM存儲引擎但是安裝快速,占用空間較少;MySQL Embedded(嵌入式版本),這個(gè)版本的競爭軟件是SQLite。雖然社區(qū)版本是免費(fèi)的并且這個(gè)版本提供的功能也沒有企業(yè)級版本豐富,同樣的硬件條件下單節(jié)點(diǎn)性能也沒有企業(yè)基本版優(yōu)秀。但是我們可以借助社區(qū)版本自身提供的功能和一些第三方軟件配合使用,搭建起相對廉價(jià)且性能不俗的MySQL數(shù)據(jù)庫集群。
2、數(shù)據(jù)庫引擎的選擇
MySQL數(shù)據(jù)庫中最重要的一個(gè)概念就是數(shù)據(jù)庫引擎,不同的數(shù)據(jù)庫引擎的工作原理存在很大差異最終造成MySQL數(shù)據(jù)庫服務(wù)的性能差異。例如如果數(shù)據(jù)庫引擎需要支持事務(wù),就必須滿足事務(wù)的基本特性——AICD特性(AICD:原子性、隔離性、一致性和永久性。屬于基礎(chǔ)知識所以不在這里贅述),那么自然就需要一定處理機(jī)制來實(shí)現(xiàn)這些特性。這樣做的現(xiàn)實(shí)效果就是導(dǎo)致寫入同樣數(shù)據(jù)量的情況下,支持事務(wù)的數(shù)據(jù)庫引擎比不支持事務(wù)的數(shù)據(jù)庫引擎耗費(fèi)更多的時(shí)間。這里我們首先為讀者列舉MySQL數(shù)據(jù)庫社區(qū)版中支持的數(shù)據(jù)庫引擎(部分):
- MEMORY:MEMORY存儲引擎將表的數(shù)據(jù)完全存放在內(nèi)存中。在MySQL數(shù)據(jù)庫的歷史版本中和該數(shù)據(jù)庫引擎類似的其它引擎是HEAP,后者曾是MySQL數(shù)據(jù)庫中訪問速度最快的數(shù)據(jù)庫引擎。但由于這兩種數(shù)據(jù)庫引擎完全工作在內(nèi)存中,所以如果MySQL或者服務(wù)器重新啟動,數(shù)據(jù)庫引擎中保存的數(shù)據(jù)將會丟失。
- BLACKHOLE:中文名“黑洞”,使用BLACKHOLE數(shù)據(jù)庫引擎的數(shù)據(jù)表不存儲任何數(shù)據(jù),只根據(jù)數(shù)據(jù)庫操作過程記錄二進(jìn)制日志。它的主要作用是作為MySQL主從復(fù)制的中繼器,并且可以在其上面添加業(yè)務(wù)過濾機(jī)制。
- MyISAM:MyISAM數(shù)據(jù)庫引擎是MySQL數(shù)據(jù)庫默認(rèn)的數(shù)據(jù)庫引擎。MyISAM使用一種表格鎖定的機(jī)制,來優(yōu)化多個(gè)并發(fā)的讀寫操作(實(shí)際上就是使用的一種避免數(shù)據(jù)臟讀的機(jī)制)。但是這種機(jī)制對存儲空間的使用有一定的浪費(fèi)。MyISAM還有一些有用的擴(kuò)展,例如用來修復(fù)數(shù)據(jù)庫文件的MYISAMCHK工具和用來恢復(fù)浪費(fèi)空間的MYISAMPACK工具。本文所介紹的MySQL數(shù)據(jù)庫相關(guān)技術(shù)將不涉及到這種數(shù)據(jù)庫引擎。
- InnoDB:InnoDB數(shù)據(jù)庫引擎是在各種版本的MySQL數(shù)據(jù)庫中使用最廣泛的一種數(shù)據(jù)庫引擎,本文后續(xù)的介紹中如果沒有特別說明都默認(rèn)是在說InnoDB數(shù)據(jù)庫引擎。InnoDB數(shù)據(jù)庫引擎使用日志機(jī)制提供事務(wù)的支持。
3、基本I/O性能
要了解MySQL數(shù)據(jù)庫中的性能問題,就首先要搞清楚在客戶端向MySQL數(shù)據(jù)庫提交一個(gè)事務(wù)操作時(shí)后者到底做了些什么事情,以及主要是怎么做的。本節(jié)所描述的工作過程主要圍繞InnoDB數(shù)據(jù)庫引擎進(jìn)行:
上圖中筆者只畫出了InnoDB數(shù)據(jù)庫引擎在insert/update一個(gè)事務(wù)的過程中所涉及的重要工作區(qū)域,InnoDB的實(shí)際工作細(xì)節(jié)要比上圖所示的步驟復(fù)雜得多。上文已經(jīng)說到InnoDB數(shù)據(jù)庫引擎是一個(gè)支持事務(wù)的數(shù)據(jù)庫引擎,那么如何解決異常崩潰情況下的數(shù)據(jù)一致性問題就是它的設(shè)計(jì)中最重要的任務(wù)之一。InnoDB數(shù)據(jù)庫引擎采用日志來解決這個(gè)問題,請注意這里說的InnoDB數(shù)據(jù)庫引擎日志,并不是MySQL數(shù)據(jù)庫全局的二進(jìn)制日志。InnoDB數(shù)據(jù)庫引擎日志還有另外一個(gè)名字:重做日志(redo log),這是因?yàn)檫@部分日志主要的作用就是在數(shù)據(jù)庫異常崩潰并重啟后進(jìn)行InnoDB引擎中數(shù)據(jù)的恢復(fù)。
為了提高M(jìn)ySQL數(shù)據(jù)庫的性能,InnoDB數(shù)據(jù)庫引擎的數(shù)據(jù)操作過程基本上都在內(nèi)存中完成,然后通過一定的策略(后文會詳細(xì)介紹)將InnoDB Log Buffer內(nèi)存區(qū)域中的日志數(shù)據(jù)同步到磁盤上的InnoDB File Log Group區(qū)域。InnoDB File Log Group區(qū)域主要用于存儲InnoDB數(shù)據(jù)庫引擎的日志文件,它由多個(gè)大小相同的日志文件構(gòu)成并且這些文件都采用順序讀寫。innodb_log_file_size參數(shù)將決定每個(gè)文件的大小,而innodb_log_files_in_group參數(shù)將決定整個(gè)日志組中有多少個(gè)日志文件。
當(dāng)MySQL數(shù)據(jù)庫完成初始化過程后這些日志文件將會按照參數(shù)的設(shè)置值,在磁盤上預(yù)占一個(gè)連續(xù)的磁盤空間。這樣做的現(xiàn)象就是雖然數(shù)據(jù)庫中還沒有任何數(shù)據(jù),但是日志文件的總大小就已經(jīng)是 innodb_log_file_size * innodb_log_files_in_group所得到的數(shù)值了:
# InnoDB數(shù)據(jù)庫引擎 日志文件示例
....
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
....
這樣做的目的是保證了后續(xù)同步日志數(shù)據(jù)的操作都是順序?qū)?#xff0c;而不是隨機(jī)寫。當(dāng)日志數(shù)據(jù)寫到最后一個(gè)文件的末尾時(shí),下一條日志數(shù)據(jù)又會重新從第一個(gè)日志文件的開始位置進(jìn)行寫入。
3-1、I/O 性能問題的產(chǎn)生
InnoDB Log Buffer內(nèi)存空間中的四個(gè)標(biāo)識指針是InnoDB數(shù)據(jù)庫引擎日志處理部分最重要元素,它們分別是:Log sequence、Log flushed、Pages flushed和Last checkpoint,這四個(gè)標(biāo)識涉及到InnoDB在崩潰重啟時(shí)不同的數(shù)據(jù)恢復(fù)策略,以及I/O性能優(yōu)化中的關(guān)鍵原理。這四個(gè)標(biāo)識實(shí)際上是四個(gè)數(shù)值它們共享一個(gè)數(shù)值池(名叫LSN,日志序列號,其總長度是64位無符號整數(shù)),代表當(dāng)前InnoDB對事務(wù)操作的處理狀態(tài)。并且它們數(shù)值有以下特點(diǎn):
Log sequence【事務(wù)產(chǎn)生】 >= Log flushed【日志持久化】 >= Pages flushed【數(shù)據(jù)持久化】 >= Last checkpoint【數(shù)據(jù)恢復(fù)檢查點(diǎn)】
還記得我們在討論磁盤設(shè)備時(shí)提到的“預(yù)讀”技術(shù)嗎?這個(gè)技術(shù)的思路是,如果某個(gè)區(qū)域的數(shù)據(jù)被讀取和使用那么在不久的將來與其相鄰的區(qū)域也將會被讀取和使用。所以為了提高讀取效率,磁盤控制芯片會將磁盤上目標(biāo)塊和其相鄰的若干塊一起讀取出來。InnoDB數(shù)據(jù)庫引擎同樣使用了這個(gè)思路,即讀取某個(gè)Page時(shí)將會同時(shí)讀取臨近的Page,但是是否能起到提到I/O性能的目的還是要分不同的運(yùn)行環(huán)境(后文進(jìn)行說明)。
當(dāng)InnoDB完成InnoDB Buffer Pool中的數(shù)據(jù)操作后,更改后數(shù)據(jù)所涉及到的Page將和此時(shí)存儲在磁盤上的數(shù)據(jù)不一樣,這樣的Page稱為臟頁。如何控制臟頁將是保持?jǐn)?shù)據(jù)一致性的關(guān)鍵,InnoDB數(shù)據(jù)庫引擎的做法是首先向InnoDB File Log Group日志文件中寫入這個(gè)事務(wù)的日志信息。這里的寫入策略由三種,通過innodb_flush_log_at_trx_commit參數(shù)可以進(jìn)行控制:
- innodb_flush_log_at_trx_commit = 0時(shí),InnoDB將按照1秒鐘為單位向磁盤寫入這個(gè)階段所有已完成的事務(wù)日志信息。這里的寫入成功并不是說寫入到Linux操作系統(tǒng)的Page Cache中就算成功,而是需要等待操作系統(tǒng)真正寫到了物理磁盤上的通知(具體請參見之前講解文件系統(tǒng)的文章)。這意味著即使InnoDB Buffer Pool中的數(shù)據(jù)操作是成功的,但是一旦數(shù)據(jù)庫系統(tǒng)異常崩潰,那么業(yè)務(wù)系統(tǒng)將會丟失前1秒內(nèi)寫入的數(shù)據(jù):因?yàn)闆]有磁盤介質(zhì)上的日志就無法在異常重啟后恢復(fù)數(shù)據(jù)信息。【定時(shí)持久化日志】
- innodb_flush_log_at_trx_commit = 1時(shí),InnoDB按照完成一個(gè)日志操作就向磁盤寫入事務(wù)日志信息的方式來工作(執(zhí)行一個(gè)事務(wù)就寫入一個(gè)事務(wù)日志)。同樣,這里的寫入成功同樣是要等待操作系統(tǒng)返回真正寫入了物理磁盤的通知。【1事務(wù)1日志,等操作系統(tǒng)寫】
- innodb_flush_log_at_trx_commit = 2時(shí),InnoDB按照完成一個(gè)日志操作就向磁盤寫入日志信息的方式來工作。但是,這種工作模式下InnoDB不會等待操作系統(tǒng)返回物理磁盤上寫入成功的通知,就會繼續(xù)工作。實(shí)際上這個(gè)時(shí)候,數(shù)據(jù)一般還存在于Linux操作系統(tǒng)的cache memory區(qū)塊中,所以這種模式下最好使用帶有日志功能的文件系統(tǒng),并且確認(rèn)開啟了文件系統(tǒng)的日志功能。【1事務(wù)1日志,不等操作系統(tǒng)寫】
InnoDB數(shù)據(jù)庫引擎在這一步驟的最后一個(gè)動作是更改Log flushed標(biāo)識指針值為當(dāng)前最后完成刷新動作的事務(wù)日志LSN值。實(shí)際上執(zhí)行完這個(gè)步驟,一個(gè)事務(wù)處理操作才算真正成功。
不同版本InnoDB數(shù)據(jù)庫引擎支持的pages flush策略是不一樣的,但最基本的規(guī)則沒有變化,就是周期性刷新。從Mysql version 5.6開始InnoDB數(shù)據(jù)庫引擎向管理者提供了一個(gè)innodb_adaptive_flushing參數(shù),當(dāng)這個(gè)參數(shù)設(shè)置為“no”時(shí)InnoDB數(shù)據(jù)庫引擎將檢測臟頁在InnoDB Buffer Pool中的比例,以及即時(shí)I/O狀態(tài)等情況來決定pages flush的周期。如果臟頁在InnoDB Buffer Pool中的比例達(dá)到了由innodb_max_dirty_pages_pct(默認(rèn)為75)參數(shù)設(shè)置的百分比閥值,這時(shí)InnoDB數(shù)據(jù)庫引擎將按照innodb_io_capacity_max(默認(rèn)值2000)參數(shù)設(shè)置的數(shù)量將這寫臟頁一起同步到磁盤。
當(dāng)磁盤I/O性能不足且innodb_io_capacity設(shè)置過大時(shí),會導(dǎo)致產(chǎn)生較長的I/O隊(duì)列造成I/O請求阻塞,一旦累積到innodb_max_dirty_pages_pct閥值,又會產(chǎn)生更長的I/O阻塞隊(duì)列;反之則會造成物理服務(wù)器的I/O性能沒有被去完全使用。所以innodb_io_capacity的設(shè)置非常重要,特別是當(dāng)讀者在硬件層采用SSD固態(tài)硬盤和高速磁盤陣列時(shí)。
- 當(dāng)代表事務(wù)的LSN數(shù)值在Log sequence——Log flushed范圍內(nèi)時(shí)(不包括Log flushed),說明在數(shù)據(jù)庫崩潰時(shí)內(nèi)存中的事務(wù)并沒有處理完,這部分事務(wù)操作將在恢復(fù)時(shí)被丟棄。
- 當(dāng)代表事務(wù)的LSN數(shù)值在Log flushed——Pages flushed范圍內(nèi)時(shí)(不包括Pages flushed),說明數(shù)據(jù)庫崩潰時(shí)磁盤上已經(jīng)擁有這些事務(wù)完整的日志記錄。InnoDB數(shù)據(jù)庫引擎將讀取這些日志數(shù)據(jù),并繼續(xù)執(zhí)行下去,直到代表這些事務(wù)的LSN值被標(biāo)記為Checkpoint(或者小于Checkpoint標(biāo)識的LSN值)。這里要注意,在數(shù)據(jù)庫崩潰時(shí)處于這個(gè)范圍內(nèi)的某些事務(wù)可能已經(jīng)完成了一部分的數(shù)據(jù)同步動作,但是肯定是不完整的。所以即使是這樣的事務(wù)也要重新進(jìn)行磁盤同步,才能保證數(shù)據(jù)的一致性。
- 實(shí)際上在MySQL version 5.5的早期版本,InnoDB數(shù)據(jù)庫引擎中只有三個(gè)標(biāo)識:Log sequence、Log flushed和Checkpoint。也就是說當(dāng)臟頁成功同步到磁盤后,就會直接更新Checkpoint標(biāo)識的LSN值。后續(xù)版本的MySQL數(shù)據(jù)庫增加了Pages flushed標(biāo)識點(diǎn),這樣做的目的是保證Checkpoint和Pages flush的更新可以擁有獨(dú)立的周期,從而降低其帶來的性能消耗。
3-2、I/O 性能問題要點(diǎn)
從上一小節(jié)的描述中,我們大致知道了在InnoDB數(shù)據(jù)庫引擎中一個(gè)事務(wù)的處理過程中有兩個(gè)步驟存在I/O操作:Log flush和Pages flush。
Log flush的過程是將完成的事務(wù)日志寫入到日志文件中,由于InnoDB數(shù)據(jù)庫引擎中日志文件的組織方式,所以Log flush中對磁盤的操作是順序?qū)憽2⑶壹夹g(shù)團(tuán)隊(duì)還可以通過innodb_flush_log_at_trx_commit參數(shù)來調(diào)整InnoDB Log Buffer到InnoDB File Log Group的同步策略,這有助于進(jìn)一步提高Log flush性能。這也就是為什么實(shí)際環(huán)境中往往將MySQL數(shù)據(jù)庫(大部分關(guān)系型數(shù)據(jù)庫都適用)直接建立在塊存儲方案上,而不是建立在文件存儲方案或者對象存儲方案上的原因。
Pages flush的過程就沒有那么幸運(yùn)了,InnoDB數(shù)據(jù)庫引擎不可能事先知道數(shù)據(jù)庫會存放哪些數(shù)據(jù),也不可能知道下次的update操作和select操作的目標(biāo)數(shù)據(jù)存放在哪個(gè)區(qū)域。所以InnoDB數(shù)據(jù)庫引擎針對Page的讀取和更新都只能基于隨機(jī)讀寫。那么Pages flush過程就需要在如何保持I/O性能這問題上想更多的解決辦法。
- 例如在讀取Page時(shí),采用“預(yù)讀”思路將目標(biāo)Page所臨近的Page一起讀取出來;在寫入Page時(shí)將目標(biāo)Page所臨近的Page一起寫入(“臨近寫”)。“預(yù)讀”策略可以通過innodb_read_ahead_threshold參數(shù)進(jìn)行設(shè)置,并通過read thread完成,另外 innodb_flush_neighors參數(shù)可以控制是否開啟“臨近寫”策略。總的來說“預(yù)讀”/“臨近寫”在默認(rèn)情況下都是開啟的,但“預(yù)讀”/“臨近寫”思路本身就需要一定的準(zhǔn)確性,低命中率的“預(yù)讀”反而會降低InnoDB的I/O性能。還有一種“隨機(jī)預(yù)讀”,它在MySQL version 5.6版本中默認(rèn)就是關(guān)閉的,并且在隨后的版本中將會慢慢廢除,所以這里就不再介紹了。
- 例如將向磁盤提交Page的動作設(shè)計(jì)為周期性且批量進(jìn)行,并且始終保持InnoDB Buffer Pool內(nèi)存區(qū)域的臟頁(Dirty Page)在一定的比例,這些策略主要由innodb_io_capacity、innodb_max_dirty_pages_pct、innodb_io_capacity_max等參數(shù)控制。
- 例如通過調(diào)整Innodb_Buffer_Pool_size參數(shù)獲得更大的InnoDB Buffer Pool內(nèi)存區(qū)域,存儲更多的Page。實(shí)際上Innodb Buffer Pool區(qū)域不僅包括我們已經(jīng)介紹的Page Cache數(shù)據(jù)部分,還包括其它的數(shù)據(jù)區(qū)塊。例如為了快速定位B+樹索引的Hash Index結(jié)構(gòu)。調(diào)整Innodb_Buffer_Pool_size參數(shù)將會使這些數(shù)據(jù)區(qū)域都享受到內(nèi)存容量帶來的優(yōu)勢——至少不會頻繁地發(fā)生內(nèi)容空間的強(qiáng)制清理。
按照本專題之前文章介紹的塊存儲方案來看(《架構(gòu)設(shè)計(jì)(1)——塊存儲方案(1)》、《架構(gòu)設(shè)計(jì)(2)——塊存儲方案(2)》),如果存儲MySQL數(shù)據(jù)的底層硬件介質(zhì)就只是一塊機(jī)械磁盤,那么無論怎樣優(yōu)化MySQL的其它各參數(shù),MySQL實(shí)際對磁盤的順序I/O速度理論上也只有100MB/S左右。這還是不計(jì)算硬件層校驗(yàn)、不計(jì)算不同文件系統(tǒng)處理耗時(shí)等等時(shí)間,所以實(shí)際I/O速度只會更慢。另外如果采用單塊機(jī)械磁盤存儲MySQL的數(shù)據(jù),那么磁盤空間的擴(kuò)容也是一個(gè)問題。目前市場上能買到的容量最大的單塊機(jī)械磁盤,它的存儲空間也只有10TB。當(dāng)這部分容量使用完后想要進(jìn)行擴(kuò)容就基本上是就一個(gè)不可能完成的任務(wù)了。最后這種存儲方式還有安全性的問題,單塊機(jī)械磁盤在持續(xù)的高I/O環(huán)境下是很容易損壞的,只要是有一定資金支持的公司,機(jī)械磁盤本身就看做耗材。
所以即使是初創(chuàng)型公司的線上生產(chǎn)環(huán)境,本文也不推薦使用單塊機(jī)械磁盤存儲任何需要持久保存的業(yè)務(wù)數(shù)據(jù)。如果是因?yàn)橘Y金問題,則推薦使用一些云服務(wù)商提供的現(xiàn)成PaaS環(huán)境,原因是這些PaaS環(huán)境本身就支持?jǐn)?shù)據(jù)恢復(fù)功能,且利用云服務(wù)商已經(jīng)建設(shè)好的價(jià)格不菲的硬件/軟件環(huán)境,MySQL數(shù)據(jù)庫的I/O性能和計(jì)算性能暫時(shí)還不會成為業(yè)務(wù)系統(tǒng)的瓶頸。
總結(jié)
以上是生活随笔為你收集整理的mysql insert 不需要日志_MySQL数据库性能优化(1)「转」的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle dat 文件怎么打开,DA
- 下一篇: mysql 表ful,你所不知的tabl