偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

MySQL引擎特性:InnoDB IO子系統(tǒng)

數(shù)據(jù)庫 MySQL
InnoDB做為一款成熟的跨平臺(tái)數(shù)據(jù)庫引擎,其實(shí)現(xiàn)了一套高效易用的IO接口,包括同步異步IO,IO合并等。本文簡(jiǎn)單介紹一下其內(nèi)部實(shí)現(xiàn),主要的代碼集中在os0file.cc這個(gè)文件中。本文的分析默認(rèn)基于MySQL 5.6,CentOS 6,gcc 4.8,其他版本的信息會(huì)另行指出。

 

前言

InnoDB做為一款成熟的跨平臺(tái)數(shù)據(jù)庫引擎,其實(shí)現(xiàn)了一套高效易用的IO接口,包括同步異步IO,IO合并等。本文簡(jiǎn)單介紹一下其內(nèi)部實(shí)現(xiàn),主要的代碼集中在os0file.cc這個(gè)文件中。本文的分析默認(rèn)基于MySQL 5.6,CentOS 6,gcc 4.8,其他版本的信息會(huì)另行指出。

基礎(chǔ)知識(shí)

WAL技術(shù) : 日志先行技術(shù),基本所有的數(shù)據(jù)庫,都使用了這個(gè)技術(shù)。簡(jiǎn)單的說,就是需要寫數(shù)據(jù)塊的時(shí)候,數(shù)據(jù)庫前臺(tái)線程把對(duì)應(yīng)的日志先寫(批量順序?qū)懀┑酱疟P上,然后就告訴客戶端操作成功,至于真正寫數(shù)據(jù)塊的操作(離散隨機(jī)寫)則放到后臺(tái)IO線程中。使用了這個(gè)技術(shù),雖然多了一個(gè)磁盤寫入操作,但是由于日志是批量順序?qū)?,效率很高,所以客戶端很快就能得到相?yīng)。此外,如果在真正的數(shù)據(jù)塊落盤之前,數(shù)據(jù)庫奔潰,重啟時(shí)候,數(shù)據(jù)庫可以使用日志來做崩潰恢復(fù),不會(huì)導(dǎo)致數(shù)據(jù)丟失。

數(shù)據(jù)預(yù)讀 : 與數(shù)據(jù)塊A“相鄰”的數(shù)據(jù)塊B和C在A被讀取的時(shí)候,B和C也會(huì)有很大的概率被讀取,所以可以在讀取B的時(shí)候,提前把他們讀到內(nèi)存中,這就是數(shù)據(jù)預(yù)讀技術(shù)。這里說的相鄰有兩種含義,一種是物理上的相鄰,一種是邏輯上的相鄰。底層數(shù)據(jù)文件中相鄰,叫做物理上相鄰。如果數(shù)據(jù)文件中不相鄰,但是邏輯上相鄰(id=1的數(shù)據(jù)和id=2的數(shù)據(jù),邏輯上相鄰,但是物理上不一定相鄰,可能存在同一個(gè)文件中不同的位置),則叫邏輯相鄰。

文件打開模式 : Open系統(tǒng)調(diào)用常見的模式主要三種:O_DIRECT,O_SYNC以及default模式。O_DIRECT模式表示后續(xù)對(duì)文件的操作不使用文件系統(tǒng)的緩存,用戶態(tài)直接操作設(shè)備文件,繞過了內(nèi)核的緩存和優(yōu)化,從另外一個(gè)角度來說,使用O_DIRECT模式進(jìn)行寫文件,如果返回成功,數(shù)據(jù)就真的落盤了(不考慮磁盤自帶的緩存),使用O_DIRECT模式進(jìn)行讀文件,每次讀操作是真的從磁盤中讀取,不會(huì)從文件系統(tǒng)的緩存中讀取。O_SYNC表示使用操作系統(tǒng)緩存,對(duì)文件的讀寫都經(jīng)過內(nèi)核,但是這個(gè)模式還保證每次寫數(shù)據(jù)后,數(shù)據(jù)一定落盤。default模式與O_SYNC模式類似,只是寫數(shù)據(jù)后不保證數(shù)據(jù)一定落盤,數(shù)據(jù)有可能還在文件系統(tǒng)中,當(dāng)主機(jī)宕機(jī),數(shù)據(jù)有可能丟失。

