作者介紹:吳雙橋 騰訊雲資料庫工程師
近年來,TokuDB 作為 MySQL 的大資料( Big Data )存儲引擎受到人們的普遍關注。其架構的核心基于一種新的叫做分形樹( Fractal Trees )的索引資料結構,該結構是緩存無關的,即使索引資料大小超過記憶體性能也不會下降,也即沒有記憶體生命周期和碎片的問題。
特别引人注意的是,TokuDB 擁有很高的壓縮比(官方稱最大可達25倍),可以在很大的資料上建立大量的索引,并保持性能不下降。同時,TokuDB 支援 ACID 和 MVCC,還有線上修改表結構(Live Schema Modification)以及增加的複制性能等特性,使其在某些特定的應用領域(如日志存儲與分析)有着獨特的優勢。
在 TokuDB 的應用場景中,通常是資料庫插入操作的量遠遠大于讀取的量,因而本次測試主要針對 TokuDB 的插入性能以及壓縮比,以 InnoDB 作為參考基準。
測試使用的機器為高配機型,記憶體大于100G,CPU 型号為 Intel(R) Xeon(R) CPU E5 系列,資料盤使用的是 SSD 硬碟。
MySQL TokuDB 版本使用的是 5.6.28-76.1,按照 Percona 網站上的安裝方法使用插件的方式進行安裝,見官網教程。使用MySQL指令檢視:
可以看到 TokuDB 引擎已經就緒,并被設定為了預設的存儲引擎。
現在開源可用的 MySQL 基準測試工具有很多,如mysqlslap,MySQL Benchmark Suite,Super Smack,Database Test Suite,TPCC 和 sysbench 等。綜合工具的功能、易用性還有流行程度,最終標明操作簡單但功能強大的 sysbench 作為測試工具。在 sysbench 0.5版本中,已經開始支援Lua腳本,使用修改起來非常靈活。另外,測試壓縮比直接使用的 mysqldump 工具。
除去根據機器硬體特性配置的正常優化參數,對于存儲引擎插入性能影響最大的是:是否将事務和binlog同步重新整理到硬碟。
需要特别說明的是,還有一個比較重要的名額,即是否開啟了 DIRECT IO 功能,由于在測試環境 InnoDB 開啟了 DIRECT IO ,并能提高整體的性能,而 TokuDB 在測試機型上無法開啟 DIRECT IO 功能,是以在這點上 InnoDB 有相對的優勢。
對于 InnoDB 來說,控制這個功能的參數為<code>innodb_flush_log_at_trx_commit</code>和<code>sync_binlog</code>。
<code>innodb_flush_log_at_trx_commit</code>參數指定了InnoDB在事務送出後的日志寫入頻率。具體來說:
當<code>innodb_flush_log_at_trx_commit</code>取值為 0 時,log buffer 會 每秒寫入到日志檔案并刷寫(flush)到硬碟。但每次事務送出不會有任何影響,也就是 log buffer 的刷寫操作和事務送出操作沒有關系。在這種情況下,MySQL性能最好,但如果 mysqld 程序崩潰,通常會導緻最後 1s 的日志丢失。
當取值為 1 時,每次事務送出時,log buffer 會被寫入到日志檔案并刷寫到硬碟。這也是預設值。這是最安全的配置,但由于每次事務都需要進行硬碟I/O,是以也最慢。
當取值為 2 時,每次事務送出會寫入日志檔案,但并不會立即刷寫到硬碟,日志檔案會每秒刷寫一次到硬碟。這時如果 mysqld 程序崩潰,由于日志已經寫入到系統緩存,是以并不會丢失資料;在作業系統崩潰的情況下,通常會導緻最後 1s 的日志丢失。
在實際的生産系統中,<code>innodb_flush_log_at_trx_commit</code>會在1和2之間選擇。一般來說,對資料一緻性和完整性要求比較高的應用場景,會将其值設定為1。
<code>sync_binlog</code>參數指定了 MySQL 的二進制日志同步到硬碟的頻率。如果MySQL autocommit開啟,那麼每個語句都寫一次binlog,否則每次事務寫一次。
預設值為 0,不主動同步binlog的寫入,而依賴于作業系統本身不定期把檔案内容flush到硬碟。
設定為 1 時,在每個語句或者事務後會同步一次binlog,即使發生意外崩潰時也最多丢失一個事務的日志,因而速度較慢。
通常情況下,<code>innodb_flush_log_at_trx_commit</code>和<code>sync_binlog</code>配合起來使用,本次性能測試覆寫了同步重新整理日志和異步重新整理日志兩種政策。
TokuDB中與InnoDB類似的名額為<code>tokudb_commit_sync</code>和<code>tokudb_fsync_log_period</code>。與<code>innodb_flush_log_at_trx_commit</code>的含義類似,<code>tokudb_commit_sync</code>指定當事務送出的時候,是否要重新整理日志到硬碟上。
預設開啟,值為1。也就是每次事務送出時,log buffer 會被寫入到日志檔案并刷寫到硬碟。
如果設定為 0,每次事務送出會寫入日志檔案,但并不會立即刷寫到硬碟,日志檔案會每隔一段時間刷寫一次到硬碟。這個時間間隔由<code>tokudb_commit_sync</code>指定。
<code>tokudb_fsync_log_period</code>指定多久将日志檔案重新整理到硬碟,TukuDB的log buffer總大小為 32 MB且不可更改。預設為 0 秒,此時如果<code>tokudb_commit_sync</code>設定為開啟,那麼這個值預設為 1 分鐘。
<code>tokudb_commit_sync</code>和<code>tokudb_fsync_log_period</code>通常也是配合起來使用,本次性能測試覆寫了兩種典型的組合政策。
大多數選擇使用TokuDB的場景,都非常重視它的存儲壓縮比,因而在實際應用場景中總會配套使用某種壓縮算法。目前TokuDB支援的壓縮算法有,quicklz, zlib, lzma, snappy,當然還有不壓縮uncompressed。關于壓縮算法的讨論,可以參考官方部落格的一篇分析文章。
本次測試會結合真實的資料來從壓縮比、耗時兩個方面來檢驗上面幾個不同的壓縮算法。TokuDB可以通過在配置檔案中設定<code>tokudb_row_format</code>來指定不同的壓縮算法,它們的取值分别是:<code>TOKUDB_QUICKLZ</code>, <code>TOKUDB_ZLIB</code>, <code>TOKUDB_LZMA</code>和<code>TOKUDB_SNAPPY</code>。值得注意的是,zlib算法是TokuDB官方最新版本的預設算法,也是現今支援TokuDB的雲服務商的預設推薦算法。
sysbench針對mysql壓測的參數有很多,這裡選取的是與實際應用場景最為相關的兩個參數:表數量以及線程數量。表數量對應的是資料庫實際在同時寫入的表的數量,線程數對應的到MySQL資料庫上的連接配接。其他的參數,如表的大小,是否是事務等可能影響整體的插入性能,但影響并不顯著,這裡隻選取最主要的兩個參數進行分析。
本測試的采用的方式為經典的控制變量法。這裡的變量有:采用的存儲引擎類型,是否同步重新整理日志,采用的壓縮算法,以及另外兩個與sysbench相關的參數:壓測的線程數量和壓測的表數量。其中,壓縮算法的選擇隻是在四種算法中選擇一種,是以并不與其他變量交叉測試。這樣以存儲引擎和同步重新整理日志來劃分測試,可以将整個測試資料分為四個大類:
InnoDB & 同步重新整理日志;
InnoDB & 異步重新整理日志;
TokuDB & 同步重新整理日志;
TokuDB & 異步重新整理日志。
在每個大類下,對壓測線程數量和壓測表數量進行交叉測試。壓測表數量取值可能為[1, 2, 4, 8, 12, 16],線程數的可能取值為[1, 2, 4, 8, 16, 24, 32, 48, 64, 80],因而每個大類下進行6 * 10 = 60 次壓測,每次壓測寫入2,000,000 條資料,對每次壓測進行插入性能統計。
将<code>innodb_flush_log_at_trx_commit</code>設定為1,<code>sync_binlog</code>設定為1,也即是保證資料最安全:同步重新整理log到硬碟,并且針對每個事務同步一次binlog。測試的情況見下表:
表數/插入TPS/線程數
1
2
4
8
12
16
5645.54
5678.40
5711.15
5687.44
5660.76
5708.54
10995.42
10921.06
11023.25
11017.88
11038.06
10902.12
18732.14
18724.20
18882.65
18935.62
18890.46
17997.52
33291.35
33379.37
33372.19
33842.15
30576.58
34048.53
54560.36
56346.20
57358.33
57454.11
57571.72
57989.96
24
66230.44
70813.87
73871.31
68764.22
68019.27
67538.82
32
66855.54
80153.06
84885.82
84387.96
76404.04
84920.13
48
56788.96
85778.22
93914.45
97055.96
84041.31
96790.49
64
55640.96
83342.88
95736.42
92667.25
96560.51
83598.00
80
58408.18
75858.18
60683.99
60491.26
60177.24
64290.13
用更直覺的圖表來展示:

