TokuDB · 引擎特性 · HybridDB for MySQL高壓縮引擎TokuDB 揭祕

原文出處:阿里雲RDS-數據庫內核組node

HybridDB for MySQL(原名petadata)是面向在線事務(OLTP)和在線分析(OLAP)混合場景的關係型數據庫。HybridDB採用一份數據存儲來進行OLTP和OLAP處理,解決了以往須要把一份數據屢次複製來分別進行業務交易和數據分析的問題,極大地下降了數據存儲的成本,縮短了數據分析的延遲,使得實時分析決策稱爲可能。mysql

HybridDB for MySQL兼容MySQL的語法及函數,而且增長了對Oracle經常使用分析函數的支持,100%徹底兼容TPC-H和TPC-DS測試標準,從而下降了用戶的開發、遷移和維護成本。算法

TokuDB是TokuTek公司(已被 Percona收購)研發的新引擎,支持事務/MVCC,有着出色的數據壓縮功能,支持異步寫入數據功能。sql

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,
  `column_b` 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_b` 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,
  `column_c` 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,
  `column_b` 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,
  `column_b` int(11) NOT NULL,
  PRIMARY KEY (`column_a`),
  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狀態以下圖所示:

image.png

  • insert 300

root數據塊上300對應的msg buffer爲空,須要進行inject promotion,也就是說會把msg存儲到下面的子樹上。下一級數據塊上300對應的msg buffer非空(msg:291),不會繼續promotion,msg被存儲到當前的msg buffer。

image.png

  • insert 100

root數據塊上100對應的msg buffer爲空,須要進行inject promotion,也就是說會把msg存儲到下面的子樹上。下一級數據塊上100對應的msg buffer也爲空,須要繼續promotion。再下一級數據塊上100對應的msg buffer非空(msg:84),不會繼續promotion,msg被存儲到當前的msg buffer。

image.png

  • insert 211

root數據塊上211對應的msg buffer爲空,須要進行inject promotion,也就是說會把msg存儲到下面的子樹上。下一級數據塊上211對應的msg buffer也爲空,須要繼續promotion。再下一級數據塊上211對應的msg buffer也爲空,可是不會繼續promotion,msg被存儲到當前的msg buffer。這是由於promotion至多向下看2層,這麼作是爲了不dirty的數據塊數量太多,減小checkpoint刷髒的壓力。

image.png

行級鎖

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上把數據讀上來。

image.png

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,而後去相應的數據塊上讀數據。

讀合併

image.png

如上圖所示,綠色是中間數據塊,紫色是葉數據塊;中間數據塊旁邊的黃色矩形是msg buffer。

如要要query區間[5-18]的數據

  • 以5做爲search key從root到leaf搜索>=5的數據,每一個數據塊內部作binary search,最終定位到第一個leaf塊。讀數據以前,判斷第一個leaf塊所包含的[5,9]區間存在須要apply的msg(上圖中是6,7,8),須要先作msg apply而後讀取數據(5,6,7,8,9);
  • 第一個leaf塊讀取完畢,以9做爲search key從root到leaf搜索>9的數據,每一個數據塊內部作binary search,最終定位到第二個leaf塊。讀數據以前,判斷第二個leaf塊所包含的[10,16]區間存在須要apply的msg(上圖中是15),須要先作msg apply而後讀取數據(10,12,15,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操做仍是先去緩存裏取下一行數據。

image.png

Batch讀過程由cursor的callback驅動,直接把數據存到TokuDB handler的buffer中,不只減小了merge次數,也減小了handler::index_next調用棧深度。

異步合併

TokuDB支持後臺異步合併msg,把中間數據塊中緩存的msg逐層向下刷,直至leaf數據塊。

這過程是由週期運行的cleaner線程完成的,cleaner線程每秒被喚醒一次。每次執行掃描必定數目的數據塊,尋找緩存msg最多的中間數據塊;掃描結束後,把msg buffer中的msg刷到(merge)下一層數據塊中。

image.png

前面提到,大部分寫數據並不會把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記錄着下次掃描開始位置。

image.png

如上圖所示,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從磁盤讀數據,讀上來的數據也是先解壓縮的。

寫優先

這裏說的寫優先是指併發讀寫數據塊時,寫操做優先級高,跟行級鎖無關。

image.png

假設用戶要讀區間[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數據塊上的 二元組,key是索引的key值(實際上是拼了pk的),value是MVCC數據。這與oracle和InnoDB不一樣,oracle的多版本是經過undo segment計算構造出來的。InnoDB MVCC實現原理與oracle近似。

事務的可見性

每一個寫事務開始時都會得到一個事務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已有技術框架,部署、升級、鏈路管理、資源管理、備份、安全、監控和日誌複用已有功能模塊,技術風險低,驗證週期短,能夠說是站在巨人肩膀上的創新。

image.png

低成本大容量存儲場景

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優化過的複製邏輯完成增量同步,經過並行複製提高性能,而且保證數據一致性。

image.png

聚合索引提高讀性能

TokuDB支持一個表上建立多個聚合索引,以空間代價換取查詢性能,減小回pk取數據。阿里雲ApsaraDB for MySQL在優化器上對TokuDB聚合索引作了額外支持,在cost接近時能夠優先選擇聚合索引;存在多個cost接近的聚合索引,能夠優先選擇與WHERE條件最匹配的聚合索引。

與單機版ApsaraDB for MySQL對比

image.png

與阿里雲OLTP+OLAP混合方案對比

image.png

性能報告

高併發業務

壓測配置:

  • 4節點,每節點8-core,32G,12000 iops,ssd盤

image.png

高吞吐業務

壓測配置:

  • 8節點,每節點16-core,48G,12000 iops,ssd盤

image.png

相關文章
相關標籤/搜索