此外,寫操作不僅需要修改或者增加的數(shù)據(jù)落盤,而且還需要文件元信息落盤,只有兩部分都落盤了,才能保證數(shù)據(jù)不丟。O_DIRECT模式不保證文件元信息落盤(但大部分文件系統(tǒng)都保證,Bug #45892),因此如果不做其他操作,用O_DIRECT寫文件后,也存在丟失的風(fēng)險(xiǎn)。O_SYNC則保證數(shù)據(jù)和元信息都落盤。default模式兩種數(shù)據(jù)都不保證。

調(diào)用函數(shù)fsync后,能保證數(shù)據(jù)和日志都落盤,因此使用O_DIRECT和default模式打開的文件,寫完數(shù)據(jù),需要調(diào)用fsync函數(shù)。

同步IO : 我們常用的read/write函數(shù)(Linux上)就是這類IO,特點(diǎn)是,在函數(shù)執(zhí)行的時(shí)候,調(diào)用者會(huì)等待函數(shù)執(zhí)行完成,而且沒有消息通知機(jī)制,因?yàn)楹瘮?shù)返回了,就表示操作完成了,后續(xù)直接檢查返回值就可知道操作是否成功。這類IO操作,編程比較簡(jiǎn)單,在同一個(gè)線程中就能完成所有操作,但是需要調(diào)用者等待,在數(shù)據(jù)庫系統(tǒng)中,比較適合急需某些數(shù)據(jù)的時(shí)候調(diào)用,例如WAL中日志必須在返回客戶端前落盤,則進(jìn)行一次同步IO操作。

異步IO : 在數(shù)據(jù)庫中,后臺(tái)刷數(shù)據(jù)塊的IO線程,基本都使用了異步IO。數(shù)據(jù)庫前臺(tái)線程只需要把刷塊請(qǐng)求提交到異步IO的隊(duì)列中即可返回做其他事情,而后臺(tái)線程IO線程,則定期檢查這些提交的請(qǐng)求是否已經(jīng)完成,如果完成再做一些后續(xù)處理工作。同時(shí)異步IO由于常常是一批一批的請(qǐng)求提交,如果不同請(qǐng)求訪問同一個(gè)文件且偏移量連續(xù),則可以合并成一個(gè)IO請(qǐng)求。例如,***個(gè)請(qǐng)求讀取文件1,偏移量100開始的200字節(jié)數(shù)據(jù),第二個(gè)請(qǐng)求讀取文件1,偏移量300開始的100字節(jié)數(shù)據(jù),則這兩個(gè)請(qǐng)求可以合并為讀取文件1,偏移量100開始的300字節(jié)數(shù)據(jù)。數(shù)據(jù)預(yù)讀中的邏輯預(yù)讀也常常使用異步IO技術(shù)。

目前Linux上的異步IO庫,需要文件使用O_DIRECT模式打開,且數(shù)據(jù)塊存放的內(nèi)存地址、文件讀寫的偏移量和讀寫的數(shù)據(jù)量必須是文件系統(tǒng)邏輯塊大小的整數(shù)倍,文件系統(tǒng)邏輯塊大小可以使用類似sudo blockdev --getss /dev/sda5的語句查詢。如果上述三者不是文件系統(tǒng)邏輯塊大小的整數(shù)倍,則在調(diào)用讀寫函數(shù)時(shí)候會(huì)報(bào)錯(cuò)EINVAL,但是如果文件不使用O_DIRECT打開,則程序依然可以運(yùn)行,只是退化成同步IO,阻塞在io_submit函數(shù)調(diào)用上。

