本系列文章會總結 QEMU/KVM 和 Ceph 之間的整合:php
(1)QEMU-KVM 和 Ceph RBD 的 緩存機制總結html
(2)QEMU 的 RBD 塊驅動(block driver)node
(3)存儲卷掛接和設備名稱linux
QEMU-KVM 的緩存機制的概念不少,Linux/KVM I/O 軟件棧的層次也不少,網上介紹其緩存機制的文章不少。邊學習邊總結。本文結合 Ceph 在 QEMU/KVM 虛機中的使用,總結一下二者結合時緩存的各類選項和原理。git
先以客戶機(Guest OS) 中的應用寫本地磁盤爲例進行介紹。客戶機的本地磁盤,實際上是 KVM 主機上的一個鏡像文件虛擬出來的,所以,客戶機中的應用寫其本地磁盤,其實就是寫到KVM主機的本地文件內,這些文件是保存在 KVM 主機本地磁盤上。github
先來看看 I/O 協議棧的層次和各層次上的緩存狀況。數據庫
熟悉 Linux Kernel 的人都知道在內核的存儲體系中主要有兩種緩存,一是 Page Cache,二是 Buffer Cache。Page Cache 是在 Linux IO 棧中爲文件系統服務的緩存,而 Buffer Cache 是處於更下層的 Block Device 層,因爲應用大部分使用的存儲數據都是基於文件系統,所以 Buffer Cache 實際上只是引用了 Page Cache 的數據,而只有在直接使用塊設備跳過文件系統時,Buffer Cache 才真正掌握緩存。關於 Page Cache 和 Buffer Cache 更多的討論參加 What is the major difference between the buffer cache and the page cache?。後端
這些 Cache 都由內核中專門的數據回寫線程負責來刷新到塊設備中,應用可使用如 fsync, fdatasync(見下面第三部分說明)之類的系統調用來完成強制執行對某個文件數據的回寫。像數據一致性要求高的應用如 MySQL 這類數據庫服務一般有本身的日誌用來保證事務的原子性,日誌的數據被要求每次事務完成前經過 fsync 這類系統調用強制寫到塊設備上,不然可能在系統崩潰後形成數據的不一致。而 fsync 的實現取決於文件系統,文件系統會將要求數據從緩存中強制寫到持久設備中。緩存
仔細地看一下 fsync 函數(來源): 安全
fsync() transfers ("flushes") all modified in-core data of (i.e., modified buffer cache pages for) the file referred to by the file descriptor fd to the disk device (or other permanent storage device) so that all changed information can be retrieved even after the system crashed or was rebooted. This includes writing through or flushing a disk cache if present. The call blocks until the device reports that the transfer has completed. It also flushes metadata information associated with the file (see stat(2)).
它會 flush 系統中全部的 cache,包括 Page cache 和 Disk write cache 以及 RBDCache,將數據放入持久存儲。也就說說,操做系統在 flush Page cache 的時候, RBDCache 也會被 flush。
(來源)
主要組成部分:
Page Cache 是客戶機和主機操做系統維護的用來提升存儲 I/O 性能的緩存,它是 Linux 虛擬文件系統緩存的一部分,位於操做系統內存中,它是易失性的,所以,在操做系統奔潰或者系統掉電時,這些數據會消失。數據是否寫入 Page cache 能夠被控制。當會寫入 page cache 時,當數據被寫入 page cache 後,應用就認爲寫入完成了,隨後的讀操做也會從 page cache 中讀取數據,這樣性能會提升。可使用 fsync 來將數據從 page cache 中拷貝到持久存儲。
在 KVM 環境中,host os 和 guest os 都有 page cache,所以,最好是能繞過一個來提升性能。
該緩存的特色是讀的時候,操做系統先檢查頁緩存裏面是否有須要的數據,若是沒有就從設備讀取,返回給用戶的同時,加到緩存一份;寫的時候,直接寫到緩存去,再由後臺的進程按期涮到磁盤去。這樣的機制看起來很是的好,在實踐中也效果很好。
考慮到其易失性,須要考慮它的大小,特別是在 KVM 主機上。如今 KVM 主機的內存能夠很大。其內存越大, 那麼在 Page cache 中尚未 flush 到磁盤(虛擬或者物理的)的髒數據就越多,其丟失的後果就越嚴重。默認的話,Linux 2.6.32 在髒數據達到內存的 10% 的時候會自動開始 flush。
這是磁盤的 write cache,它會提升數據到存儲的寫性能。寫到 disk write cache 後,寫操做會被認爲完成了,即便數據還沒真正被寫入物理磁盤。這樣,若是 disk write cache 沒有備份電池的話,斷電將致使還沒有寫入物理磁盤的數據丟失。要強制數據被寫入磁盤,應用能夠經過操做系統能夠發出 fsync 命令。所以,disk write cache 會提到寫I/O 性能,可是,須要確保應用和存儲棧會將數據寫入磁盤中。若是 disk write cache 被關閉,那麼寫性能將降低,可是斷電時數據丟失將會避免。
從該過程能夠看出:
QEMU-KVM Linux 支持關閉和開啓任一一個 Page cache,也就是說有四種組合模式,分別會帶來不一樣的效果。在各類I/O的過程當中,最好是繞過一個或者兩個 Page cache。
寫 I/O 過程比較複雜,本文其他部分會詳細闡述。從 1.3 表格總結,基本上
關於 guest os page cache,看起來它主要是做爲讀緩存,而對於寫,沒有一種模式是以寫入它做爲寫入結束標誌的。
在 libvirt xml 中使用 'cache' 參數來指定driver的緩存模式,好比:
<disk type='file' device='disk'> #對於 type,'file' 表示是 host 上的文件,'network' 表示經過網絡訪問,好比Ceph <driver name='qemu' type='raw' cache='writeback'/>
QEMU/KVM 支持以下這些緩存模式做爲 ‘cache’ 的可選值:
緩存模式 | 說明 | GUEST OS Page cache | Host OS Page cache | Disk write cache | 被認爲數據寫入成功 | 數據安全性 | |
cache = unsafe | 跟 writeback 相似,只是會忽略 GUEST OS 的 flush 操做,徹底由 HOST OS 控制 flush |
|
E | E | Host page cache | 最不安全,只有在特定的場合纔會使用 | |
Cache=writeback | I/O 寫到 HOST OS Page cache 就算成功,支持 GUEST OS flush 操做。 效率最快,可是也最不安全 |
Bypass(?不肯定) | E | E | Host Page Cache | 不安全. (only for temporary data where potential data loss is not a concern ) | |
Cache=none | 客戶機的I/O 不會被緩存到 page cache,而是會放在 disk write cache。 這種模式對寫效率比較好,由於是寫到 disk cache,可是讀效率不高,由於沒有放到 page cache。所以,能夠在大 I/O 寫需求時使用這種模式。
也就是常說的 O_DIRECT I/O 模式 |
Bypass | Bypass | E | Disk write cache | 不安全. 若是要保證安全的話,須要disk cache備份電池或者電容,或者使用 fync | |
Cache=writethrough | I/O 數據會被同步寫入 Host Page cache 和 disk。至關於每寫一次,就會 flush 一次,將 page cache 中的數據寫入持久存儲。 這種模式,會將數據放入 Page cache,所以便於未來的讀;而繞過 disk write cache,會致使寫效率較低。所以,這是較慢的模式,適合於寫I/O不大,可是讀I/O相對較大的狀況,最好是用在小規模的有低 I/O 需求客戶機的場景中。 也就是常說的 O_SYNC I/O 模式 |
E | E | Bypass | disk | 安全 | |
Cache=DirectSync | 跟 writethrough 相似,只是不寫入 HOST OS Page cache 也就是常說的 O_DIRECT & O_SYNC 模式 |
Bypass | Bypass | Bypass | disk | 同 」O_SYNC「,對一些數據庫應用來講,每每會直接使用這種模式,直接將數據寫到數據盤 | |
cache=default | 使用各類driver 類型的默認cache 模式 qcow2:默認 writeback |
看看性能比較:
基本結論:
上面的基本結論中,writethrough 是最安全的,可是效率也是最低的。它將數據放在 HOST Page Cache 中,一方面來支持讀緩存,另外一方面,在每個 write 操做後,都執行 fsync,確保數據被寫入物理存儲。只有在數據被寫入磁盤後,寫操做纔會標記爲成功。這種模式下,客戶機的 virtual storage adapter 會被通知不會使用 writeback 模式,所以,它不會主動發送 fsync 命令,由於它是重複的,不須要的。
那還有沒有什麼辦法使它在保持數據可靠性的同時,使它的效率提升一些呢?答案是 KVM Write barrier 功能。新的 KVM 版本中,啓用了 「barrier-passing」 功能,它能保證在無論是用什麼緩存模式下,將客戶機上應用寫入的數據 100% 寫入持久存儲。
好吧,這真是個神器。。那它是如何實現的呢?以 fio 工具爲例,在支持 write barrier 的客戶機操做系統上,在使用 direct 和 sync 參數的狀況下,會使用這種模式。它在寫入部分數據之後,會使得操做系統發出一個 fdatasync 命令,這樣 QEMU-KVM 就會將緩存中的數據 flush 到物理磁盤上。
基本過程:
看起來和 writethrough 差很少是吧。可是它的效率比 writethrough 高。二者的區別在於,writethrough 是每次 write 都會發 fsync,而 barrier-passing 是在若干個寫操做或者一個會話以後發 fdatasync 命令,所以其效率更高。
也能夠看到,使用它是有條件的:
也能夠看到,應用在須要的時候發出 flush 指令是關鍵。一方面,Cache 都由內核中專門的數據回寫線程負責來刷新到塊設備中;另外一方面,應用可使用如 fsync(2), fdatasync(2) 之類的系統調用來完成強制執行對某個文件數據的回寫。像數據一致性要求高的應用如 MySQL 這類數據庫服務一般有本身的日誌用來保證事務的原子性,日誌的數據被要求每次事務完成前經過 fsync(2) 這類系統調用強制寫到塊設備上,不然可能在系統崩潰後形成數據的不一致。而 fsync(2) 的實現取決於文件系統,文件系統會將要求數據從緩存中強制寫到持久設備中。相似地,支持 librbd 的QEMU 在適當的時候也會發出 flush 指令。
以 fio 爲例,設置有兩個參數時,會有 flush 指令發出:
fsync=int How many I/Os to perform before issuing an fsync(2) of dirty data. If 0, don't sync. Default: 0. fdatasync=int Like fsync, but uses fdatasync(2) instead to only sync the data parts of the file. Default: 0.
須要注意的是,頻繁的發送(int 值設置的比較小),會影響 IOPS 的值。爲了測得最大的IOPS,能夠在測試準備階段發一個sync,而後再收集階段就不發sync,徹底由 RBDCache 本身的機制去 flush;或者須要的話,把 int 值設得比較大,來模擬一些應用場景。
考慮到 KVM write barrier 的原理和 KVM 各類緩存模式的原理,顯而易見,writeback + barrier 的方式下,能夠實現 效率最高+數據安全 這種最優效果。
Linux 系統中被用於 Page cache 的主內存能夠經過 free -m 命令來查看:
root@controller:~# free -m total used free shared buffers cached Mem: 4867 3586 1280 0 193 557 -/+ buffers/cache: 2834 2032 Swap: 2022 0 2022
寫 Page cache:
(1)當數據被寫時,一般狀況下,它首先會被寫入 page cache,被看成一個 dirty page 來管理。’dirty‘ 的意思是,數據還保存在 page cache 中,還須要被寫入底下的持久存儲。dirty pages 中的內容會被系統週期性地、以及使用諸如 sync 或者 fsync 的系統調用來寫入持久存儲。
root@controller:~# cat /proc/meminfo | grep Dirty Dirty: 148 kB root@controller:~# sync root@controller:~# cat /proc/meminfo | grep Dirty Dirty: 0 kB
sync writes any data buffered in memory out to disk. This can include (but is not limited to) modified superblocks, modified inodes, and delayed reads and writes.
(2)當數據被從持久性存儲讀出時,它也會被寫入 page cache。所以,當連續兩次讀時,第二次會比第一次快,由於第一次讀後把數據寫入了 page cache,第二次就直接從這裏讀了。完整過程以下:
須要注意的是,須要結合應用的須要,來決定是否在寫數據時一併寫入 page cache。
QEMU 可以支持將主機的一個塊設備映射給客戶機,可是從 0.15 版本開始,就再也不須要先將一個 Ceph volume 映射到主機再給客戶機了。如今,QEMU 能夠直接經過 librbd 來象 virtual block device 同樣訪問 Ceph image。這既提升了性能,也使得可使用 RBDCache 了。
RBDCache 是 Ceph 的塊存儲接口實現庫 Librbd 用來在客戶端側緩存數據的目的,它主要提供了讀數據緩存,寫數據匯聚寫回的目的,用來提升順序讀寫的性能。須要說明的是,Ceph 既支持之內核模塊的方式來實現對 Linux 動態增長塊設備,也支持以 QEMU Block Driver 的形式給使用 QEMU 虛擬機增長虛擬塊設備,並且二者使用不一樣的庫,前者是內核模塊的形式,後者是普通的用戶態庫,本文討論的 RBDCache 針對後者,前者使用內核的 Page Cache 達到目的。
從這個棧能夠看出來,RBDCache 相似於磁盤的 write cache。它應該有三個功能:
所以,須要注意的是,理論上,RBDCache 對順序寫的效率提高應該很是有幫助,而對隨機寫的效率提高應該沒那麼大,其緣由應該是後者合併寫操做的效率沒前者高(也就是可以合併的寫操做的百分比比較少)。具體效果待測試。
在使用 QEMU 實現的 VM 來使用 RBD 塊設備,那麼 Linux Kernel 中的塊設備驅動是 virtio_blk,它會對塊設備各類請求封裝成一個消息經過 virtio 框架提供的隊列發送到 QEMU 的 IO 線程,QEMU 收到請求後會轉給相應的 QEMU Block Driver 來完成請求。當 QEMU Block Driver 是 RBD 時,緩存就會交給 Librbd 自身去維護,也就是一直所說的 RBDCache;用戶在使用本地文件或者 Host 提供的 LVM 分區時,跟 RBDCache 一樣性質的緩存包括了 Guest Cache 和 Host Page Cache,見本文第一部分的描述。
在 ceph.conf 中,設置 rbd cache = true 便可以啓用 RBDCache。它有如下幾個主要的配置參數:
配置項 | 含義 | 默認值 |
rbd cache | 是否啓用 RBDCache | 0.87 版本開始:true,啓用 0.87 版本以前:false,禁用 |
rbd_cache_size | Librbd 能使用的最大緩存大小 | 32 MiB |
rbd_cache_max_dirty | 緩存中容許髒數據的最大值,用來控制回寫大小,不能超過 rbd_cache_size。超過的話,應用的寫入應該會被阻塞, 這時候IOPS就會降低 |
24 MiB |
rbd_cache_target_dirty | 開始執行回寫過程的髒數據大小,不能超過 rbd_cache_max_dirty | 16MiB |
rbd_cache_max_dirty_age | 緩存中單個髒數據最大的存在時間,避免可能的髒數據由於遲遲未達到開始回寫的要求而長時間存在 | 1 秒 |
可見,默認狀況下:
也能看出,RBDCache 從空間和時間來方面,在效率和數據有效性之間作平衡。
幾個重要的注意事項:
(1)QEMU 和 ceph 配置項的相互覆蓋問題
http://ceph.com/docs/master/rbd/qemu-rbd/#qemu-cache-options
優先級:QEMU 命令行中的配置 > Ceph 文件中的顯式配置 > QEMU 配置 > Ceph 默認配置
(2)在啓用 RBDCache 時,必須在 QEMU 中配置 」cache=writeback」,不然可能會致使數據丟失。在使用文件系統的狀況下,這可能會致使文件系統損壞。
Important
If you set rbd_cache=true, you must set cache=writeback or risk data loss. Without cache=writeback, QEMU will not send flush requests to librbd. If QEMU exits uncleanly in this configuration, filesystems on top of rbd can be corrupted.
http://ceph.com/docs/master/rbd/qemu-rbd/#running-qemu-with-rbd
(3)使用 raw 格式的 Ceph 卷設備 「 <driver name='qemu' type='raw' cache='writeback'/>「
http://ceph.com/docs/master/rbd/qemu-rbd/#creating-images-with-qemu
理論上,你可使用其餘 QEMU 支持的格式好比 qcow2 或者 vmdk,可是它們會帶來 overhead
The raw data format is really the only sensible format option to use with RBD. Technically, you could use other QEMU-supported formats (such as qcow2 or vmdk), but doing so would add additional overhead, and would also render the volume unsafe for virtual machine live migration when caching (see below) is enabled.
(4)在新版本的 Ceph 中(未來的版本,尚不知版本號),Ceph 配置項 rbd cache 將會被刪除,RBDCache 是否開啓將由 QEMU 配置項決定。
也就是說,若是 QEMU 中設置 cache 爲 ‘none’ 的話, RBDCache 將不會被使用;設置爲 ‘writeback’ 的話,RBDCache 將會被啓用。參考連接:ceph : [client] rbd cache = true override qemu cache=none|writeback。
(5)對 Nova 來講,不設置 disk_cachemode 值的話,默認的 driver 的 cache 模式是 ‘none’。可是,在不支持 ‘none’ 模式的存儲系統上,會改成使用 ‘writethrough’ 模式。(來源)
def disk_cachemode(self): if self._disk_cachemode is None: # We prefer 'none' for consistent performance, host crash # safety & migration correctness by avoiding host page cache. # Some filesystems (eg GlusterFS via FUSE) don't support # O_DIRECT though. For those we fallback to 'writethrough' # which gives host crash safety, and is safe for migration # provided the filesystem is cache coherant (cluster filesystems # typically are, but things like NFS are not). self._disk_cachemode = "none" if not self._supports_direct_io(FLAGS.instances_path): self._disk_cachemode = "writethrough" return self._disk_cachemode
有兩種類型的 flush:
關於第二種 flush,這裏的一個問題是,何時會有這種主動 flush 指定發出。有文章說,」QEMU 做爲最終使用 Librbd 中 RBDCache 的用戶,它在 VM 關閉、QEMU 支持的熱遷移操做或者 RBD 塊設備卸載時都會調用 QEMU Block Driver 的 Flush 接口「。同時,一些對數據的安全性敏感的應用也能夠經過操做系統在須要的時候發出 flush 指定,好比一些數據庫系統。你可使用 fio 工具的 fdatasync 參數在指定的寫入操做後發出 fdatasync 指令。具體效果還待測試。
librados 的 flush API:
CEPH_RADOS_API int rados_aio_flush(rados_ioctx_t io) Block until all pending writes in an io context are safe This is not equivalent to calling rados_aio_wait_for_safe() on all write completions, since this waits for the associated callbacks to complete as well. Parameters io - the context to flush
下面是一個 Linux 系統上文件操做的僞代碼(來源)。可見,該程序知道只有在 fdatasync 執行成功後,數據纔算寫入成功。
#include "stdlib.h" /* for exit */ #include "unistd.h" /* for write fdatasync*/ #include "fcntl.h" /* for open */ int main(void){ int fd; if((fd=open("/home/zzx/test.file",O_WRONLY|O_APPEND|O_DSYNC))<0){ exit(1); } char buff[]="abcdef"; if(write(fd,buff,6)!= 6){ exit(2); } if(fdatasync(fd)==-1){ exit(3); } exit(0); }
由於 RBDCache 是利用內存來緩存數據,所以數據也是易失性的。那麼,最安全的是,設置 rbd_cache_max_dirty = 0,就是不緩存數據,至關於 writethrough 的效果。很明顯,這沒有實現 RBDCache 的目的。
另外,Ceph 還提供 rbd_cache_writethrough_until_flush 選項,它使得 RBDCache 在收到第一個 flush 指令以前,使用 writethrough 模式,透傳數據,避免數據丟失;在收到第一個 flush 指令後,開始 writeback 模式,經過 KVM barrier 功能來保證數據的可靠性。
該選項的含義:
This option enables the cache for reads but does writethrough until we observe a FLUSH command come through, which implies that the guest OS is issuing barriers. This doesn't guarantee they are doing it properly, of course, but it means they are at least trying. Once we see a flush, we infer that writeback is safe.
該選項的默認值究竟是 true 仍是 false 比較坑爹:
所以,你在使用不一樣版本的 librbd 狀況下使用默認配置時,其 IOPS 性能是有很大的區別的:
在實現上,
(1)收到第一個flush 以前,至關於 rbd_cache_max_dirty 被設置爲0 了:
uint64_t init_max_dirty = cct->_conf->rbd_cache_max_dirty; if (cct->_conf->rbd_cache_writethrough_until_flush) init_max_dirty = 0;
(2)收到第一個 flush 以後,就轉爲 writeback 了
if (object_cacher && cct->_conf->rbd_cache_writethrough_until_flush) { md_lock.get_read(); bool flushed_before = flush_encountered; md_lock.put_read(); uint64_t max_dirty = cct->_conf->rbd_cache_max_dirty;
各類配置下的Ceph RBD 緩存效果:
配置 | rbd_cache_writethrough_until_flush 的值 | 緩存效果 |
rbd cache = false | N/A | 沒有讀寫緩存,等同於 directsync |
rbd cache = true rbd_cache_max_dirty = 0 |
N/A | 只有讀緩存,沒有寫緩存,等同於 writethrough |
rbd cache = true rbd_cache_max_dirty > 0 「cache=writeback」 |
True | 在收到 QEMU 發出的第一個 flush 前, 使用 writethrough 模式;收到後,使用 writeback 模式 |
rbd cache = true rbd_cache_max_dirty > 0 「cache=writeback」 |
False | 一直使用 writeback 模式,QEMU 會在特定時候發出 flush,可能會致使數據丟失 |
rbd cache = true rbd_cache_max_dirty > 0 「cache=none」 |
True | 一直使用 writethrough 模式,沒有寫緩存,只有讀緩存 |
rbd cache = true rbd_cache_max_dirty > 0 「cache=writeback」 |
False | 一直使用 writeback 模式,QEMU 會發出 flush 使緩存數據寫入Ceph 集羣 |
備註:主要內容引用自 2016/05/27 發表於 Linux內核之旅 微信公衆號的 系統和進程信息與文件IO緩衝 一文。
忽略文件打開的過程,一般咱們會說「寫文件」有兩個階段,一個是調用 write 咱們稱爲寫數據階段(實際上是受open的參數影響),調用 fsync(或者fdatasync)咱們稱爲flush階段。Linux上的塊設備的操做能夠分爲兩類:出於速度和效率的考慮。系統 I/O 調用(即內核)和標準 C語言庫 I/O 函數,在操做磁盤文件時會對數據進行緩衝。
使用 C 標準庫中的 fopen/fread/fwrite 系列的函數,咱們能夠稱其爲 buffered I/O。具體的I/O path以下:
Application <-> Library Buffer <-> Operation System Cache <-> File System/Volume Manager <-> Device
其中,library buffer 是標準庫提供的用戶空間的 buffer,能夠經過 setvbuf 改變其大小。
設置方法以下:
(1)經過 setvbuf 函數,結合不一樣的mode,能夠設置緩衝的類型,和大小:
(2)咱們也能夠經過fflush
來刷新緩衝區。
使用 Linux 的系統調用的 open/read/write 系列的函數,咱們能夠稱其爲 non-buffered I/O。其I/O 路徑爲:
Application<-> Operation System Cache <-> File System/Volume Manager <->Device
OS Cache 即內核緩衝的緩衝區,它是在內核態的,沒辦法經過像 stdio 庫的 setvbuf 那樣給它指定一個用戶態的緩衝區,只能控制緩衝區的刷新策略等。
fsync
用於將描述符 fd 相關的全部元數據都刷新到磁盤上,至關於強制使文件處於同步I/O文件完整性。 fdatasync 的
做用相似於 fsync,只是強制文件處於數據完整性,確保數據已經傳遞到磁盤上。sync
僅在全部數據(數據塊,元數據)已傳遞到磁盤上時才返回。這些函數都只能刷新一次緩衝,此後每次發生的I/O操做都須要再次調用上面的三個系統調用來刷新緩衝,爲此能夠經過設置描述符的屬性來保證每次IO的刷新緩衝。
O_SYNC
,至關於每次發生 IO 操做後都調用 fsync
和 fdatasync
系統調用。[寫操做只有在數據和元數據好比修改時間等被寫入磁盤後才返回]O_DSYNC
標誌則要求寫操做是同步 I/O 數據完整性的也就是至關於調用 fdatasync。[寫操做必須等到數據被寫入磁盤後才返回,此時元數據能夠沒有被寫入磁盤,好比文件的 inode 信息還沒有被寫入時]
O_RSYNC
標誌須要結合 O_DSYNC
和 O_SYNC
標誌一塊兒使用。將這些標誌的寫操做做用結合到讀操做中,也就說在讀操做以前,會先按照O_DSYNC
或O_SYNC
對寫操做的要求,完成全部待處理的寫操做後纔開始讀。
更詳細信息,能夠參考 http://man7.org/linux/man-pages/man2/open.2.html。
Linux 容許應用程序在執行磁盤 I/O 時繞過緩衝區高速緩存(Host OS Page Cache),從用戶空間直接將數據傳遞到文件或磁盤設備上(實際上是 Disk cache 中),這咱們稱之爲直接 I/O,或者裸 I/O。直接 I/O 只適用於特定 I/O 需求的應用,例如:數據庫系統,其高速緩存和I/O優化機制自成一體,無需內核消耗CPU時間和內存去完成相同任務。經過在打開文件的時候指定 O_DIRECT
標誌設置文件讀寫爲直接IO的方式。使用直接 IO 存在一個問題,就是I/O得對齊限制,由於直接 I/O 涉及對磁盤的直接訪問,因此在執行I/O時,必須遵照一些限制:
用於傳遞數據的緩衝區,其內存邊界必須對齊爲塊大小的整數倍。
數據傳輸的開始點,亦即文件和設備的偏移量,必須是塊大小的整數倍。
待傳遞數據的 buffer 必須是塊大小的整數倍。
對於 MySQL 來講,該數據庫系統是基於文件系統的,其性能和設備讀寫的機制有密切的關係。和數據庫性能密切相關的文件I/O操做的三個操做:
MySQL 的 innodb_flush_log_at_trx_commit 參數肯定日誌文件什麼時候 write 和 flush。innodb_flush_method 則肯定日誌及數據文件如何 write、flush。在Linux下,innodb_flush_method能夠取以下值:fdatasync, O_DSYNC, O_DIRECT,那這三個值分別是如何影響文件寫入的?
簡單的,InnoDB 在每次提交事務時,爲了保證數據已經持久化到磁盤(Durable),須要調用一次 fsync(或者是 fdatasync、或者使用 O_DIRECT 選項等)來告知文件系統將可能在緩存中的數據刷新到磁盤(更多關於fsync)。而 fsync 操做自己是很是「昂貴」的(關於「昂貴」:消耗較多的IO資源,響應較慢):傳統硬盤(10K轉/分鐘)大約每秒支撐150個fsync操做,SSD(Intel X25-M)大約每秒支撐1200個fsync操做。因此,若是每次事務提交都單獨作fsync操做,那麼這裏將是系統TPS的一個瓶頸。因此就有了Group Commit的概念。
當多個事務併發時,咱們讓多個都在等待 fsync 的事務一塊兒合併爲僅調用一次 fsync 操做。這樣的一個簡單的優化將大大提升系統的吞吐量,Yoshinori Matsunobu的實驗代表,這將帶來五到六倍的性能提高。
一個典型的存儲 I/O 路徑:
一些組件有本身的cache:
其中:
<做者注:做者對本文中的一些概念還有一些疑惑或者不解,所以,本文內容會持續更新>
參考資料: