TokuDB · 引擎特性 · HybridDB for MySQL高壓縮引擎TokuDB 揭祕
原文出處:阿里雲RDS-資料庫核心組
HybridDB for MySQL(原名petadata)是面向線上事務(OLTP)和線上分析(OLAP)混合場景的關係型資料庫。HybridDB採用一份資料儲存來進行OLTP和OLAP處理,解決了以往需要把一份資料多次複製來分別進行業務交易和資料分析的問題,極大地降低了資料儲存的成本,縮短了資料分析的延遲,使得實時分析決策稱為可能。
HybridDB for MySQL相容MySQL的語法及函式,並且增加了對Oracle常用分析函式的支援,100%完全相容TPC-H和TPC-DS測試標準,從而降低了使用者的開發、遷移和維護成本。
TokuDB是TokuTek公司(已被 Percona收購)研發的新引擎,支援事務/MVCC,有著出色的資料壓縮功能,支援非同步寫入資料功能。
TokuDB索引結構採用fractal tree資料結構,是buffer tree的變種,寫入效能優異,適合寫多讀少的場景。除此之外,TokuDB還支援線上加減欄位,線上建立索引,鎖表時間很短。
Percona Server和Mariadb支援TokuDB作為大資料場景下的引擎,目前官方MySQL還不支援TokuDB。ApsaraDB for MySQL從2015年4月開始支援TokuDB,在大資料或者高併發寫入場景下推薦使用。
TokuDB優勢
資料壓縮
TokuDB最顯著的優勢就是資料壓縮,支援多種壓縮演演算法,使用者可按照實際的資源消耗修改壓縮演演算法,生產環境下推薦使用zstd,實測的壓縮比是4:1。
目前HybridDB for MySQL支援6中壓縮演演算法:
- lzma: 壓縮比最高,資源消耗高
- zlib:Percona預設壓縮演演算法,最流行,壓縮比和資源消耗適中
- quicklz:速度快,壓縮比最低
- snappy:google研發的,壓縮比較低,速度快
- zstd:壓縮比接近zlib,速度快
- uncompressed:不壓縮,速度最快
Percona建議6核以下場景使用預設壓縮演演算法zlib,6核以上可以使用壓縮率更高的壓縮演演算法,大資料場景下推薦使用zstd壓縮演演算法,壓縮比高,壓縮和解壓速度快,也比較穩定。
使用者可以在建表時使用ROW_FORMAT子句指定壓縮演演算法,也可用使用ALTER TABLE修改壓縮演演算法。ALTER TABLE執行後新資料使用新的壓縮演演算法,老資料仍是老的壓縮格式。
mysql> CREATE TABLE t_test (column_a INT NOT NULL PRIMARY KEY,column_b INT NOT NULL) ENGINE=TokuDB ROW_FORMAT=tokudb_zstd;
mysql> SHOW CREATE TABLE t_test\G
Table: t_test
Create Table: CREATE TABLE `t_test` (
`column_a` int(11) NOT NULL,`column_b` int(11) NOT NULL,PRIMARY KEY (`column_a`)
) ENGINE=TokuDB DEFAULT CHARSET=latin1 ROW_FORMAT=TOKUDB_ZSTD
mysql> ALTER TABLE t_test ROW_FORMAT=tokudb_snappy;
mysql> SHOW CREATE TABLE t_test\G
Table: t_test
Create Table: CREATE TABLE `t_test` (
`column_a` int(11) NOT NULL,PRIMARY KEY (`column_a`)
) ENGINE=TokuDB DEFAULT CHARSET=latin1 ROW_FORMAT=TOKUDB_SNAPPY複製程式碼
TokuDB採用塊級壓縮,每個塊大小是4M,這是壓縮前的大小;假設壓縮比是4:1,壓縮後大小是1M左右。比較tricky地方是:TokuDB壓縮單位是partition,大小是64K。相比innodb16K的塊大小來說要大不少,更有利壓縮演演算法尋找重複串。
上面提到,修改壓縮演演算法後新老壓縮格式的資料可以同時存在。如何識別呢?
每個資料塊在壓縮資料前預留一個位元組儲存壓縮演演算法。從磁碟讀資料後,會根據那個位元組的內容呼叫相應的解壓縮演演算法。
另外,TokuDB還支援並行壓縮,資料塊包含的多個partition可以利用執行緒池並行進行壓縮和序列化工作,極大加速了資料寫盤速度,這個功能在資料批量匯入(import)情況下開啟。
線上增減欄位
TokuDB還支援在輕微阻塞DML情況下,增加或刪除表中的欄位或者擴充套件欄位長度。
執行線上增減欄位時表會鎖一小段時間,一般是秒級鎖表。鎖表時間短得益於fractal tree的實現。TokuDB會把這些操作放到後臺去做,具體實現是:往root塊推送一個廣播msg,通過逐層apply這個廣播msg實現增減欄位的操作。
需要注意的:
- 不建議一次更新多個欄位
- 刪除的欄位是索引的一部分會鎖表,鎖表時間跟資料量成正比
- 縮短欄位長度會鎖表,鎖表時間跟資料量成正比
mysql> ALTER TABLE t_test ADD COLUMN column_c int(11) NOT NULL;
mysql> SHOW CREATE TABLE t_test\G
Table: t_test
Create Table: CREATE TABLE `t_test` (
`column_a` int(11) NOT NULL,`column_c` int(11) NOT NULL,PRIMARY KEY (`column_a`),KEY `ind_1` (`column_b`)
) ENGINE=TokuDB DEFAULT CHARSET=latin1 ROW_FORMAT=TOKUDB_SNAPPY
mysql> ALTER TABLE t_test DROP COLUMN column_b;
mysql> SHOW CREATE TABLE t_test\G
Table: t_test
Create Table: CREATE TABLE `t_test` (
`column_a` int(11) NOT NULL,PRIMARY KEY (`column_a`)
) ENGINE=TokuDB DEFAULT CHARSET=latin1複製程式碼
穩定高效寫入效能
TokuDB索引採用fractal tree結構,索引修改工作由後臺執行緒非同步完成。TokuDB會把每個索引更新轉化成一個msg,在server層上下文只把msg加到root(或者某個internal)塊msg buffer中便可返回;msg應用到leaf塊的工作是由後臺執行緒完成的,此後臺執行緒被稱作cleaner,負責逐級apply msg直至leaf塊
DML語句被轉化成FTINSERT/FTDELETE,此類msg只應用到leaf節點。
線上加索引/線上加欄位被轉化成廣播msg,此類msg會被應用到每個資料塊的每個資料項。
實際上,fractal tree是buffer tree的變種,在索引塊內快取更新操作,把隨機請求轉化成順序請求,縮短server執行緒上下文的訪問路徑,縮短RT。所以,TokuDB在高併發大資料量場景下,可以提供穩定高效的寫入效能。
除此之外,TokuDB實現了bulk fetch優化,range query效能也是不錯的。
線上增加索引
TokuDB支援線上加索引不阻塞更新語句 (insert,update,delete) 的執行。可以通過變數 tokudbcreateindex_online 來控制是否開啟該特性,不過遺憾的是目前只能通過 CREATE INDEX 語法實現線上建立;如果用ALTER TABLE建立索引還是會鎖表的。
mysql> SHOW CREATE TABLE t_test\G
Table: t_test
Create Table: CREATE TABLE `t_test` (
`column_a` int(11) NOT NULL,PRIMARY KEY (`column_a`)
) ENGINE=TokuDB DEFAULT CHARSET=latin1 ROW_FORMAT=TOKUDB_SNAPPY
mysql> SET GLOBAL tokudb_create_index_online=ON;
mysql> CREATE INDEX ind_1 ON t_test(column_b);
mysql> SHOW CREATE TABLE t_test\G
Table: t_test
Create Table: CREATE TABLE `t_test` (
`column_a` int(11) NOT NULL,KEY `ind_1` (`column_b`)
) ENGINE=TokuDB DEFAULT CHARSET=latin1 ROW_FORMAT=TOKUDB_SNAPPY複製程式碼
寫過程
如果不考慮unique constraint檢查,TokuDB寫是非同步完成的。每個寫請求被轉化成FT_insert型別的msg,記錄著要寫入的
Server上下文的寫路徑很短,只要把寫請求對應的msg追加到roo資料塊的msg buffer即可,這是LSM資料結構的核心思想,把隨機寫轉換成順序寫,LevelDB和RocksDB也是採用類似實現。
由於大家都在root資料塊快取msg,必然造成root塊成為熱點,也就是效能瓶頸。
為瞭解決這個問題,TokuDB提出promotion概念,從root資料塊開始至多往下看2層。如果當前塊資料塊是中間塊並且msg buffer是空的,就跳過這層,把msg快取到下一層中間塊。
下面我們舉例說明write過程。
假設,insert之qiafractal tree狀態如下圖所示:
- insert 300
root資料塊上300對應的msg buffer為空,需要進行inject promotion,也就是說會把msg儲存到下面的子樹上。下一級資料塊上300對應的msg buffer非空(msg:291),不會繼續promotion,msg被儲存到當前的msg buffer。
- insert 100
root資料塊上100對應的msg buffer為空,需要進行inject promotion,也就是說會把msg儲存到下面的子樹上。下一級資料塊上100對應的msg buffer也為空,需要繼續promotion。再下一級資料塊上100對應的msg buffer非空(msg:84),不會繼續promotion,msg被儲存到當前的msg buffer。
- insert 211
root資料塊上211對應的msg buffer為空,需要進行inject promotion,也就是說會把msg儲存到下面的子樹上。下一級資料塊上211對應的msg buffer也為空,需要繼續promotion。再下一級資料塊上211對應的msg buffer也為空,但是不會繼續promotion,msg被儲存到當前的msg buffer。這是因為promotion至多向下看2層,這麼做是為了避免dirty的資料塊數量太多,減少checkpoint刷髒的壓力。
行級鎖
TokuDB提供行級鎖處理併發讀寫資料。
所有的INSERT、DELETE或者SELECT FOR UPDATE語句在修改索引資料結構fractal tree之前,需要先拿記錄(也就是key)對應的行鎖,獲取鎖之後再去更新索引。與InnoDB行鎖實現不同,InnoDB是鎖記錄資料結構的一個bit。
由此可見,TokuDB行鎖實現導致一些效能問題,不適合大量併發更新的場景。
為了緩解行鎖等待問題,TokuDB提供了行鎖timeout引數(預設是4秒),等待超時會返回失敗。這種處理有助於減少deadlock發生。
讀過程
由於中間資料塊(internal block)會快取更新操作的msg,讀資料時需要先把上層msg buffer中的msg apply到葉子資料塊(leaf block)上,然後再去leaf上把資料讀上來。
3,4,5,6,7,8,9是中間資料塊,10,11,12,13,14,15,16,17是葉子資料塊;
上圖中,每個中間資料塊的fanout是2,表示至多有2個下一級資料塊;中間節點的msg buffer用來快取下一級資料塊的msg,橘黃色表示有資料,黃綠色表示msg buffer是空的。
如果需要讀block11的資料,需要先把資料塊3和資料塊6中的msg apply到葉子資料塊11,然後去11上讀資料。
Msg apply的過程也叫合併(merge),所有基於LSM原理的k-v引擎(比方LevelDB,RocksDB)讀資料時都要先做merge,然後去相應的資料塊上讀資料。
讀合併
如上圖所示,綠色是中間資料塊,紫色是葉資料塊;中間資料塊旁邊的黃色矩形是msg buffer。
如要要query區間[5-18]的資料
- 以5作為search key從root到leaf搜尋>=5的資料,每個資料塊內部做binary search,最終定位到第一個leaf塊。讀資料之前,判斷第一個leaf塊所包含的[5,9]區間存在需要apply的msg(上圖中是6,8),需要先做msg apply然後讀取資料(5,9);
- 第一個leaf塊讀取完畢,以9作為search key從root到leaf搜尋>9的資料,每個資料塊內部做binary search,最終定位到第二個leaf塊。讀資料之前,判斷第二個leaf塊所包含的[10,16]區間存在需要apply的msg(上圖中是15),需要先做msg apply然後讀取資料(10,16);
- 第二個leaf塊讀取完畢,以16作為search key從root到leaf搜尋>16的資料,每個資料塊內部做binary search,最終定位到第三個leaf塊。第三個資料塊所包含的[17,18]區間不存在需要apply的msg,直接讀取資料(17,18)。
優化range query
為了減少merge代價,TokuDB提供bulk fetch功能:每個basement node大小64K(這個是資料壓縮解壓縮的單位)只要做一次merge操作;並且TokuDB的cursor支援批量讀,一個batch內讀取若干行資料快取在記憶體,之後每個handler::indexnext先去快取裡取下一行資料,只有當快取資料全部被消費過之後發起下一個batch讀,再之後handler::indexnext操作還是先去快取裡取下一行資料。
Batch讀過程由cursor的callback驅動,直接把資料存到TokuDB handler的buffer中,不僅減少了merge次數,也減少了handler::index_next呼叫棧深度。
非同步合併
TokuDB支援後臺非同步合併msg,把中間資料塊中快取的msg逐層向下刷,直至leaf資料塊。
這過程是由週期執行的cleaner執行緒完成的,cleaner執行緒每秒被喚醒一次。每次執行掃描一定數目的資料塊,尋找快取msg最多的中間資料塊;掃描結束後,把msg buffer中的msg刷到(merge)下一層資料塊中。
前面提到,大部分寫資料並不會把msg直接寫到leaf,而是把msg快取到root或者某一級中間資料塊上。雖然promotion緩解了root塊熱點問題,區域性熱點問題依然存在。
假設某一個時間段大量併發更新某範圍的索引資料,msg buffer短時間內堆積大量msg;由於cleaner執行緒是單執行緒順序掃描,很可能來不及處理熱點資料塊,導致熱點資料msg堆積,並且資料塊讀寫鎖爭搶現象越來越嚴重。
為瞭解決這個問題,TokuDB引入了專門的執行緒池來幫助cleaner執行緒快速處理熱點塊。大致處理是:如果msg buffer快取了過多的msg,寫資料上下文就會喚醒執行緒池中的執行緒幫助cleaner快速合併當前資料塊。
刷髒
為了加速資料處理過程,TokuDB在記憶體快取資料塊,所有資料塊組織成一個hash表,可以通過hash計算快速定位,這個hash表被稱作cachetable。InnoDB也有類似快取機制,叫做buffer pool(簡記bp)。
記憶體中資料塊被修改後不會立即寫回磁碟,而是被標記成dirty狀態。Cachetable滿會觸發evict操作,選擇一個victim資料塊釋放記憶體。如果victim是dirty的,需要先把資料寫回。Evict操作是由後臺執行緒evictor處理的,預設1秒鐘執行一次,也可能由於快取滿由server上下文觸發。
TokuDB採用激進的快取策略,儘量把資料保留在記憶體中。除了evictor執行緒以外,還有一個定期刷髒的checkpoint執行緒,預設60每秒執行一次把記憶體中所有髒資料回刷到磁碟上。Checkpoint結束後,清理redo log檔案。
TokuDB採用sharp checkpoint策略,checkpoint開始時刻把cachetable中所有資料塊遍歷一遍,對每個資料塊打上checkpointpending標記,這個過程是拿著client端exclusive鎖的,所有INSERT/DELETE操作會被阻塞。標記checkpointpending過程結束後,釋放exclusive鎖,server的更新請求可以繼續執行。
隨後checkpoint執行緒會對每個標記checkpoint_pending的髒頁進行回寫。為了減少I/O期間資料塊讀寫鎖衝突,先把資料clone一份,然後對cloned資料進行回寫;clone過程是持有讀寫鎖的write鎖,clone結束後釋放讀寫鎖,資料塊可以繼續提供讀寫服務。Cloned資料塊寫回時,持有讀寫I/O的mutex鎖,保證on-going的I/O至多隻有一個。
更新資料塊發現是checkpoint_pending並且dirty,那麼需要先把老資料寫盤。由於checkpoint是單執行緒,可能來不及處理這個資料塊。為此,TokuDB提供一個專門的執行緒池,server上下文只要把資料clone一份,然後把回寫cloned資料的任務扔給執行緒池處理。
Cachetable
所有快取在記憶體的資料塊按照首次訪問(cachemiss)時間順序組織成clocklist。TokuDB沒有維護LRU list,而是使用clocklist和count(可理解成age)來模擬資料塊使用頻率。
Evictor,checkpoint和cleaner執行緒(參見非同步合併小結)都是掃描clock_list,每個執行緒維護自己的head記錄著下次掃描開始位置。
如上圖所示,hash中黑色連線表示bucket連結串列,藍色連線表示clocklist。Evictor,checkpoint和cleaner的header分別是mclockhead,mcheckpointhead和mcleaner_head。
資料塊被訪問,count遞增(最大值15);每次evictor執行緒掃到資料塊count遞減,減到0整個資料塊會被evict出去。
TokuDB塊size比較大,預設是4M;所以按照塊這個維度去做evict不是特別合理,有些partition資料比較熱需要在記憶體多呆一會,冷的partition可以儘早釋放。
為此,TokuDB還提供partial evict功能,資料塊被掃描時,如果count>0並且是clean的,就把冷partition釋放掉。Partial evict對中間資料塊(包含key分佈資訊)做了特殊處理,把partition轉成壓縮格式減少記憶體使用,後續訪問需要先解壓縮再使用。Partial evict對leaf資料塊的處理是:把partition釋放,後續訪問需要呼叫pf_callback從磁碟讀資料,讀上來的資料也是先解壓縮的。
寫優先
這裡說的寫優先是指併發讀寫資料塊時,寫操作優先順序高,跟行級鎖無關。
假設使用者要讀區間[210,256],需要從root->leaf每層做binary search,在search之前要把資料塊讀到記憶體並且加readlock。
如上圖所示,root(height 3)和root子資料塊(height 2)嘗試讀鎖(try_readlock)成功,但是在root的第二級子資料塊(height 1)嘗試讀鎖失敗,這個query會把root和root子資料塊(height 2)讀鎖釋放掉,退回到root重新嘗試讀鎖。
日誌
TokuDB採用WAL(Write Ahead Log),每個INSERT/DELETE/CREATE INDEX/DROP INDEX操作之前會記redo log和undo log,用於崩潰恢復和事務回滾。
TokuDB的redo log是邏輯log,每個log entry記錄一個更新事件,主要包含:
- 長度1
- log command(標識操作型別)
- lsn
- timestamp
- 事務id
- crc
- db
- key
- val
- 長度2
其中,db,key和val不是必須的,比如checkpoint就沒有這些資訊。
長度1和長度2一定是相等的,記兩個長度是為了方便前向(backward)和後向(forward)掃描。
Recory過程首先前向掃描,尋找最後一個有效的checkpoint;從那個checkpoint開始後向掃描回放redo log,直至最後一個commit事務。然後把所有活躍事務abort掉,最後做一個checkpoint把資料修改同步到磁碟上。
TokuDB的undo日誌是記錄在一個單獨的檔案上,undo日誌也是邏輯的,記錄的是更新的逆操作。獨立的undo日誌,避免老資料造成資料空間膨脹問題。
事務和MVCC
相對RocksDB,TokuDB最顯著的優勢就是支援完整事務,支援MVCC。
TokuDB還支援事務巢狀,可以用來實現savepoint功能,把一個大事務分割成一組小事務,小事務失敗只要重試它自己就好了,不用回滾整個事務。
ISOLATION LEVEL
TokuDB支援隔離級別:READ UNCOMMITTED,READ COMMITTED (default),REPEATABLE READ,SERIALIZABLE。SERIALIZABLE是通過行級鎖實現的;READ COMMITTED (default),和REPEATABLE READ是通過snapshot實現。
TokuDB支援多版本,多版本資料是記錄在頁資料塊上的。每個leaf資料塊上的
事務的可見性
每個寫事務開始時都會獲得一個事務id(TokuDB記做txnid,InnoDB記做trxid)。其實,事務id是一個全域性遞增的整數。所有的寫事務都會被加入到事務mgr的活躍事務列表裡面。
所謂活躍事務就是處於執行中的事務,對於RC以上隔離界別,活躍事務都是不可見的。前面提到過,SERIALIZABLE是通過行級鎖實現的,不必考慮可見性。
一般來說,RC可見性是語句級別的,RR可見性是事務級別的。這在TokuDB中是如何實現的呢?
每個語句執行開始都會建立一個子事務。如果是RC、RR隔離級別,還會建立snapshot。Snapshot也有活躍事務列表,RC隔離級別是複製事務mgr在語句事務開始時刻的活躍事務列表,RR隔離級別是複製事務mgr在server層事務開始時刻的活躍事務列表。
Snapshot可見性就是事務id比snapshot的事務id更小,意味著更早開始執行;但是不在snapshot活躍事務列表的事務。
GC
隨著事務提交snapshot結束,老版本資料不在被訪問需要清理,這就引入了GC的問題。
為了判斷寫事務的更新是否被其他事務訪問,TokuDB的事務mgr維護了referencexids陣列,記錄事務提交時刻,系統中處於活躍狀態snapshot個數,作用相當於referencecount。
以上描述了TokuDB如何跟蹤寫事務的引用者。那麼GC是何時執行的呢?
可以呼叫OPTIMIZE TABLE顯式觸發,也可以在後續訪問索引key時隱式觸發。
典型業務場景
以上介紹了TokuDB引擎核心原理,下面我們從HybridDB for MySQL產品的角度談一下業務場景和效能。
HybridDB for MySQL設計目標是提供低成本大容量分散式資料庫服務,一體式處理OLTP和OLAP混合業務場景,提供儲存和計算能力;而儲存和計算節點在物理上是分離的,使用者可以根據業務特點定製儲存計算節點的配比,也可以單獨購買儲存和計算節點。
HybridDB for MySQL資料只儲存一份,減少資料交換成本,同時也降低了儲存成本;所有功能整合在一個例項之中,提供統一的使用者介面,一致的資料檢視和全域性統一的SQL相容性。
HybridDB for MySQL支援資料庫分割槽,整體容量和效能隨分割槽數目增長而線性增長;使用者可先購買一個基本配置,隨業務發展後續可以購買更多的節點進行擴容。HybridDB for MySQL提供線上的擴容和縮容能力,水平擴充套件/收縮儲存和計算節點拓撲結構;在擴充套件過程中,不影響業務對外提供服務,優化資料分佈演演算法,減少重新分佈資料量;採用流式遷移,備份資料不落地。
除此之外,HybridDB for MySQL還支援高可用,複用鏈路高可用技術,採用一主多備方式實現三副本。HybridDB for MySQL複用ApsaraDB for MySQL已有技術框架,部署、升級、鏈路管理、資源管理、備份、安全、監控和日誌複用已有功能模組,技術風險低,驗證週期短,可以說是站在巨人肩膀上的創新。
低成本大容量儲存場景
HybridDB for MySQL使用軟硬體整體方案解決大容量低成本問題。
軟體方面,HybridDB for MySQL是分散式資料庫,擺脫單機硬體資源限制,提供橫向擴充套件能力,容量和效能隨節點數目增加而線性增加。儲存節點MySQL例項選擇使用TokuDB引擎,支援塊級壓縮,壓縮演演算法以表單位進行配置。使用者可根據業務自身特點選擇使用壓縮效果好的壓縮演演算法比如lzma,也可以選擇quicklz這種壓縮速度快資源消耗低的壓縮演演算法,也可以選擇像zstd這種壓縮效果和壓縮速度比較均衡的壓縮演演算法。如果選用zstd壓縮演演算法,線上實測的壓縮比是3~4。
硬體方面,HybridDB for MySQL採用分層儲存解決方案,大量冷資料儲存在SATA盤上,少量溫資料儲存在ssd上,熱資料儲存在資料庫引擎的記憶體快取中(TokuDB cachetable)。SATA盤和ssd上資料之間的對映關係通過bcache驅動模組來管理,bcache可以配置成WriteBack模式(寫路徑資料寫ssd後即返回,ssd中更新資料由bcache負責同步到SATA盤上),可加速資料庫checkpoint寫盤速度;也可以配置成WriteThrough模式(寫路徑資料同時寫到ssd和SATA上,兩者都ack寫才算完成)。
持續高併發寫入場景
TokuDB採用fractal tree(中文譯作分型樹)資料結構,優化寫路徑,大部分二級索引的寫操作是非同步的,寫被快取到中間資料塊即返回。寫操作同步到葉資料塊可以通過後臺cleaner執行緒非同步完成,也可能由後續的讀操作同步完成(讀合併)。Fractal tree在前面的核心原理部分有詳盡描述,這裡就不贅述了。
細心的朋友可能會發現,我們在非同步寫前加了個字首:大部分二級索引。那麼大部分是指那些情況呢?這裡大部分是指不需要做quickness檢查的索引,寫請求直接扔給fractal tree的msg buffer即可返回。如果二級索引包含unique索引,必須先做唯一性檢查保證不存在重複鍵值。否則,非同步合併(或者讀合併)無法通知唯一性檢查失敗,也無法回滾其他索引的更新。Pk欄位也有類似的唯一性語義,寫之前會去查詢pk鍵值是否已存在,順便做了root到leaf資料塊的預讀和讀合併。所以,每條新增資料執行INSERT INTO的過程不完全是非同步寫。
ApsaraDB for MySQL對於日誌場景做了優化,利用INSERT IGNORE語句保證pk鍵值唯一性,並且通過把二級索引鍵值1-1對映到pk鍵值空間的方法保證二級索引唯一性,將寫操作轉換成全非同步寫,大大降低了寫延遲。由於省掉唯一性檢查的讀過程,引擎在記憶體中快取的資料量大大減少,快取寫請求的資料塊受讀幹擾被釋放的可能性大大降低,進而寫路徑上發生cachetable miss的可能性降低,寫效能更加穩定。
分散式業務場景
HybridDB for MySQL同時提供單分割槽事務和分散式事務支援,支援跨表、跨引擎、跨資料庫、跨MySQL例項,跨儲存節點的事務。HybridDB for MySQL使用兩階段提交協議支援分散式事務,提交階段proxy作為協調者將分散式事務狀態記錄到事務元資料庫;分割槽事務恢復時,proxy從事務元資料庫取得分散式事務狀態,並作為協調者重新發起失敗分割槽的事務。
HybridDB for MySQL還可以通過判斷WHERE條件是否包含分割槽鍵的等值條件,決定是單分割槽事務還是分散式事務。如果是單分割槽事務,直接傳送給分割槽MySQL例項處理。
線上擴容/縮容場景
HybridDB for MySQL通過將儲存分割槽無縫遷移到更多(或更少的)MySQL分割槽例項上實現彈性資料擴充套件(收縮)的功能,分割槽遷移完成之後proxy層更新路由資訊,把請求切到新分割槽上,老分割槽上的資料會自動清理。Proxy切換路由資訊時會保持連線,不影響使用者業務。
資料遷移是通過全量備份+增量備份方式實現,全量備份不落地直接流式上傳到oss。增量備份通過binlog方式同步,HybridDB for MySQL不必自行實現binlog解析模組,而是利用ApsaraDB for MySQL優化過的複製邏輯完成增量同步,通過並行複製提升效能,並且保證資料一致性。
聚合索引提升讀效能
TokuDB支援一個表上建立多個聚合索引,以空間代價換取查詢效能,減少回pk取資料。阿里雲ApsaraDB for MySQL在優化器上對TokuDB聚合索引做了額外支援,在cost接近時可以優先選擇聚合索引;存在多個cost接近的聚合索引,可以優先選擇與WHERE條件最匹配的聚合索引。
與單機版ApsaraDB for MySQL對比
與阿里雲OLTP+OLAP混合方案對比
效能報告
高併發業務
壓測配置:
- 4節點,每節點8-core,32G,12000 iops,ssd盤
高吞吐業務
壓測配置:
- 8節點,每節點16-core,48G,12000 iops,ssd盤