InnoDB常規(guī)IO操作以及同步IO

在InnoDB中,如果系統(tǒng)有pread/pwrite函數(shù)(os_file_read_func和os_file_write_func),則使用它們進(jìn)行讀寫,否則使用lseek+read/write方案。這個(gè)就是InnoDB同步IO。查看pread/pwrite文檔可知,這兩個(gè)函數(shù)不會(huì)改變文件句柄的偏移量且線程安全,所以多線程環(huán)境下推薦使用,而lseek+read/write方案則需要自己使用互斥鎖保護(hù),在高并發(fā)情況下,頻繁的陷入內(nèi)核態(tài),對(duì)性能有一定影響。

在InnoDB中,使用open系統(tǒng)調(diào)用打開文件(os_file_create_func),模式方面除了O_RDONLY(只讀),O_RDWR(讀寫),O_CREAT(創(chuàng)建文件)外,還使用了O_EXCL(保證是這個(gè)線程創(chuàng)建此文件)和O_TRUNC(清空文件)。默認(rèn)情況下(數(shù)據(jù)庫不設(shè)置為只讀模式),所有文件都以O(shè)_RDWR模式打開。innodb_flush_method這個(gè)參數(shù)比較重要,重點(diǎn)介紹一下:

  • 如果innodb_flush_method設(shè)置了O_DSYNC,日志文件(ib_logfileXXX)使用O_SYNC打開,因此寫完數(shù)據(jù)不需要調(diào)用函數(shù)fsync刷盤,數(shù)據(jù)文件(ibd)使用default模式打開,因此寫完數(shù)據(jù)需要調(diào)用fsync刷盤。
  • 如果innodb_flush_method設(shè)置了O_DIRECT,日志文件(ib_logfileXXX)使用default模式打開,寫完數(shù)據(jù)需要調(diào)用fsync函數(shù)刷盤,數(shù)據(jù)文件(ibd)使用O_DIRECT模式打開,寫完數(shù)據(jù)需要調(diào)用fsync函數(shù)刷盤。
  • 如果innodb_flush_method設(shè)置了fsync或者不設(shè)置,數(shù)據(jù)文件和日志文件都使用default模式打開,寫完數(shù)據(jù)都需要使用fsync來刷盤。
  • 如果innodb_flush_method設(shè)置為O_DIRECT_NO_FSYNC,文件打開方式與O_DIRECT模式類似,區(qū)別是,數(shù)據(jù)文件寫完后,不調(diào)用fsync函數(shù)來刷盤,主要針對(duì)O_DIRECT能保證文件的元數(shù)據(jù)也落盤的文件系統(tǒng)。

InnoDB目前還不支持使用O_DIRECT模式打開日志文件,也不支持使用O_SYNC模式打開數(shù)據(jù)文件。

注意,如果使用linux native aio(詳見下一節(jié)),innodb_flush_method一定要配置成O_DIRECT,否則會(huì)退化成同步IO(錯(cuò)誤日志中不會(huì)有任務(wù)提示)。

InnoDB使用了文件系統(tǒng)的文件鎖來保證只有一個(gè)進(jìn)程對(duì)某個(gè)文件進(jìn)行讀寫操作(os_file_lock),使用了建議鎖(Advisory locking),而不是強(qiáng)制鎖(Mandatory locking),因?yàn)閺?qiáng)制鎖在不少系統(tǒng)上有bug,包括linux。在非只讀模式下,所有文件打開后,都用文件鎖鎖住。

InnoDB中目錄的創(chuàng)建使用遞歸的方式(os_file_create_subdirs_if_needed和os_file_create_directory)。例如,需要?jiǎng)?chuàng)建/a/b/c/這個(gè)目錄,先創(chuàng)建c,然后b,然后a,創(chuàng)建目錄調(diào)用mkdir函數(shù)。此外,創(chuàng)建目錄上層需要調(diào)用os_file_create_simple_func函數(shù),而不是os_file_create_func,需要注意一下。

InnoDB也需要臨時(shí)文件,臨時(shí)文件的創(chuàng)建邏輯比較簡(jiǎn)單(os_file_create_tmpfile),就是在tmp目錄下成功創(chuàng)建一個(gè)文件后直接使用unlink函數(shù)釋放掉句柄,這樣當(dāng)進(jìn)程結(jié)束后(不管是正常結(jié)束還是異常結(jié)束),這個(gè)文件都會(huì)自動(dòng)釋放。InnoDB創(chuàng)建臨時(shí)文件,首先復(fù)用了server層函數(shù)mysql_tmpfile的邏輯,后續(xù)由于需要調(diào)用server層的函數(shù)來釋放資源,其又調(diào)用dup函數(shù)拷貝了一份句柄。

如果需要獲取某個(gè)文件的大小,InnoDB并不是去查文件的元數(shù)據(jù)(stat函數(shù)),而是使用lseek(file, 0, SEEK_END)的方式獲取文件大小,這樣做的原因是防止元信息更新延遲導(dǎo)致獲取的文件大小有誤。

InnoDB會(huì)預(yù)分配一個(gè)大小給所有新建的文件(包括數(shù)據(jù)和日志文件),預(yù)分配的文件內(nèi)容全部置為零(os_file_set_size),當(dāng)前文件被寫滿時(shí),再進(jìn)行擴(kuò)展。此外,在日志文件創(chuàng)建時(shí),即install_db階段,會(huì)以100MB的間隔在錯(cuò)誤日志中輸出分配進(jìn)度。

總體來說,常規(guī)IO操作和同步IO相對(duì)比較簡(jiǎn)單,但是在InnoDB中,數(shù)據(jù)文件的寫入基本都用了異步IO。

InnoDB異步IO

由于MySQL誕生在Linux native aio之前,所以在MySQL異步IO的代碼中,有兩種實(shí)現(xiàn)異步IO的方案。

***種是原始的Simulated aio,InnoDB在Linux native air被import進(jìn)來之前以及某些不支持air的系統(tǒng)上,自己模擬了一條aio的機(jī)制。異步讀寫請(qǐng)求提交時(shí),僅僅把它放入一個(gè)隊(duì)列中,然后就返回,程序可以去做其他事情。后臺(tái)有若干異步io處理線程(innobase_read_io_threads和innobase_write_io_threads這兩個(gè)參數(shù)控制)不斷從這個(gè)隊(duì)列中取出請(qǐng)求,然后使用同步IO的方式完成讀寫請(qǐng)求以及讀寫完成后的工作。

另外一種就是Native aio。目前在linux上使用io_submit,io_getevents等函數(shù)完成(不使用glibc aio,這個(gè)也是模擬的)。提交請(qǐng)求使用io_submit, 等待請(qǐng)求使用io_getevents。另外,window平臺(tái)上也有自己對(duì)應(yīng)的aio,這里就不介紹了,如果使用了window的技術(shù)棧,數(shù)據(jù)庫應(yīng)該會(huì)選用sqlserver。目前,其他平臺(tái)(Linux和window之外)都只能使用Simulate aio。

首先介紹一下一些通用的函數(shù)和結(jié)構(gòu),接下來分別詳細(xì)介紹一下Simulate alo和Linux上的Native aio。

