硬體和引數
測驗機器
測驗機器是一臺建立在 KVM 的 Centos, 4C、8G,作為個人開發所用,測驗中只運行 MySQL,
top - 19:33:02 up 176 days, 2:47, 1 user, load average: 0.08, 0.03, 0.05
Tasks: 139 total, 1 running, 138 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.3 us, 1.3 sy, 0.0 ni, 98.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 0.3 us, 0.3 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 0.3 us, 0.3 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 0.3 us, 0.7 sy, 0.0 ni, 99.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 8010844 total, 140608 free, 2698796 used, 5171440 buff/cache
KiB Swap: 2097148 total, 2093812 free, 3336 used. 4563576 avail Mem
版本相關
| name | version |
|---|---|
| MariaDB | 10.0.13 |
| InnoDB | 5.6.19-67.0 |
| TokuDB | 7.1.7 |
MySQL 引數設定
| Variable_name | Value | des |
|---|---|---|
| innodb_buffer_pool_size | 5368709120 | 5 G |
| innodb_thread_concurrency | 20 | 并發數 |
| innodb_compression_level | 6 | 壓縮等級 |
| tokudb_block_size | 4194304 | |
| tokudb_cache_size | 4101552128 | |
| tokudb_read_buf_size | 131072 | |
| tokudb_row_format | tokudb_zlib | 壓縮等級 |
快取設定
為了避免快取造成的測驗查詢結果失真,資料庫關閉 Query_cache,
+------------------------------+-------+
| Variable_name | Value |
+------------------------------+-------+
| query_cache_type | OFF |
+------------------------------+-------+
結構和資料
模擬一張用戶激活表,共計1414656條,
TukuDB
CREATE TABLE `active_tokudb` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '',
`active_date` int(11) unsigned NOT NULL DEFAULT '19700101' COMMENT '',
`device_id` varchar(50) NOT NULL DEFAULT '',
`original_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '',
..... 省去部分欄位 .....
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '',
PRIMARY KEY (`id`),
UNIQUE KEY `device_id` (`device_id`,`original_id`) USING BTREE,
KEY `active_date` (`active_date`) USING BTREE
) ENGINE=TokuDB AUTO_INCREMENT=528991 DEFAULT CHARSET=utf8 COMMENT='' `compression`='tokudb_zlib';
InnoDB
CREATE TABLE `active_innodb` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '',
`active_date` int(11) unsigned NOT NULL DEFAULT '19700101' COMMENT '',
`device_id` varchar(50) NOT NULL DEFAULT '',
`original_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '',
..... 省去部分欄位 .....
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '',
PRIMARY KEY (`id`),
UNIQUE KEY `device_id` (`device_id`,`original_id`) USING BTREE,
KEY `active_date` (`active_date`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=528991 DEFAULT CHARSET=utf8 COMMENT='';
基準測驗
壓縮對比
兩者都設定為中等級壓縮
innodb 壓縮等級 innodb_compression_level 設定為 6
tokudb 壓縮等級 tokudb_row_format 設定為 tokudb_zlib
同數量級資料下,兩表的壓縮對比如下:
| TABLE_NAME | data_size | index_size |
|---|---|---|
| active_innodb | 263.75 MB | 0.00 MB |
| active_tokudb | 249.73 MB | 34.23 MB |
在百萬資料級下似乎 tokudb 的壓縮優勢并不是很明顯,
OnLine DDL
索引操作(Index Operations)
對于添加索引,添加/洗掉列、修改列NULL/NOT NULL屬性等操作,需要修改MySQL內部的資料記錄,對這類操作進行Online DDL操作時,需要重建表(rebuild),
相反,對于洗掉索引,修改列默認值,修改列名等操作不需要修改MySQL內部的資料記錄,只需要修改結構資料frm檔案,而不需要重建表(no-rebuild),
故在此僅僅測驗增加索引,
-
一般索引
ALTER TABLEtest.active_innodbADD INDEXactive_time(active_time) USING BTREE;
ALTER TABLEtest.active_tokedbADD INDEXactive_time(active_time) USING BTREE; -
唯一索引
ALTER TABLEtest.active_innodbADD UNIQUE INDEXdevice_id(device_id,original_id) USING BTREE;
ALTER TABLEtest.active_tokudbADD UNIQUE INDEXdevice_id(device_id,original_id) USING BTREE;
表現如下表:
| engine | 一般索引 | 唯一索引 |
|---|---|---|
| innodb | 7.18 s | 12.87 s |
| tokudb | 4.46 s | 20.46 s |
tokudb采用的分型樹索引ft-index采用更大的索引頁和資料頁(ft-index默認為4M, InnoDB默認為16K), 這使得ft-index的資料頁和索引頁的壓縮比更高,也就是說,在打開索引頁和資料頁壓縮的情況下,插入等量的資料, ft-index占用的存盤空間更少,
但tokudb采用壓縮索引的速度相對innodb還是會慢一些,
欄位操作(schema Operations)
TokuDB支持在輕微阻塞DML情況下,增加或洗掉表中的欄位或者擴展欄位長度,
執行在線增減欄位時表會鎖一小段時間,一般是秒級鎖表,鎖表時間短得益于fractal tree的實作,TokuDB會把這些操作放到后臺去做,具體實作是:往root塊推送一個廣播msg,通過逐層apply這個廣播msg實作增減欄位的操作,
但是也要注意一些點:
- 所有的這些操作不是立即執行, 而是放到后臺中由 Fractal Tree 完成, 操作包括主鍵和非主鍵索引,也可以手工強制執行這些操作, 使用 OPTIMIZE TABLE X 命令即可, TokuDB 從1.0 開始OPTIMIZE TABLE命令也支持在線完成, 但是不會重建索引,
- 不要一次更新多列, 分開對每列進行操作,
- 避免同時對一列進行 add, delete, expand 或 drop 操作,
- 表鎖的時間主要由快取中的臟頁(dirty page)決定, 臟頁越多 flush 的時間就越長. 每做一次更新, MySQL 都會關閉一次表的連接以釋放之前的資源,
- 避免洗掉的列是索引的一部分, 這類操作會特別慢, 非要洗掉的話可以去掉索引和該列的關聯再進行洗掉操作
擴充類的操作只支持 char, varchar, varbinary 和 int 型別的欄位, - 一次只 rename 一列, 操作多列會降級為標準的 MySQL 行為, 語法中列的屬性必須要指定上,如:ALTER TABLE table CHANGE column_old column_new DATA_TYPE REQUIRED_NESS DEFAULT
- rename 操作還不支持欄位: TIME, ENUM, BLOB, TINYBLOB, MEDIUMBLOB, LONGBLOB,
- 不支持更新臨時表,
InnoDB中不同的版本或不同的操作對online DDL有不同的實作方式,
早期實作方式(MySQL5.6.7之前版本)
-
COPY方式,這是InnoDB最早期支持的方式,主要實作步驟:
- 創建與原表結構定義一致的臨時表;
- 對原表加鎖,不允許執行DML,但允許查詢;
- 在臨時表上執行DDL陳述句;
- 逐行拷貝原表資料到臨時表;
- 原表與臨時表進行RENAME操作,此時會升級原表上的鎖,不允許讀寫,直至完成DDL操作;
-
INPLACE方式,是MySQL5.5及之后版本為了提高創建二級索引效率的方式,所以INPLACE方式僅限于二級索引的創建跟洗掉,主要實作步驟:
- 創建臨時的frm檔案;
- 對原表加鎖,不允許執行DML,但允許查詢;
- 根據聚集索引的順序,構造新的索引項,按照順序插入新索引頁;
- 升級原表上的鎖,不允許讀寫操作;
- 進行RENAME操作,替換原表的frm檔案,完成DDL操作,
相對于COPY方式,INPLACE方式在原表上進行,不會生成臨時表,也不會拷貝原表資料,減少了很多系統I/O資源占用,但還是無法進行DML操作,也只適用于索引的創建與洗掉,并不適用于其他型別的DDL陳述句,
當前實作方式(MySQL5.6.7及之后版本)
-
Online DDL方式,
Online DDL特性是基于MySQL5.5的InnoDB fast index creation上改進增強的,Online DDL同樣包含COPY方式、INPLACE方式,
其中,某些DDL陳述句不支持Online DDL的就采用COPY方式,支持Online DDL的則采用INPLACE方式,因為Online DDL是對早期INPLACE方式的增加,所以INPLACE方式根據是否涉及到記錄格式的修改又分為Rebuilds Table、No-Rebuilds Table兩種情形,
Rebuilds Table操作是因為DDL有涉及到行記錄格格式的修改,如欄位的增、刪、型別修改等,
No-Rebuilds Table則不涉及行記錄格式的修改,如索引洗掉、欄位名修改等,
基于上面了解到的內容開始增減欄位的測驗,
-
增加欄位
ALTER TABLE
test.active_tokudbADD COLUMNtest_fieldvarchar(50) NOT NULL DEFAULT ‘’ AFTERoaid;ALTER TABLE
test.active_innodbADD COLUMNtest_fieldvarchar(50) NOT NULL DEFAULT ‘’ AFTERoaid; -
洗掉欄位
ALTER TABLE
test.active_tokudbDROP COLUMNtest_field;ALTER TABLE
test.active_innodbDROP COLUMNtest_field;
表現如下:
| engine | add column | drop column |
|---|---|---|
| Tokudb | 0.70 s | 0.16 s |
| Innodb | 1 min 32.70 s | 1 min 4.28 s |
Tokudb因為 Fractal-tree 索引的特性, 將隨機的 IO 操作替換為順序 IO 操作,在欄位的變更上有著更好的體現,
DML
InnoDB 是以主鍵組織的B+Tree結構,資料按照主鍵順序排列,對于順序的自增主鍵有很好的性能,但是不適合隨機寫入,大量的隨機I/O會使資料頁分裂產生碎片,索引維護開銷很多大,
TokuDB 采用 Fractal Tree的索引結構,使其在隨機寫資料的處理上有很大提升,
TokuDB 解決隨機寫入的問題得益于其索引結構,Fractal Tree 和 B-Tree 的差別主要在于索引樹的內部節點上,B-Tree索引的內部結構只有指向父節點和子節點的指標,而 Fractal Tree 的內部節點不僅有指向父節點和子節點的指標,還有一塊 Buffer 區,
當資料寫入時會先落到這個 Buffer 區上,該區是一個 FIFO 結構,寫是一個順序的程序,和其他緩沖區一樣,滿了就一次性刷寫資料,所以 TokuDB上 插入資料基本上變成了一個順序添加的程序,
Query
-
Point-Query, where id = ? (其中id是索引)的查詢操作稱之為Point-Query,
select * from active_tikedb where active_time = ‘2021-01-10 14:00:20’;
select * from active_innodb where active_time = ‘2021-01-10 14:00:20’;采用分形樹索引的 TokuDB 會稍微慢一些,但是在百萬數量級并沒有啥差距,TokuDB 稍慢是需要加載Root節點,通過二分搜索確定Key落在Root節點的鍵值區間Range, 找到對應的Range的Child指標,
加載Child指標對應的的節點, 若該節點為非葉子節點,則繼續沿著分形樹一直往下查找,一直到葉子節點停止, 若當前節點為葉子節點,則停止查找,
查找到葉子節點后,我們并不能直接回傳葉子節點中的BasementNode的Value給用戶, 因為分形樹的插入操作是通過訊息(Message)的方式插入的, 此時需要把從Root節點到葉子節點這條路徑上的所有訊息依次apply到葉子節點的BasementNode, 待apply所有的訊息完成之后,查找BasementNode中的key對應的value,就是用戶需要查找的值,
-
Range-Query, where id >= ? and id <= ? (其中id是索引)的查詢操作稱之為Range-Query,
select * from active_tokudb where active_time >= ‘2021-01-10 14:00:20’ and active_time <= ‘2021-01-11 23:0:00’;
select * from active_innodb where active_time >= ‘2021-01-10 14:00:20’ and active_time <= ‘2021-01-11 23:0:00’;范圍查詢中 Tokudb 花費了1.23S, Innodb 花費了 0.56 s, 采用了分形樹索引的 TokuDB 在查詢上慢的特性就體現出來了,
分形樹的 Range-Query 基本等價于進行N次 Point-Query 操作,操作的代價也基本等價于N次 Point-Query 操作的代價, 由于分形樹在非葉子節點的 msg_buffer 中存放著 BasementNode 的更新操作,因此在查找每一個 Key 的 Value 時,都需要從根節點查找到葉子節點,然后將這條路徑上的訊息apply 到 basenmentNode 的 Value上,
在 B+ 樹中,由于底層的各個葉子節點都通過指標組織成一個雙向鏈表, 因此,只需要從跟節點到葉子節點定位到第一個滿足條件的Key, 然后不斷在葉子節點迭代 next 指標,即可獲取到 Range-Query 的所有 Key-Value鍵值,因此,對于 B+ 樹的 Range-Query 操作來說,除了第一次需要從 root 節點遍歷到葉子節點做隨機寫操作,后繼資料讀取基本可以看做是順序IO,
結論通過比較分形樹和B+樹的 Range-Query 實作可以可知,分形樹的 Range-Query 查詢代價明顯比B+ 樹代價高,因為分型樹需要遍歷 Root 節點的覆寫 Range 的整顆子樹,而 B+ 樹只需要一次 Seed 到 Range 的起始Key,后續迭代基本等價于順序IO,
Write (Insert/Delete/Update)
分形樹是一種寫優化(隨機寫)的資料結構, 它的寫操作性能要優于B+樹的寫操作性能,而在B+樹中,大量的這種隨機寫操作將導致LRU-Cache中大量的熱點資料頁落在B+樹的上層,這樣底層的葉子節點命中Cache的概率降低,從而造成大量的磁盤IO操作,導致B+樹的隨機寫性能瓶頸,但B+樹的順序寫操作很快,因為順序寫操作充分利用了區域熱點資料,磁盤IO次數大大降低,
分形樹插入操作的流程大致可分為以下幾點:
- 加載 Root 節點;
- 判斷 Root 節點是否需要分裂(或合并),如果滿足分裂(或者合并)條件,則分裂(或者合并)Root節點,
- 當 Root 節點 height > 0, 也就是 Root 是非葉子節點時,通過二分搜索找到Key所在的鍵值區間Range,將(Key, Value)包裝成一條訊息(Insert, Key, Value),放入到鍵值區間 Range 對應的 Child 指標的 Message Buffer中,
- 當 Root 節點 height = 0 時,即 Root 是葉子節點時,將訊息(Insert, Key, Value)應用(Apply) 到 BasementNode 上,也就是插入(Key, Value)到 BasementNode中,
在大量的插入(包括隨機和順序插入)情況下, Root 節點會經常性的被撐飽滿,這將會導致Root節點做大量的分裂操作,然后,Root 節點做了大量的分裂操作之后,產生大量的height=1的節點, 然后height=1的節點被撐爆滿之后,又會產生大量height=2的節點, 最終樹的高度越來越高,
但每一次插入操作都落在Root節點就馬上回傳了,每次寫操作并不需要搜索樹形結構最底層的 BasementNode, 這樣會導致大量的熱點資料集中落在在 Root 節點的上層,從而充分利用熱點資料的區域性,大大減少了磁盤IO操作,
對兩表批量插入900條資料,在速度上差距不大,沒有測出 TOkudb 的優勢,后面想想其他辦法再試試,
結論
-
高壓縮比 尤其是對字串(varchar,text等)型別有非常高的壓縮比,比較適合存盤日志、原始資料等,但是我測驗下來并不是很明顯…
-
online DDL HCADER 特性,支持在線欄位增加、洗掉、擴展、重命名操作,上面測驗看到相對 Innodb 有明顯的優勢,
-
非常快的寫入性能,哈哈哈,我沒測出來…
-
支持show processlist 進度查看,這點比較人性化,Innodb 需要借助性能模式實作,
-
沒有完善的熱備工具,只能通過mysqldump進行邏輯備份,
-
不支持外鍵(foreign key)功能,
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/263339.html
標籤:其他
