摘要:?前言 日志先行的技術(shù)廣泛應(yīng)用于現(xiàn)代數(shù)據(jù)庫(kù)中,其保證了數(shù)據(jù)庫(kù)在數(shù)據(jù)不丟的情況下,進(jìn)一步提高了數(shù)據(jù)庫(kù)的性能。本文主要分析了WAL模塊在MySQL各個(gè)版本中的演進(jìn)以及在阿里云新一代數(shù)據(jù)庫(kù)POLARDB中的改進(jìn)。
前言
日志先行的技術(shù)廣泛應(yīng)用于現(xiàn)代數(shù)據(jù)庫(kù)中,其保證了數(shù)據(jù)庫(kù)在數(shù)據(jù)不丟的情況下,進(jìn)一步提高了數(shù)據(jù)庫(kù)的性能。本文主要分析了WAL模塊在MySQL各個(gè)版本中的演進(jìn)以及在阿里云新一代數(shù)據(jù)庫(kù)POLARDB中的改進(jìn)。
基礎(chǔ)知識(shí)
用戶如果對(duì)數(shù)據(jù)庫(kù)中的數(shù)據(jù)就行了修改,必須保證日志先于數(shù)據(jù)落盤。當(dāng)日志落盤后,就可以給用戶返回操作成功,并不需要保證當(dāng)時(shí)對(duì)數(shù)據(jù)的修改也落盤。如果數(shù)據(jù)庫(kù)在日志落盤前crash,那么相應(yīng)的數(shù)據(jù)修改會(huì)回滾。在日志落盤后crash,會(huì)保證相應(yīng)的修改不丟失。有一點(diǎn)要注意,雖然日志落盤后,就可以給用戶返回操作成功,但是由于落盤和返回成功包之間有一個(gè)微小的時(shí)間差,所以即使用戶沒有收到成功消息,修改也可能已經(jīng)成功了,這個(gè)時(shí)候就需要用戶在數(shù)據(jù)庫(kù)恢復(fù)后,通過再次查詢來確定當(dāng)前的狀態(tài)。 在日志先行技術(shù)之前,數(shù)據(jù)庫(kù)只需要把修改的數(shù)據(jù)刷回磁盤即可,用了這項(xiàng)技術(shù),除了修改的數(shù)據(jù),還需要多寫一份日志,也就是磁盤寫入量反而增大,但是由于日志是順序的且往往先存在內(nèi)存里然后批量往磁盤刷新,相比數(shù)據(jù)的離散寫入,日志的寫入開銷比較小。 日志先行技術(shù)有兩個(gè)問題需要工程上解決:
日志刷盤問題。由于所有對(duì)數(shù)據(jù)的修改都需要寫日志,當(dāng)并發(fā)量很大的時(shí)候,必然會(huì)導(dǎo)致日志的寫入量也很大,為了性能考慮,往往需要先寫到一個(gè)日志緩沖區(qū),然后在按照一定規(guī)則刷入磁盤,此外日志緩沖區(qū)大小有限,用戶會(huì)源源不斷的生產(chǎn)日志,數(shù)據(jù)庫(kù)還需要不斷的把緩存區(qū)中的日志刷入磁盤,緩存區(qū)才可以復(fù)用,因此,這里就構(gòu)成了一個(gè)典型的生產(chǎn)者和消費(fèi)者模型?,F(xiàn)代數(shù)據(jù)庫(kù)必須直面這個(gè)問題,在高并發(fā)的情況下,這一定是個(gè)性能瓶頸,也一定是個(gè)鎖沖突的熱點(diǎn)。
數(shù)據(jù)刷盤問題。在用戶收到操作成功的時(shí)候,用戶的數(shù)據(jù)不一定已經(jīng)被持久化了,很有可能修改還沒有落盤,這就需要數(shù)據(jù)庫(kù)有一套刷數(shù)據(jù)的機(jī)制,專業(yè)術(shù)語叫做刷臟頁算法。臟頁(內(nèi)存中被修改的但是還沒落盤的數(shù)據(jù)頁)在源源不斷的產(chǎn)生,然后要持續(xù)的刷入磁盤,這里又湊成一個(gè)生產(chǎn)者消費(fèi)者模型,影響數(shù)據(jù)庫(kù)的性能。如果在臟頁沒被刷入磁盤,但是數(shù)據(jù)庫(kù)異常crash了,這個(gè)就需要做奔潰恢復(fù),具體的流程是,在接受用戶請(qǐng)求之前,從checkpoint點(diǎn)(這個(gè)點(diǎn)之前的日志對(duì)應(yīng)的數(shù)據(jù)頁一定已經(jīng)持久化到磁盤了)開始掃描日志,然后應(yīng)用日志,從而把在內(nèi)存中丟失的更新找回來,最后重新刷入磁盤。這里有一個(gè)很重要的點(diǎn):在數(shù)據(jù)庫(kù)正常啟動(dòng)的期間,checkpoint怎么確定,如果checkpoint做的慢了,就會(huì)導(dǎo)致奔潰恢復(fù)時(shí)間過長(zhǎng),從而影響數(shù)據(jù)庫(kù)可用性,如果做的快了,會(huì)導(dǎo)致刷臟壓力過大,甚至數(shù)據(jù)丟失。
MySQL中為了解決上述兩個(gè)問題,采用了以下機(jī)制:
當(dāng)用戶線程產(chǎn)生日志的時(shí)候,首先緩存在一個(gè)線程私有的變量(mtr)里面,只有完成某些原子操作(例如完成索引分裂或者合并等)的時(shí)候,才把日志提交到全局的日志緩存區(qū)中。全局緩存區(qū)的大小(innodb_log_file_size)可以動(dòng)態(tài)配置。當(dāng)線程的事務(wù)執(zhí)行完后,會(huì)按照當(dāng)前的配置(innodb_flush_log_at_trx_commit)決定是否需要把日志從緩沖區(qū)刷到磁盤。
當(dāng)把日志成功拷貝到全局日志緩沖區(qū)后,會(huì)繼續(xù)把當(dāng)前已經(jīng)被修改過的臟頁加入到一個(gè)全局的臟頁鏈表中。這個(gè)鏈表有一個(gè)特性:按照最早被修改的時(shí)間排序。例如,有數(shù)據(jù)頁A,B,C,數(shù)據(jù)頁A早上9點(diǎn)被第一次修改,數(shù)據(jù)頁B早上9點(diǎn)01分被第一次修改,數(shù)據(jù)頁C早上9點(diǎn)02分被第一次修改,那么在這個(gè)鏈表上數(shù)據(jù)頁A在最前,B在中間,C在最后。即使數(shù)據(jù)頁A在早上9點(diǎn)之后又一次被修改了,他依然排在B和C之前。在數(shù)據(jù)頁上,有一個(gè)字段來記錄這個(gè)最早被修改的時(shí)間:oldest_modification,只不過單位不是時(shí)間,而是lsn,即從數(shù)據(jù)庫(kù)初始化開始,一共寫了多少個(gè)字節(jié)的日志,由于其是一個(gè)遞增的值,因此可以理解為廣義的時(shí)間,先寫的數(shù)據(jù),其產(chǎn)生的日志對(duì)應(yīng)的lsn一定比后寫的小。在臟頁列表上的數(shù)據(jù)頁,就是按照oldest_modification從小到大排序,刷臟頁的時(shí)候,就從oldest_modification小的地方開始。checkpoint就是臟頁列表中最小的那個(gè)oldest_modification,因?yàn)檫@種機(jī)制保證小于最小oldest_modification的修改都已經(jīng)刷入磁盤了。這里最重要的是,臟頁鏈表的有序性,假設(shè)這個(gè)有序性被打破了,如果數(shù)據(jù)庫(kù)異常crash,就會(huì)導(dǎo)致數(shù)據(jù)丟失。例如,數(shù)據(jù)頁ABC的oldest_modification分別為120,100,150,同時(shí)在臟頁鏈表上的順序依然為A,B,C,A在最前面,C在最后面。數(shù)據(jù)頁A被刷入磁盤,然后checkpoint被更新為120,但是數(shù)據(jù)頁B和C都還沒被刷入磁盤,這個(gè)時(shí)候,數(shù)據(jù)庫(kù)crash,重啟后,從checkpoint為120開始掃描日志,然后恢復(fù)數(shù)據(jù),我們會(huì)發(fā)現(xiàn),數(shù)據(jù)頁C的修改被恢復(fù)了,但是數(shù)據(jù)頁B的修改丟失了。
在第一點(diǎn)中的,我們提到了私有變量mtr,這個(gè)結(jié)構(gòu)除了存儲(chǔ)了修改產(chǎn)生的日志和臟頁外,還存儲(chǔ)了修改臟頁時(shí)加的鎖。在適當(dāng)?shù)臅r(shí)候(例如日志提交完且臟頁加入到臟頁鏈表)可以把鎖給釋放。
接下來,我們結(jié)合各個(gè)版本的實(shí)現(xiàn),來剖析一下具體實(shí)現(xiàn)細(xì)節(jié)。注意,以下內(nèi)容需要一點(diǎn)MySQL源碼基礎(chǔ),適合MySQL內(nèi)核開發(fā)者以及資深的DBA。
MySQL 5.1版本的處理方式
5.1的版本是MySQL比較早的版本,那個(gè)時(shí)候InnoDB還是一個(gè)插件。因此設(shè)計(jì)也相對(duì)粗糙,簡(jiǎn)化后的偽代碼如下:
日志進(jìn)入全局緩存:
mutex_enter(log_sys->mutex); copy?local?redo?log?to?global?log?buffer mtr.start_lsn?=?log_sys->lsn mtr.end_lsn?=?log_sys->lsn?+?log_len?+?log_block_head_or_tail_len increase?global?lsn:?log_sys->lsn,?log_sys->buf_freefor?every?lock?in?mtr????if?(lock?==?share?lock) ????????release?share?lock?directly????else?if?(lock?==?exclusive?lock)????????if?(lock?page?is?dirty)???????????? ????????if?(page.oldest_modification?==?0)??//This?means?this?page?is?not?in?flush?list ????????page.oldest_modification?=?mtr.start_lsn ???????????????add?to?flush?list???????????//?have?one?flush?list?only ????????release?exclusive?lock mutex_exit(log_sys->mutex);
日志寫入磁盤:
mutex_enter(log_sys->mutex);log_sys->write_lsn?=?log_sys->lsn; write?log?to?log?file mutex_exit(log_sys->mutex);
更新checkpoint:
page?=?get_first_page(flush_list) checkpoint_lsn?=?page.oldest_modification write?checkpoint_lsn?to?log?file
奔潰恢復(fù):
read?checkpoint_lsn?from?log?filestart?parse?and?apply?redo?log?from?checkpoint_lsn?point
從上述偽代碼中可以看出,由于日志進(jìn)入全局的緩存都在臨界區(qū)內(nèi),不但保證了拷貝日志的有序性,也保證了臟頁進(jìn)入臟頁鏈表的有序性。需要獲取checkpoint_lsn時(shí),只需從臟頁鏈表中獲取第一個(gè)數(shù)據(jù)頁的oldest_modification即可。奔潰恢復(fù)也只需要從記錄的checkpoint點(diǎn)開始掃描即可。在高并發(fā)的場(chǎng)景下,有很多線程需要把自己的local日志拷貝到全局緩存,會(huì)造成鎖熱點(diǎn),另外在全局日志寫入日志文件的地方,也需要加鎖,進(jìn)一步造成了鎖的爭(zhēng)搶。此外,這個(gè)數(shù)據(jù)庫(kù)的緩存(Buffer Pool)只有一個(gè)臟頁鏈表,性能也不高。這種方式存在于早期的InnoDB代碼中,通俗易懂,但在現(xiàn)在的多核系統(tǒng)上,顯然不能做到很好的擴(kuò)展性。
MySQL 5.5,5.6,5.7版本的處理方式
這三個(gè)版本是目前主流的MySQL版本,很多分支都在上面做了不少優(yōu)化,但是主要的處理邏輯變化依然不大:
日志進(jìn)入全局緩存:
mutex_enter(log_sys->mutex); copy?local?redo?log?to?global?log?buffer mtr.start_lsn?=?log_sys->lsn mtr.end_lsn?=?log_sys->lsn?+?log_len?+?log_block_head_or_tail_len increase?global?lsn:?log_sys->lsn,?log_sys->buf_free mutex_enter(log_sys->log_flush_order_mutex); mutex_exit(log_sys->mutex);for?every?page?in?mtr???if?(lock?==?exclusive?lock)????if?(page?is?dirty)???????? if?(page.oldest_modification?==?0)??//This?means?this?page?is?not?in?flush?list ????????page.oldest_modification?=?mtr.start_lsn ????????????????add?to?flush?list?according?to?its?buffer?pool?instance mutex_exit(log_sys->log_flush_order_mutex);for?every?lock?in?mtr ????release?all?lock?directly
日志寫入磁盤:
mutex_enter(log_sys->mutex);log_sys->write_lsn?=?log_sys->lsn; write?log?to?log?file mutex_exit(log_sys->mutex);
更新checkpoint:
for?ervery?flush?list: ????page?=?get_first_page(curr_flush_list);????if?current_oldest_modification?>?page.oldest_modification ????current_oldest_modification?=?page.oldest_modification checkpoint_lsn?=?current_oldest_modification write?checkpoint_lsn?to?log?file
奔潰恢復(fù):
read?checkpoint_lsn?from?log?filestart?parse?and?apply?redo?log?from?checkpoint_lsn?point
主流的版本中最重要的一個(gè)優(yōu)化是,除了log_sys->mutex外,引入了另外一把鎖log_sys->log_flush_order_mutex。在臟頁加入到臟頁鏈表的操作中,不需要log_sys->mutex保護(hù),而是需要log_sys->log_flush_order_mutex保護(hù),這樣減少了log_sys->mutex的臨界區(qū),從而減少了熱點(diǎn)。此外,引入多個(gè)臟頁鏈表,減少了單個(gè)鏈表帶來的沖突。 注意,主流的分支還做了很多其他的優(yōu)化,例如:
引入雙全局日志緩存。如果只有一個(gè)全局日志緩存,當(dāng)這個(gè)日志緩存在寫盤的時(shí)候,會(huì)導(dǎo)致后續(xù)的用戶線程無法往里面拷貝日志,直到刷盤結(jié)束。有了雙日志緩存,其中一個(gè)用來接收用戶提交過來的日志,另外一個(gè)可以用來把之前的日志刷盤,這樣用戶線程不需要等待。
日志自動(dòng)擴(kuò)展。如果發(fā)現(xiàn)當(dāng)前需要拷貝的日志比全局的日志緩存一半還大,就會(huì)自動(dòng)把全局日志緩存給擴(kuò)大一倍。注意,只要擴(kuò)大后,就不會(huì)再縮小了。
日志對(duì)齊。早期的磁盤都是512原子寫,現(xiàn)代的SSD磁盤大部分是4K原子寫。如果小于4K的寫入,會(huì)導(dǎo)致先把4K先讀取出來,然后內(nèi)存中修改,再寫下去,性能低下。但是有了日志對(duì)齊這個(gè)優(yōu)化后,可以以指定大小刷日志,不夠大的后面填0補(bǔ)齊,能提高寫入效率。 這里貼一個(gè)優(yōu)化后的日志寫入磁盤的偽代碼:
mutex_enter(log_sys->write_mutex); check?if?other?thead?has?done?write?for?us mutex_enter(log_sys->mutex); calculate?the?range?log?need?to?be?write switch?log?buffer?so?that?user?threads?can?still?copy?log?during?writing mutex_exit(log_sys->mutex); align?log?to?specified?size?if?needed write?log?to?log?file? log_sys->write_lsn?=?log_sys->lsn; mutex_exit(log_sys->write_mutex);
可以看到log_sys->mutex被進(jìn)一步縮小。往日志文件里面寫日志的階段已經(jīng)不許要log_sys->mutex保護(hù)了。 有了以上的優(yōu)化,MySQL的日志子系統(tǒng)在大多數(shù)場(chǎng)景下不會(huì)達(dá)到瓶頸。但是,用戶線程往全局日志緩存拷貝日志以及臟頁加入臟頁鏈表這兩個(gè)操作,依然是基于鎖機(jī)制的,很難發(fā)揮出多核系統(tǒng)的性能。
MySQL 8.0版本的處理方式
之前的版本雖然做了很多優(yōu)化,但是沒有真正做到lock free,在高并發(fā)下,可以看到很多鎖沖突。官方因此在這塊下了大力氣,徹頭徹尾的大改了一番。 詳細(xì)細(xì)節(jié)可以參考上個(gè)月這篇月報(bào)。 這里再簡(jiǎn)單概括一下。 在日志寫入階段,通過atomic變量分配保留空間,由于atomic變量增長(zhǎng)是個(gè)原子操作,所以這一步不要加鎖。分配完空間后,就可以拷貝日志,由于上一步中空間已經(jīng)被預(yù)留,所以多線程可以同時(shí)進(jìn)行拷貝,而不會(huì)導(dǎo)致日志有重疊。但是不能保證拷貝完成的先后順序,有可能先拷貝的,后完成,所以需要有一種機(jī)制來保證某個(gè)點(diǎn)之前的日志已經(jīng)都拷貝到全局日志緩存了。這里,官方就引入了一種新的lock free數(shù)據(jù)結(jié)構(gòu)Link_buf,它是一個(gè)數(shù)組,用來標(biāo)記拷貝完成的情況。每個(gè)用戶線程完成拷貝后,就在那個(gè)數(shù)組中標(biāo)記一下,然后后臺(tái)再開一個(gè)線程來計(jì)算是否有連續(xù)的塊完成拷貝了,完成了就可以把這些日志刷到磁盤。 在臟頁插入臟頁鏈表這一塊,官方也提出了一種有趣的算法,它也是基于新的lock free數(shù)據(jù)結(jié)構(gòu)Link_buf?;舅枷胧牵K頁鏈表的有序性可以被部分的打破,也就是說,在一定范圍內(nèi)可以無序,但是整體還是有序的。這個(gè)無序程序是受控的。假設(shè)臟頁鏈表第一個(gè)數(shù)據(jù)頁的oldest_modification為A, 在之前的版本中,這個(gè)臟頁鏈表后續(xù)的page的oldest_modification都嚴(yán)格大于等于A,也就是不存在一個(gè)數(shù)據(jù)頁比第一個(gè)數(shù)據(jù)頁還老。在MySQL 8.0中,后續(xù)的page的oldest_modification并不是嚴(yán)格大于等于A,可以比A小,但是必須大于等于A-L,這個(gè)L可以理解為無序度,是一個(gè)定值。那么問題來了,如果臟頁鏈表順序亂了,那么checkpoint怎么確定,或者說是,奔潰恢復(fù)后,從那個(gè)checkpoint_lsn開始掃描日志才能保證數(shù)據(jù)不丟。官方給出的解法是,checkpoint依然由臟頁鏈表中第一個(gè)數(shù)據(jù)頁的oldest_modification的確定,但是奔潰恢復(fù)從checkpoint_lsn-L開始掃描(有可能這個(gè)值不是一個(gè)mtr的邊界,因此需要調(diào)整)。 所以可以看到,官方通過link_buf這個(gè)數(shù)據(jù)結(jié)構(gòu)很巧妙的解決了局部日志往全局日志拷貝的問題以及臟頁插入臟頁鏈表的問題。由于都是lock free算法,因此擴(kuò)展性會(huì)比較好。 但是,從實(shí)際測(cè)試的情況來看,似乎是因?yàn)橛昧颂嗟臈l件變量event,在我們的測(cè)試中沒有官方標(biāo)稱的性能。后續(xù)我們會(huì)進(jìn)一步分析原因。
POLARDB FOR MYSQL的處理方式
POLARDB作為阿里云下一代關(guān)系型云數(shù)據(jù)庫(kù),我們自然在InnoDB日志子系統(tǒng)做了很多優(yōu)化,其中也包含了上述的領(lǐng)域。這里可以簡(jiǎn)單介紹一下我們的思路:
每個(gè)buffer pool instance都額外增加了一把讀寫鎖(rw_locks),主要用來控制對(duì)全局日志緩存的訪問。 此外還引入兩個(gè)存儲(chǔ)臟頁信息的集合,我們這里簡(jiǎn)稱in-flight set和ready-to-process set。主要用來臨時(shí)存儲(chǔ)臟頁信息。
日志進(jìn)入全局緩存:
release?all?share?locks?holded?by?this?mtr's?page acquire?log_buf?s-locks?for?all?buf_pool?instances?for?which?we?have?dirty?pages reserver?enough?space?on?log_buf?via?increasing?atomit?variables????????//Just?like?MySQL?8.0 copy?local?log?to?global?log?buffer add?all?pages?dirtied?by?this?mtr?to?in-flight?set release?all?exclusive?locks?holded?by?this?mtr's?page release?log_buf?s-locks?for?all?buf_pool?instances
日志寫入磁盤:
mutex_enter(log_sys->write_mutex) check?if?other?thead?has?done?write?for?us mutex_enter(log_sys->mutex) acquire?log_buf?x-locks?for?all?buf_pool?instances update?log_sys->lsn?to?newest switch?log?buffer?so?that?user?threads?can?still?copy?log?during?writing mutex_exit(log_sys->mutex) release?log_buf?x-locks?for?all?buf_pool?instances align?log?to?specified?size?if?needed write?log?to?log?file? log_sys->write_lsn?=?log_sys->lsn; mutex_exit(log_write_mutex)
刷臟線程(每個(gè)buffer pool instance):
acquire?log_buf?x-locks?for?specific?buffer?pool?instance toggle?in-flight?set?with?ready-to-process?set.?Only?this?thread?will?toggle?between?these? two.release?log_buf?x-locks?for?specific?buffer?pool?instancefor?each?page?in?ready-to-process??? add?page?to?flush?listdo?normal?flush?page?operations
更新checkpoint:
for?ervery?flush?list: ????acquire?log_buf?x-locks?for?specific?buffer?pool?instance ????ready_to_process_lsn?=?minimum?oldest_modification?in?ready-to-process?set ????flush_list_lsn?=?get_first_page(curr_flush_list).oldest_modification ????min_lsn?=?min(ready_to_process_lsn,?flush_list_lsn)????release?log_buf?x-locks?for?specific?buffer?pool?instance ????if?current_oldest_modification?>?min_lsn ????current_oldest_modification?=?min_lsn checkpoint_lsn?=?current_oldest_modification write?checkpoint_lsn?to?log?file
奔潰恢復(fù):
read?checkpoint_lsn?from?log?filestart?parse?and?apply?redo?log?from?checkpoint_lsn?point
在局部日志拷貝入全局日志這塊,與官方MySQL 8.0類似,首先利用atomic變量的原子增長(zhǎng)來分配空間,但是MySQL 8.0是使用link_buf來保證拷貝完成,而在POLARDB中,我們使用讀寫鎖的機(jī)制,即在拷貝之前加上讀鎖,拷貝完才釋放讀鎖,而在日志寫入磁盤前,首先嘗試加上寫鎖,利用寫鎖和讀鎖互斥的特性,保證在獲取寫鎖時(shí)所有讀鎖都釋放,即所有拷貝操作都完成。 在臟頁進(jìn)入臟頁鏈表這塊,官方MySQL允許臟頁鏈表有一定的無序度(也是通過link_buf保證),然后通過在奔潰恢復(fù)的時(shí)候從checkpoint_lsn-L開始掃描的機(jī)制,來保證數(shù)據(jù)的一致性。在POLARDB中,我們解決辦法是,把臟頁臨時(shí)加入到一個(gè)集合,在刷臟線程工作前再按順序加入臟頁鏈表,通過獲取寫鎖來保證在加入臟頁鏈表前,整個(gè)集合是完整的。換句話說,假設(shè)這個(gè)臟頁集合最小的oldest_modification為A,那么可以保證沒有加入臟頁集合的臟頁的oldest_modification都大于等于A。 從臟頁集合加入到臟頁鏈表的操作,我們沒有加鎖,所以在更行checkpoint的時(shí)候,我們需要使用min(ready_to_process_lsn, flush_list_lsn)來作為checkpoint_lsn。在奔潰恢復(fù)的時(shí)候,直接從checkpoint_lsn掃描即可。 此外,我們?cè)赑OLARDB上,還做了額外的優(yōu)化:
提前釋放page的共享鎖。如果一個(gè)數(shù)據(jù)頁被加了共享鎖,說明沒有被修改,只是被讀取而已,我們可以提前釋放掉,這有助于減少熱點(diǎn)數(shù)據(jù)頁的鎖沖突。
在日志進(jìn)入全局緩存時(shí),我們沒有及時(shí)更新log_sys->lsn,而是先更新另外一個(gè)變量,當(dāng)在日志寫入磁盤前,即獲取log_buf寫鎖后,然后在更新log_sys->lsn。主要是為了減少?zèng)_突。
最后我們測(cè)試了一下性能,在non_index_updates的全內(nèi)存高并發(fā)測(cè)試下,性能有10%的提高。
Upstream?5.6.40:?71KMySQL-8.0:?132KPolarDB?(master):?162KPolarDB(master?+?mtr_optimize):?178K
當(dāng)然,這不是我們最高的性能,可以小小透露一下,通過對(duì)事務(wù)子系統(tǒng)的優(yōu)化,我們可以達(dá)到200K的性能。 更多更好用的功能都在路上,歡迎使用POLARDB!
總結(jié)
日志子系統(tǒng)是關(guān)系型數(shù)據(jù)庫(kù)不可獲取的模塊,也是數(shù)據(jù)庫(kù)內(nèi)核開發(fā)者非常感興趣的模塊,本文結(jié)合代碼分析了MySQL不同版本的WAL機(jī)制的實(shí)現(xiàn),希望對(duì)大家有所幫助。
評(píng)論
查看更多