在os0file.cc中定義了全局?jǐn)?shù)組,類型為os_aio_array_t,這些數(shù)組就是Simulate aio用來緩存讀寫請(qǐng)求的隊(duì)列,數(shù)組的每一個(gè)元素是os_aio_slot_t類型,里面記錄了每個(gè)IO請(qǐng)求的類型,文件的fd,偏移量,需要讀取的數(shù)據(jù)量,IO請(qǐng)求發(fā)起的時(shí)間,IO請(qǐng)求是否已經(jīng)完成等。另外,Linux native io中的struct iocb也在os_aio_slot_t中。數(shù)組結(jié)構(gòu)os_aio_slot_t中,記錄了一些統(tǒng)計(jì)信息,例如有多少數(shù)據(jù)元素(os_aio_slot_t)已經(jīng)被使用了,是否為空,是否為滿等。這樣的全局?jǐn)?shù)組一共有5個(gè),分別用來保存數(shù)據(jù)文件讀異步請(qǐng)求(os_aio_read_array),數(shù)據(jù)文件寫異步請(qǐng)求(os_aio_write_array),日志文件寫異步請(qǐng)求(os_aio_log_array),insert buffer寫異步請(qǐng)求(os_aio_ibuf_array),數(shù)據(jù)文件同步讀寫請(qǐng)求(os_aio_sync_array)。日志文件的數(shù)據(jù)塊寫入是同步IO,但是這里為什么還要給日志寫分配一個(gè)異步請(qǐng)求隊(duì)列(os_aio_log_array)呢?原因是,InnoDB日志文件的日志頭中,需要記錄checkpoint的信息,目前checkpoint信息的讀寫還是用異步IO來實(shí)現(xiàn)的,因?yàn)椴皇呛芫o急。在window平臺(tái)中,如果對(duì)特定文件使用了異步IO,就這個(gè)文件就不能使用同步IO了,所以引入了數(shù)據(jù)文件同步讀寫請(qǐng)求隊(duì)列(os_aio_sync_array)。日志文件不需要讀異步請(qǐng)求隊(duì)列,因?yàn)橹挥性谧霰紳⒒謴?fù)的時(shí)候日志才需要被讀取,而做崩潰恢復(fù)的時(shí)候,數(shù)據(jù)庫還不可用,因此完全沒必要搞成異步讀取模式。這里有一點(diǎn)需要注意,不管變量innobase_read_io_threads和innobase_write_io_threads兩個(gè)參數(shù)是多少,os_aio_read_array和os_aio_write_array都只有一個(gè),只不過數(shù)據(jù)中的os_aio_slot_t元素會(huì)相應(yīng)增加,在linux中,變量加1,元素?cái)?shù)量增加256。例如,innobase_read_io_threads=4,則os_aio_read_array數(shù)組被分成了四部分,每一個(gè)部分256個(gè)元素,每個(gè)部分都有自己獨(dú)立的鎖、信號(hào)量以及統(tǒng)計(jì)變量,用來模擬4個(gè)線程,innobase_write_io_threads類似。從這里我們也可以看出,每個(gè)異步read/write線程能緩存的讀寫請(qǐng)求是有上限的,即為256,如果超過這個(gè)數(shù),后續(xù)的異步請(qǐng)求需要等待。256可以理解為InnoDB層對(duì)異步IO并發(fā)數(shù)的控制,而在文件系統(tǒng)層和磁盤層面也有長(zhǎng)度限制,分別使用cat /sys/block/sda/queue/nr_requests和cat /sys/block/sdb/queue/nr_requests查詢。

os_aio_init在InnoDB啟動(dòng)的時(shí)候調(diào)用,用來初始化各種結(jié)構(gòu),包括上述的全局?jǐn)?shù)組,還有Simulate aio中用的鎖和互斥量。os_aio_free則釋放相應(yīng)的結(jié)構(gòu)。os_aio_print_XXX系列的函數(shù)用來輸出aio子系統(tǒng)的狀態(tài),主要用在show engine innodb status語句中。

Simulate aio

