天天看點

MySQL核心月報 2014.08-MariaDB·分支特性·支援大于16K的InnoDB Page Size

<b>背景</b>

最近釋出的mariadb 10.1 alpha版本,送出了一個改動,放寬了innodb page&lt;=16k的限制,将上限提高到64k。 從mdev-6075需求文檔中可以看出,目前隻支援compact的結構,dynamic結構能否支援還在研究,compressed結構則确定無法支援。

<b>業務應用</b>

是以當我們的資料行本身就比較長,尤其是做大塊插入的時候,更大的頁面更有利于提升如速度,因為一個頁面可以放入更多的行,每個io寫下去的大小更大,就可以以更少的iops寫更多的資料。 而且,當行長超過8k的時候,如果是16k的頁面,就會強制轉換一些字元串類型為text,把字元串主體轉移到擴充頁中,會導緻讀取列需要多一個io,更大的頁面也就支援了更大的行長,64k頁面可以支援近似32k的行長而不用使用擴充頁。 但是,如果是短小行長的随機讀取和寫入,則不适合使用這麼大的頁面,這會導緻io效率下降,大io隻能讀取到小部分有效資料,得不償失。

随着儲存設備越來越快,innodb許多原有的設計不再适合新的高速硬體,是以mariadb 10.1 alpha版本針對fusionio pci-e ssd做出了專門的優化,充分利用了fio的硬體特性。 mdev-6246這個需求改造了mariadb,以利用fio的atomic writes和檔案系統壓縮特性。

為何fio會更快呢,因為傳統的儲存設備讀取,是左圖的方式,要經過raid控制器,來回的路徑就長了。而fio才有右圖的方式,裝置通過pci槽直接與cpu互動,大大縮短了路徑。

MySQL核心月報 2014.08-MariaDB·分支特性·支援大于16K的InnoDB Page Size

<b>atomic writes</b>

innodb一直存在一個叫做double write buffer的東西,目的就是為了防止頁面寫到一半系統崩潰,導緻頁面損壞,因為innodb的page是16k,而一般的機械硬碟扇區是512位元組,ssd大都是4k的塊大小,都不能保證16k的寫入是完整的。 而fio的nvmfs檔案系統則提供了原子寫的保證,隻要對檔案句柄增加dfs_ioctl_atomic_write_set的ioctl标記位,就可以啟用這個檔案的原子寫支援。

mariadb新增了一個參數來啟用這個特性,一旦開啟,所有檔案會用dfs_ioctl_atomic_write_set标記打開。

這樣一來double write buffer就沒有存在的價值了,因為不會出現部分寫,每個write下去都可以保證所寫内容全部完成,這可以相當程度上提升innodb的性能。

<b>page compression</b>

innodb标準的頁面大小是16k,innodb也提供1k、2k、4k、8k的壓縮頁面大小,通過key_block_size來設定壓縮大小,使用zlib标準庫來進行壓縮。 但是page是頻繁被更新的,如果每次修改都重新壓縮頁面,代價很高,innodb就采用了modification log來暫存部分修改資訊,而避免了頻繁解壓縮,待modification log存滿時,再重新對整個page做一次重構壓縮。 但是compressed page載入innodb buffer pool時,innodb隻能處理未壓縮的頁面,是以還要在記憶體中存一份解壓頁面,回寫到磁盤時再次壓縮。

總而言之,innodb的compressed page有這些缺點:

mariadb與fusionio合作利用nvmfs檔案系統的特性,修改innodb的page結構來支援檔案系統級的壓縮。 page compression要求innodb做了如下配置:

它的實作方法是,隻在page即将寫入到檔案系統時,才進行壓縮,是以最終隻有壓縮後的容量被寫入到磁盤,如果壓縮失敗,那麼就把沒有壓縮的容量寫入磁盤。另外還會對page内的512位元組的倍數的未使用空間清理掉,不占用實際存儲:

當頁面被讀取時,會在放入buffer pool之前進行解壓縮,将原始頁面載入記憶體。是以需要在檔案頭中加入一個新的page type:fil_page_page_compressed 

MySQL核心月報 2014.08-MariaDB·分支特性·支援大于16K的InnoDB Page Size

綜合起來可以這樣定義一張表:

意思是将t3表存到/dev/fioa盤,開啟page compression,采用4級壓縮,開啟原子寫。

經過測試,可以看出,lz4的壓縮比例最好,而且,對性能影響非常小。

MySQL核心月報 2014.08-MariaDB·分支特性·支援大于16K的InnoDB Page Size
MySQL核心月報 2014.08-MariaDB·分支特性·支援大于16K的InnoDB Page Size

tokuft是個支援事務的key/value存儲層,tokudb-engine是mysql api對接層,調用關系為:tokudb-engine -&gt;tokuft。

tokuft裡的一個value,在tokudb-engine裡就是一條row資料,底層存儲與上層調用解耦,是個很棒的設計。

在tokuft是個key裡,索引的每個node都是大塊頭(4mb),node又細分為多個"小塊"(internal node的叫做partition,leaf node的叫做basement)。

從磁盤讀取資料到記憶體的方式有2種:

僅讀一個"小塊"的資料,反序列化到記憶體(提升point query性能,隻讀取需要的那部分資料即可)

讀取整個node資料,反序列化到記憶體(提升區間性能,一次讀取整個node磁盤資料)

對于tokudb-engine層的區間操作(比如get_next等),tokuft這層是無狀态的,必須告訴目前的key,然後給你查找next,流程大體是:

這樣,即使tokuft緩存了整個node資料,tokudb-engine還是周遊着跟tokuft要一遍:tokuft每次都要根據目前key,多次調用compare操作最終查出next,路徑太長了!

有什麼辦法優化呢?這就是bulk fetch的威力: tokudb-engine向tokuft一次要回整個node的資料,自己解析出next row資料,tokuft的調用就省了:

從tokutek的測試看,在使用bulk fetch後,能有2x-5x的性能提升。

但并不是所有的區間操作都可以bulk fetch的(比如涉及update/delete),tokudb目前實作了:select、create_table、insert_select和replace_select的bulk fetch功能,預計釋出在7.1.8版,更多bulk fetch介紹:

<a href="https://github.com/tokutek/tokudb-engine/wiki/bulk-fetch">https://github.com/tokutek/tokudb-engine/wiki/bulk-fetch</a>