可以看到:
線上程數比較少的時候(不高于24個,即總CPU數目的一半),資料表的個數對整體的性能影響很小;當線程數較多時才顯示出差別:相同線程數下,增加表數目可提升資料庫整體吞吐量;
InnoDB整體性能在48線程時達到頂峰,也即達到CPU的總數目,說明InnoDB能充分利用硬體多CPU的特性;
線上程數或者表數量很小的時候,增加線程數或者表數量可以線性地提升性能,在實際環境中值得注意;而線上程數量超過實體CPU數量時,整體插入性能會下降。
将<code>innodb_flush_log_at_trx_commit</code>設定為2,<code>sync_binlog</code>設定為 0,日志檔案會每秒刷寫一次到硬碟,并且不主動同步binlog的寫入,而依賴于作業系統本身不定期把檔案内容flush到硬碟。測試的情況見下表:
7751.83
7776.78
7741.86
7739.29
7770.41
7662.18
15199.98
14952.94
15252.04
15184.54
15186.76
15176.68
29075.83
29194.62
29228.09
29204.63
29625.60
29406.31
53578.10
51007.42
53116.44
54029.60
53291.41
52173.69
61002.65
71383.45
74656.36
75597.66
76305.24
76412.77
52758.54
70906.04
78472.49
81999.99
80430.52
82896.78
51740.38
68061.25
79936.12
82063.79
84966.86
83667.26
50961.62
65962.79
79952.45
85511.97
86223.38
86718.83
53378.76
65758.29
74224.26
76779.63
75368.30
76614.14
55056.88
66799.11
73969.11
62867.60
62039.68
63572.61
可以看到與<code>InnoDB Sync</code>的情況有所不同:
異步的情況,随着線程數的增加,插入性能提升較快,在16個線程的時候已經接近峰值;
插入TPS的峰值比同步情況的峰值低,這個與以往SATA/SAS磁盤環境下的MySQL優化經驗不比對;通過iostat檢視SSD盤的使用率很低,隻有百分之幾,因而SSD硬碟條件下的InnoDB的優化政策需要持續改進。
将<code>tokudb_commit_sync</code>設定為1,<code>tokudb_fsync_log_period</code>設定為0,也就是每次事務送出時,log buffer 會被寫入到日志檔案并刷寫到硬碟。
6506.16
6533.51
6514.36
6505.52
6581.28
6588.60
12335.72
12323.07
12494.74
12572.37
12600.94
12623.45
22726.24
23001.47
23331.96
23854.47
24267.20
24060.65
34935.53
38952.86
40093.37
41687.61
42472.60
44021.98
30777.25
42145.63
50293.47
55585.24
58956.65
59804.16
28102.26
38306.27
44420.44
47960.77
50440.59
52436.36
26227.49
35677.10
39627.22
40717.32
42562.45
44657.12
23335.33
30574.98
34961.22
34289.79
34819.25
35367.41
28923.69
36267.50
35228.70
35306.85
30393.76
29644.79
28323.74
33808.39
34203.47
35249.88
27757.45
32269.39
用圖表來展示:
出乎意料的是,TokuDB線上程數16的時候插入TPS達到峰值,也就是說TokuDB并沒有完全利用起多實體CPU的優勢。
對比InnoDB線程數從1~16的情況可以看到,TokuDB在相同條件下比InnoDB的性能還要高;而線上程數增多的時候,TokuDB的插入TPS在逐漸減小,而InnoDB線上程數超過實體CPU個數的時,插入TPS才開始下降,說明TokuDB還有很大的優化空間。
線程數/插入TPS/表數
6478.74
6514.03
6571.31
6555.92
6462.55
6588.75
12388.24
12433.75
12417.53
12457.83
12629.21
12597.90
22915.61
22265.08
24002.63
24248.57
22596.18
24323.07
35078.08
39179.46
39583.82
39505.79
42549.19
43493.89
30017.06
42012.43
46019.37
58790.76
60294.11
61057.96
27675.70
37873.94
45151.54
45075.48
49145.73
52417.81
26078.90
35496.19
39891.16
38888.13
43570.03
44882.92
23340.20
32392.89
35164.66
33858.16
34209.27
35350.72
29522.80
36475.76
36969.68
32381.53
32160.18
32589.76
27976.86
33483.40
34994.01
34977.62
30259.96
32680.34
對比之前的圖示,可以看到:
與InnoDB不同的是,是否開啟log的同步對TokuDB的插入性能影響不大,<code>TokuDB Sync</code>和<code>TokuDB Async</code>兩者的圖形形狀幾乎一樣。
與同步的情況類似,TokuDB線上程數16的時候插入TPS就達到峰值,有很大的優化空間。
壓縮算法測試使用的實際的運作資料做測試,原來用InnoDB存儲的日志資料為92GB,用mysqldump工具導出後為79GB。測試表是典型的日志存儲表,其結構如下:
依次将TokuDB的<code>tokudb_row_format</code>設定為不同的壓縮算法,得到其導入後的實際存儲空間以及導入時間,測試結果如下:
壓縮算法/項目
存儲大小
導入時間
snappy
12GB
47min40s
quicklz
7.1GB
47min21s
zlib
5.7GB
48min9s
lzma
4.7GB
47min10s
從表中可以觀察到:
幾種壓縮算法耗時差不多,相差很小;
不同的壓縮算法的壓縮比差異較大,snappy壓縮比較小,約為6.6倍;壓縮比最大的lzma為16.8倍;
zlib作為官方選擇的預設壓縮算法,在壓縮比和CPU消耗上有較好的平衡,壓縮比為13.8倍。
結合在測試過程中持續觀察CPU的使用情況,lzma算法在運作過程中CPU使用率在600%~700%左右,而zlib算法CPU使用率在80%~180%之間擺動。因而,在實際生産環境中,如果沒有特殊的考慮,建議使用zlib壓縮算法。
本次測試以InnoDB為參考,主要測試TokuDB的寫入性能以及存儲壓縮比。通過不同場景下的對比測試,可以得出幾個觀點:
InnoDB現階段插入性能有優勢,性能大約高出30%左右;
TokuDB雖然沒有充分利用硬體的能力,但是已經表現出強大的足夠高的性能,考慮到TokuDB的成熟度,後面它還有較大的提升空間,可以持續關注其後續進展;
TokuDB選擇日志同步或者異步重新整理對性能影響不大,建議預設選擇同步日志保護資料;
TokuDB在資料壓縮存儲上有絕對的優勢,十幾倍的壓縮比對于冷備資料存儲有着極大的吸引力。
值得一提的是,InnoDB性能表現優異部分原因可歸功于InnoDB的成熟度,可靈活的配置許多參數以适應特定的應用場景,而TokuDB暴露出的優化參數很少,不能根據硬體配置調整一些重要參數。綜上,雖然TokuDB在現階段還沒成熟,但已經表現出強大的性能以及突出的特性,應該作為某些特定應用場景的首選。