Simulate aio相對(duì)Native aio來說,由于InnoDB自己實(shí)現(xiàn)了一套模擬機(jī)制,相對(duì)比較復(fù)雜。

  • 入口函數(shù)為os_aio_func,在debug模式下,會(huì)校驗(yàn)一下參數(shù),例如數(shù)據(jù)塊存放的內(nèi)存地址、文件讀寫的偏移量和讀寫的數(shù)據(jù)量是否是OS_FILE_LOG_BLOCK_SIZE的整數(shù)倍,但是沒有檢驗(yàn)文件打開模式是否用了O_DIRECT,因?yàn)镾imulate aio最終都是使用同步IO,沒有必要一定要用O_DIRECT打開文件。
  • 校驗(yàn)通過后,就調(diào)用os_aio_array_reserve_slot,作用是把這個(gè)IO請(qǐng)求分配到某一個(gè)后臺(tái)io處理線程(innobase_xxxx_io_threads分配的,但其實(shí)是在同一個(gè)全局?jǐn)?shù)組中)中,并把io請(qǐng)求的相關(guān)信息記錄下來,方便后臺(tái)io線程處理。如果IO請(qǐng)求類型相同,請(qǐng)求同一個(gè)文件且偏移量比較接近(默認(rèn)情況下,偏移量差別在1M內(nèi)),則InnoDB會(huì)把這兩個(gè)請(qǐng)求分配到同一個(gè)io線程中,方便在后續(xù)步驟中IO合并。
  • 提交IO請(qǐng)求后,需要喚醒后臺(tái)io處理線程,因?yàn)槿绻笈_(tái)線程檢測(cè)到?jīng)]有IO請(qǐng)求,會(huì)進(jìn)入等待狀態(tài)(os_event_wait)。
  • 至此,函數(shù)返回,程序可以去干其他事情了,后續(xù)的IO處理交給后臺(tái)線程了。
  • 介紹一下后臺(tái)IO線程怎么處理的。
  • InnoDB啟動(dòng)時(shí),后臺(tái)IO線程會(huì)被啟動(dòng)(io_handler_thread)。其會(huì)調(diào)用os_aio_simulated_handle從全局?jǐn)?shù)組中取出IO請(qǐng)求,然后用同步IO處理,結(jié)束后,需要做收尾工作,例如,如果是寫請(qǐng)求的話,則需要在buffer pool中把對(duì)應(yīng)的數(shù)據(jù)頁從臟頁列表中移除。
  • os_aio_simulated_handle首先需要從數(shù)組中挑選出某個(gè)IO請(qǐng)求來執(zhí)行,挑選算法并不是簡(jiǎn)單的先進(jìn)先出,其挑選所有請(qǐng)求中offset最小的請(qǐng)求先處理,這樣做是為了后續(xù)的IO合并比較方便計(jì)算。但是這也容易導(dǎo)致某些offset特別大的孤立請(qǐng)求長(zhǎng)時(shí)間沒有被執(zhí)行到,也就是餓死,為了解決這個(gè)問題,在挑選IO請(qǐng)求之前,InnoDB會(huì)先做一次遍歷,如果發(fā)現(xiàn)有請(qǐng)求是2s前推送過來的(也就是等待了2s),但是還沒有被執(zhí)行,就優(yōu)先執(zhí)行最老的請(qǐng)求,防止這些請(qǐng)求被餓死,如果有兩個(gè)請(qǐng)求等待時(shí)間相同,則選擇offset小的請(qǐng)求。
  • os_aio_simulated_handle接下來要做的工作就是進(jìn)行IO合并,例如,讀請(qǐng)求1請(qǐng)求的是file1,offset100開始的200字節(jié),讀請(qǐng)求2請(qǐng)求的是file1,offset300開始的100字節(jié),則這兩個(gè)請(qǐng)求可以合并為一個(gè)請(qǐng)求:file1,offset100開始的300字節(jié),IO返回后,再把數(shù)據(jù)拷貝到原始請(qǐng)求的buffer中就可以了。寫請(qǐng)求也類似,在寫操作之前先把需要寫的數(shù)據(jù)拷貝到一個(gè)臨時(shí)空間,然后一次寫完。注意,只有在offset連續(xù)的情況下IO才會(huì)合并,有間斷或者重疊都不會(huì)合并,一模一樣的IO請(qǐng)求也不會(huì)合并,所以這里可以算是一個(gè)可優(yōu)化的點(diǎn)。
  • os_aio_simulated_handle如果發(fā)現(xiàn)現(xiàn)在沒有IO請(qǐng)求,就會(huì)進(jìn)入等待狀態(tài),等待被喚醒

