mysql-hbase存儲引擎插件實現大容量數據存儲

最近把hbase-storage-plugin代碼分享到github 上,為了記錄筆者當時的思路,所以寫了這篇文章。

1、初衷:做一個集中的大容量存儲引擎

1.1、起因

自從進入公司運維部以后,雖然一直在做開發的工作,但是跟DBA同學可以“親密”接觸,從而可以體會到個中的各種酸甜苦辣。在我們這邊,DBA同學遇到的很多告警是磁盤空間告警,半夜起來處理這種故障實在是讓人狼心。
在處理這種磁盤故障的過程中,發現很多業務庫中存儲了日志型的數據,定期就需要刪除,近期的數據訪問就不是很頻繁,至于很多歷史數據,就更是很少訪問了。
考慮存在冷熱數據的不同,一直琢磨在MySQL基礎上實現一個大容量存儲引擎。熱數據用Innodb存儲,等它變成冷數據,就改成大容量引擎。

1.2 第一次嘗試

剛開始筆者考慮基于HDFS上做一個MySQL存儲引擎,由于HDFS文件不能修改。正好利用LevelDB的存儲特性,只會生成文件,而不會修改文件,于是改造了LevelDB的代碼,讓LevelDB運行在HDFS之上,然后基于LevelDB做了一個MySQL存儲引擎。
這樣在開發環境可以跑起來了,但是實際運行中,經常出現內存問題,因為HDFS的C-API是基于JVM的,沒有純C的庫,內存問題無法解決,最后只能放棄。
因為hbase提供了一個thrift服務,可以支持c++語言,而且hbase天然有索引特性,這樣我們在實現主鍵功能時會非常簡單,所以最后敲定了hbase。

1.3、打算解決的問題

如果我們有了hbase這樣一個海量的MySQL存儲引擎,我們就可以解決以下幾個難題。

1、冷熱數據采用不同引擎

如下圖所示:把近期的熱數據先用Innodb引擎存儲,隨著時間的推移,逐步把一些老數據表,通過alter table 表名 engine hbase改成用Hbase來存儲。

code-hot-data.png

通過這種方式,可以在數據的高效訪問與數據保存周期上達到雙贏,重復利用了Innodb的性能和hbase海量容量的特性。

2、主從庫采用不同引擎
主從庫采用不同的引擎,在主庫中采用Innodb,并且只保留近期數據。從庫中用hbase引擎存儲所有數據,歷史數據從主庫刪除的時候,不刪除從庫中的表。
這樣也可以達到數據長期保存的效果,而且還可以防止因hbase引擎代理問題,影響線上業務。

master-slave.png

3、集中存儲,數據共享

一套Hbase存儲多套業務數據,甚至于,可以讓不同業務訪問相同的Hbase的表。一個業務的表也輕松的轉移到另外一個業務中來。

sharingstorage.png

2、hbase存儲引擎的開發

2.1 主數據存儲格式

首先,每一張MySQL表,對應在Hbase中建立一張對應的表,所以在MySQL的增刪改查都會對應到Hbase表中的操作。
Bbase只有一個rowkey用來定位數據,而MySQL的鍵可以有多個字段組成,為了實現鍵查詢和鍵 前綴查詢,筆者首先按照MySQL主鍵字段順序逐個組織成一個字節數組,也就是最后要存儲到Hbase中的RowKey。

rowkey.png

MySQL中主鍵的字段類型,這里只列了整數型和字符串型,開發Hbase存儲引擎的時候,筆者只支持以下的數據類型成為主鍵:

MYSQL_TYPE_LONG
MYSQL_TYPE_LONGLONG
MYSQL_TYPE_TINY
MYSQL_TYPE_SHORT
MYSQL_TYPE_INT24
MYSQL_TYPE_TIME
MYSQL_TYPE_DATETIME
MYSQL_TYPE_TIMESTAMP
MYSQL_TYPE_VAR_STRING
MYSQL_TYPE_VARCHAR
MYSQL_TYPE_BIT
MYSQL_TYPE_STRING

這些字段類型除了后面的4個是字符串以外,前面的都可以轉換成為整數型數據。按照圖中的格式存儲主鍵,主要是為了實現鍵字段數據還原、鍵順序查詢(order by)等功能。

由于hbase支持字段,所以數據字段就按照hbase的字段來存儲。

由于Hbase天然具有順序,所以筆者按照主鍵存儲在Rowkey,數據字段存儲在hbase的列中,這樣主數據存儲了根據主鍵定位數據的能力,所以Hbase引擎表是一種列簇表。從代碼中我們就可以看出來:

virtual bool primary_key_is_clustered() { return TRUE; }

2.2 第二索引功能

第二索引功能的實現有賴于Hbase對一個表有批量寫操作的支持,下面我們先看一下Hbase支持的批量寫操作API。

/**
* Performs multiple mutations atomically on a single row. Currently
* {@link Put} and {@link Delete} are supported.
*
* @param rm object that specifies the set of mutations to perform atomically
* @throws IOException
*/
void mutateRow(final RowMutations rm) throws IOException;

這個API可以保證這些變更操作的原子性,基于這個保證,筆者就能夠輕易的實現第二索引功能了。

2.2.1 第二索引存儲格式

為了保證操作的原子性,筆者把第二索引的存儲也存儲在主數據對應的這張Hbase表中,格式為:
RowKey:格式是有組成鍵值的字段按照順序組成
entry:key 字段存儲了主鍵的數據。

2.2.2 第二索引數據變更

在增刪改查時,和主數據一起生成一批Mutation,在Hbase中一次性對表進行操作,從而保證了原子性。

2.3.2 TODOList

開源的代碼中實現了唯一性的第二索引,對于非唯一的第二索引,可以考慮把重復的鍵值存放在相同的第二索引Rowkey下。

2.3 批量數據插入

MySQL存儲引擎提供了很多優化的操作能力,譬如批量數據插入,當我們load數據、批量插入或者做一些表變更(如:更換存儲引擎)的時候,會用到批量數據操作。
批量數據操作會先緩存一些數據行,當達到緩存大小時,把這些數據一次性的寫入底層存儲中,這里也利用了Hbase的批量操作能力。

2.4 基于主鍵的查詢優化

當一條SQL語句中,指定了所有的主鍵字段的情況下, 這時候,是可以避免采用范圍查詢,而是直接采用基于rowkey的定位查詢功能的。筆者實現了下面的函數:
virtual int index_read_idx_map(uchar * buf, uint index, const uchar * key,
key_part_map keypart_map, enum ha_rkey_function find_flag);
在這個函數中,是直接調用了ScannerID HbaseClient::scannerOpenWithScan(const Text& tableName, const TScan& scan, const std::map<Text, Text> & attributes)函數來快速定位到主鍵的。

2.5 其他

由于MySQL實例訪問Hbase是通過網絡來訪問的,所以這里做一些底層的優化處理,如:連接池、連接重建等,還有很多優化的空間。

3、改造thrift server

開發完引擎以后,與hbase一起聯調,一旦建立幾個連接,后續的連接請求就無法服務了,主要原因是thrift server才用了傳統的半同步半異步設計模式,每個新的連接,會啟動一個獨立的線程來為它服務,一旦線程用完就無法再為后續的連接請求服務了。

如何解決這個問題呢,可以把這種模式改造成反應器設計模式,就能夠提供高并發的服務了。
于是基于swift重新實現了hbase的thrift server,swift是一套基于netty實現的thrift服務框架,開發的步驟主要是:
1)基于thrift協議文件,生成服務框架:

java -jar .\swift-generator-cli-0.19.3-standalone.jar -override_package org.apache.hadoop.hbase.swift.generated -use_java_namespace org\apache\hadoop\hbase\thrift\Hbase.thrift -out ..\java

2)在生成的框架中實現hbase的訪問邏輯。
3)重寫thrift server之后,還有一個好處是我們可以擴展thrift server的能力,筆者在原有的API的基礎上添加了幾個API,如下圖所示:

thrift-api-change.png

有了這些api,我們就可以利用它們來實現一些額外的功能,如:更改引擎,truncate table語法等。

有興趣研究swift的可以看一下筆者很早以前記錄的一篇文章(今天放到簡書):http://www.lxweimin.com/p/49c619d33307

4、總結

筆者在公司內部沒有采用這個方案,最終選擇了mariadb來解決這種日志型存儲的問題,日志性的表可以選擇tokudb引擎,一般能達到4倍以上的壓縮比,好的情況下可以達到10倍。在公司現有業務場景下基本上能解決絕大多數問題了。畢竟Mariadb的成熟度高,使用廣,穩定性好。當然仍然無法解決海量的存儲問題。

后來筆者基于思路完成了大部分代碼,近期把它開源了放在了github上:
https://github.com/herry2038/mysql-hbase-storage-plugin
主要是筆者覺得hbase這個思路不錯,一方面交流學習,另一方面希望有機會能繼續完善項目。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容