綜上所述,可以看出IO請(qǐng)求是一個(gè)一個(gè)的push的對(duì)立面,每push進(jìn)一個(gè)后臺(tái)線程就拿去處理,如果后臺(tái)線程優(yōu)先級(jí)比較高的話,IO合并效果可能比較差,為了解決這個(gè)問題,Simulate aio提供類似組提交的功能,即一組IO請(qǐng)求提交后,才喚醒后臺(tái)線程,讓其統(tǒng)一進(jìn)行處理,這樣IO合并的效果會(huì)比較好。但這個(gè)依然有點(diǎn)小問題,如果后臺(tái)線程比較繁忙的話,其就不會(huì)進(jìn)入等待狀態(tài),也就是說只要請(qǐng)求進(jìn)入了隊(duì)列,就會(huì)被處理。這個(gè)問題在下面的Native aio中可以解決。

總體來說,InnoDB實(shí)現(xiàn)的這一套模擬機(jī)制還是比較安全可靠的,如果平臺(tái)不支持Native aio則使用這套機(jī)制來讀寫數(shù)據(jù)文件。

Linux native aio

如果系統(tǒng)安裝了libaio庫且在配置文件里面設(shè)置了innodb_use_native_aio=on則啟動(dòng)時(shí)候會(huì)使用Native aio。

  • 入口函數(shù)依然為os_aio_func,在debug模式下,依然會(huì)檢查傳入的參數(shù),同樣不會(huì)檢查文件是否以O(shè)_DIRECT模式打開,這算是一個(gè)有點(diǎn)風(fēng)險(xiǎn)的點(diǎn),如果用戶不知道linux native aio需要使用O_DIRECT模式打開文件才能發(fā)揮出aio的優(yōu)勢(shì),那么性能就不會(huì)達(dá)到預(yù)期。建議在此處做一下檢查,有問題輸出到錯(cuò)誤日志。
  • 檢查通過之后,與Simulated aio一樣,調(diào)用os_aio_array_reserve_slot,把IO請(qǐng)求分配給后臺(tái)線程,分配算法也考慮了后續(xù)的IO合并,與Simulated aio一樣。不同之處,主要是需要用IO請(qǐng)求的參數(shù)初始化iocb這個(gè)結(jié)構(gòu)。IO請(qǐng)求的相關(guān)信息除了需要初始化iocb外,也需要在全局?jǐn)?shù)組的slot中記錄一份,主要是為了在os_aio_print_XXX系列函數(shù)中統(tǒng)計(jì)方便。
  • 調(diào)用io_submit提交請(qǐng)求。
  • 至此,函數(shù)返回,程序可以去干其他事情了,后續(xù)的IO處理交給后臺(tái)線程了。

接下來是后臺(tái)IO線程。

  • 與Simulate aio類似,后臺(tái)IO線程也是在InnoDB啟動(dòng)時(shí)候啟動(dòng)。如果是Linux native aio,后續(xù)會(huì)調(diào)用os_aio_linux_handle這個(gè)函數(shù)。這個(gè)函數(shù)的作用與os_aio_simulated_handle類似,但是底層實(shí)現(xiàn)相對(duì)比較簡(jiǎn)單,其僅僅調(diào)用io_getevents函數(shù)等待IO請(qǐng)求完成。超時(shí)時(shí)間為0.5s,也就是說如果即使0.5內(nèi)沒有IO請(qǐng)求完成,函數(shù)也會(huì)返回,繼續(xù)調(diào)用io_getevents等待,當(dāng)然在等待前會(huì)判斷一下服務(wù)器是否處于關(guān)閉狀態(tài),如果是則退出。

在分發(fā)IO線程時(shí),盡量把相鄰的IO放在一個(gè)線程內(nèi),這個(gè)與Simulate aio類似,但是后續(xù)的IO合并操作,Simulate aio是自己實(shí)現(xiàn),Native aio則交給內(nèi)核完成了,因此代碼比較簡(jiǎn)單。

還要一個(gè)區(qū)別是,當(dāng)沒有IO請(qǐng)求的時(shí)候,Simulate aio會(huì)進(jìn)入等待狀態(tài),而Native aio則會(huì)每0.5秒醒來一次,做一些檢查工作,然后繼續(xù)等待。因此,當(dāng)有新的請(qǐng)求來時(shí),Simulated aio需要用戶線程喚醒,而Native aio不需要。此外,在服務(wù)器關(guān)閉時(shí),Simulate aio也需要喚醒,Native aio則不需要。

可以發(fā)現(xiàn),Native aio與Simulate aio類似,請(qǐng)求也是一個(gè)一個(gè)提交,然后一個(gè)一個(gè)處理,這樣會(huì)導(dǎo)致IO合并效果比較差。Facebook團(tuán)隊(duì)提交了一個(gè)Native aio的組提交優(yōu)化:把IO請(qǐng)求首先緩存,等IO請(qǐng)求都到了之后,再調(diào)用io_submit函數(shù),一口氣提交先前的所有請(qǐng)求(io_submit可以一次提交多個(gè)請(qǐng)求),這樣內(nèi)核就比較方便做IO優(yōu)化。Simulate aio在IO線程壓力大的情況下,組提交優(yōu)化會(huì)失效,而Native aio則不會(huì)。注意,組提交優(yōu)化,不能一口氣提交太多,如果超過了aio等待隊(duì)列長(zhǎng)度,會(huì)強(qiáng)制發(fā)起一次io_submit。

總結(jié)

 

本文詳細(xì)介紹了InnoDB中IO子系統(tǒng)的實(shí)現(xiàn)以及使用需要注意的點(diǎn)。InnoDB日志使用同步IO,數(shù)據(jù)使用異步IO,異步IO的寫盤順序也不是先進(jìn)先出的模式,這些點(diǎn)都需要注意。Simulate aio雖然有比較大的學(xué)習(xí)價(jià)值,但是在現(xiàn)代操作系統(tǒng)中,推薦使用Native aio。 

責(zé)任編輯:龐桂玉 來源: 數(shù)據(jù)庫開發(fā)
相關(guān)推薦

2017-12-15 10:20:56

MySQLInnoDB同步機(jī)制

2019-06-11 16:11:16

MySQLMyISAMInnoDB

2015-11-10 16:55:00

性能IO子系統(tǒng)Linux

2010-11-23 11:27:53

MySQL MyISA

2010-05-11 15:06:24

MySQL MyISA

2010-05-21 16:23:52

MySQL MyISA

2009-10-12 12:46:55

Linux內(nèi)核SCSI IO

2010-05-12 12:36:46

MySQL innod

2010-05-21 16:10:28

2014-09-22 13:31:46

Linux

2009-05-05 10:19:37

存儲(chǔ)引擎InnoDBMyISAM

2018-06-14 10:44:59

MySQLMyISAMInnoDB

2011-07-27 09:33:16

MySQL數(shù)據(jù)庫INNODB數(shù)據(jù)庫引擎

2009-05-19 09:58:41

MyISAMInnoDB存儲(chǔ)引擎

2022-12-14 07:32:40

InnoDBMySQL引擎

2015-10-29 13:44:06

MySQLinnodb引擎備份

2019-06-04 15:27:49

InnoDB存儲(chǔ)引擎

2017-09-11 14:50:55

MySQL數(shù)據(jù)表類型存儲(chǔ)引擎

2024-04-15 10:30:22

MySQL存儲(chǔ)引擎

2010-05-21 15:53:30

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)