mysql-hbase存儲(chǔ)引擎插件實(shí)現(xiàn)大容量數(shù)據(jù)存儲(chǔ)

最近把hbase-storage-plugin代碼分享到github 上俗他,為了記錄筆者當(dāng)時(shí)的思路例隆,所以寫了這篇文章。

1、初衷:做一個(gè)集中的大容量存儲(chǔ)引擎

1.1渠旁、起因

自從進(jìn)入公司運(yùn)維部以后攀例,雖然一直在做開發(fā)的工作,但是跟DBA同學(xué)可以“親密”接觸顾腊,從而可以體會(huì)到個(gè)中的各種酸甜苦辣肛度。在我們這邊,DBA同學(xué)遇到的很多告警是磁盤空間告警投慈,半夜起來處理這種故障實(shí)在是讓人狼心承耿。
在處理這種磁盤故障的過程中,發(fā)現(xiàn)很多業(yè)務(wù)庫(kù)中存儲(chǔ)了日志型的數(shù)據(jù)伪煤,定期就需要?jiǎng)h除加袋,近期的數(shù)據(jù)訪問就不是很頻繁,至于很多歷史數(shù)據(jù)抱既,就更是很少訪問了职烧。
考慮存在冷熱數(shù)據(jù)的不同,一直琢磨在MySQL基礎(chǔ)上實(shí)現(xiàn)一個(gè)大容量存儲(chǔ)引擎防泵。熱數(shù)據(jù)用Innodb存儲(chǔ)蚀之,等它變成冷數(shù)據(jù),就改成大容量引擎捷泞。

1.2 第一次嘗試

剛開始筆者考慮基于HDFS上做一個(gè)MySQL存儲(chǔ)引擎足删,由于HDFS文件不能修改。正好利用LevelDB的存儲(chǔ)特性锁右,只會(huì)生成文件失受,而不會(huì)修改文件,于是改造了LevelDB的代碼咏瑟,讓LevelDB運(yùn)行在HDFS之上拂到,然后基于LevelDB做了一個(gè)MySQL存儲(chǔ)引擎。
這樣在開發(fā)環(huán)境可以跑起來了码泞,但是實(shí)際運(yùn)行中兄旬,經(jīng)常出現(xiàn)內(nèi)存問題,因?yàn)镠DFS的C-API是基于JVM的余寥,沒有純C的庫(kù)领铐,內(nèi)存問題無法解決,最后只能放棄劈狐。
因?yàn)閔base提供了一個(gè)thrift服務(wù)罐孝,可以支持c++語言呐馆,而且hbase天然有索引特性肥缔,這樣我們?cè)趯?shí)現(xiàn)主鍵功能時(shí)會(huì)非常簡(jiǎn)單,所以最后敲定了hbase汹来。

1.3续膳、打算解決的問題

如果我們有了hbase這樣一個(gè)海量的MySQL存儲(chǔ)引擎改艇,我們就可以解決以下幾個(gè)難題。

1坟岔、冷熱數(shù)據(jù)采用不同引擎

如下圖所示:把近期的熱數(shù)據(jù)先用Innodb引擎存儲(chǔ)谒兄,隨著時(shí)間的推移,逐步把一些老數(shù)據(jù)表社付,通過alter table 表名 engine hbase改成用Hbase來存儲(chǔ)承疲。

code-hot-data.png

通過這種方式,可以在數(shù)據(jù)的高效訪問與數(shù)據(jù)保存周期上達(dá)到雙贏鸥咖,重復(fù)利用了Innodb的性能和hbase海量容量的特性燕鸽。

2、主從庫(kù)采用不同引擎
主從庫(kù)采用不同的引擎啼辣,在主庫(kù)中采用Innodb啊研,并且只保留近期數(shù)據(jù)。從庫(kù)中用hbase引擎存儲(chǔ)所有數(shù)據(jù)鸥拧,歷史數(shù)據(jù)從主庫(kù)刪除的時(shí)候党远,不刪除從庫(kù)中的表。
這樣也可以達(dá)到數(shù)據(jù)長(zhǎng)期保存的效果富弦,而且還可以防止因hbase引擎代理問題沟娱,影響線上業(yè)務(wù)。

master-slave.png

3腕柜、集中存儲(chǔ)花沉,數(shù)據(jù)共享

一套Hbase存儲(chǔ)多套業(yè)務(wù)數(shù)據(jù),甚至于媳握,可以讓不同業(yè)務(wù)訪問相同的Hbase的表碱屁。一個(gè)業(yè)務(wù)的表也輕松的轉(zhuǎn)移到另外一個(gè)業(yè)務(wù)中來。

sharingstorage.png

2蛾找、hbase存儲(chǔ)引擎的開發(fā)

2.1 主數(shù)據(jù)存儲(chǔ)格式

首先娩脾,每一張MySQL表,對(duì)應(yīng)在Hbase中建立一張對(duì)應(yīng)的表打毛,所以在MySQL的增刪改查都會(huì)對(duì)應(yīng)到Hbase表中的操作柿赊。
Bbase只有一個(gè)rowkey用來定位數(shù)據(jù),而MySQL的鍵可以有多個(gè)字段組成幻枉,為了實(shí)現(xiàn)鍵查詢和鍵 前綴查詢碰声,筆者首先按照MySQL主鍵字段順序逐個(gè)組織成一個(gè)字節(jié)數(shù)組,也就是最后要存儲(chǔ)到Hbase中的RowKey熬甫。

rowkey.png

MySQL中主鍵的字段類型胰挑,這里只列了整數(shù)型和字符串型,開發(fā)Hbase存儲(chǔ)引擎的時(shí)候,筆者只支持以下的數(shù)據(jù)類型成為主鍵:

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個(gè)是字符串以外瞻颂,前面的都可以轉(zhuǎn)換成為整數(shù)型數(shù)據(jù)豺谈。按照?qǐng)D中的格式存儲(chǔ)主鍵,主要是為了實(shí)現(xiàn)鍵字段數(shù)據(jù)還原贡这、鍵順序查詢(order by)等功能茬末。

由于hbase支持字段,所以數(shù)據(jù)字段就按照hbase的字段來存儲(chǔ)盖矫。

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

virtual bool primary_key_is_clustered() { return TRUE; }

2.2 第二索引功能

第二索引功能的實(shí)現(xiàn)有賴于Hbase對(duì)一個(gè)表有批量寫操作的支持辐马,下面我們先看一下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;

這個(gè)API可以保證這些變更操作的原子性,基于這個(gè)保證喜爷,筆者就能夠輕易的實(shí)現(xiàn)第二索引功能了冗疮。

2.2.1 第二索引存儲(chǔ)格式

為了保證操作的原子性,筆者把第二索引的存儲(chǔ)也存儲(chǔ)在主數(shù)據(jù)對(duì)應(yīng)的這張Hbase表中檩帐,格式為:
RowKey:格式是有組成鍵值的字段按照順序組成
entry:key 字段存儲(chǔ)了主鍵的數(shù)據(jù)术幔。

2.2.2 第二索引數(shù)據(jù)變更

在增刪改查時(shí),和主數(shù)據(jù)一起生成一批Mutation湃密,在Hbase中一次性對(duì)表進(jìn)行操作诅挑,從而保證了原子性。

2.3.2 TODOList

開源的代碼中實(shí)現(xiàn)了唯一性的第二索引泛源,對(duì)于非唯一的第二索引拔妥,可以考慮把重復(fù)的鍵值存放在相同的第二索引Rowkey下。

2.3 批量數(shù)據(jù)插入

MySQL存儲(chǔ)引擎提供了很多優(yōu)化的操作能力达箍,譬如批量數(shù)據(jù)插入没龙,當(dāng)我們load數(shù)據(jù)、批量插入或者做一些表變更(如:更換存儲(chǔ)引擎)的時(shí)候缎玫,會(huì)用到批量數(shù)據(jù)操作硬纤。
批量數(shù)據(jù)操作會(huì)先緩存一些數(shù)據(jù)行,當(dāng)達(dá)到緩存大小時(shí)赃磨,把這些數(shù)據(jù)一次性的寫入底層存儲(chǔ)中筝家,這里也利用了Hbase的批量操作能力。

2.4 基于主鍵的查詢優(yōu)化

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

2.5 其他

由于MySQL實(shí)例訪問Hbase是通過網(wǎng)絡(luò)來訪問的芒珠,所以這里做一些底層的優(yōu)化處理,如:連接池搅裙、連接重建等皱卓,還有很多優(yōu)化的空間。

3部逮、改造thrift server

開發(fā)完引擎以后娜汁,與hbase一起聯(lián)調(diào),一旦建立幾個(gè)連接兄朋,后續(xù)的連接請(qǐng)求就無法服務(wù)了掐禁,主要原因是thrift server才用了傳統(tǒng)的半同步半異步設(shè)計(jì)模式,每個(gè)新的連接颅和,會(huì)啟動(dòng)一個(gè)獨(dú)立的線程來為它服務(wù)傅事,一旦線程用完就無法再為后續(xù)的連接請(qǐng)求服務(wù)了。

如何解決這個(gè)問題呢峡扩,可以把這種模式改造成反應(yīng)器設(shè)計(jì)模式蹭越,就能夠提供高并發(fā)的服務(wù)了。
于是基于swift重新實(shí)現(xiàn)了hbase的thrift server教届,swift是一套基于netty實(shí)現(xiàn)的thrift服務(wù)框架响鹃,開發(fā)的步驟主要是:
1)基于thrift協(xié)議文件,生成服務(wù)框架:

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)在生成的框架中實(shí)現(xiàn)hbase的訪問邏輯案训。
3)重寫thrift server之后买置,還有一個(gè)好處是我們可以擴(kuò)展thrift server的能力,筆者在原有的API的基礎(chǔ)上添加了幾個(gè)API强霎,如下圖所示:

thrift-api-change.png

有了這些api堕义,我們就可以利用它們來實(shí)現(xiàn)一些額外的功能,如:更改引擎脆栋,truncate table語法等倦卖。

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

4、總結(jié)

筆者在公司內(nèi)部沒有采用這個(gè)方案椿争,最終選擇了mariadb來解決這種日志型存儲(chǔ)的問題怕膛,日志性的表可以選擇tokudb引擎,一般能達(dá)到4倍以上的壓縮比秦踪,好的情況下可以達(dá)到10倍褐捻。在公司現(xiàn)有業(yè)務(wù)場(chǎng)景下基本上能解決絕大多數(shù)問題了掸茅。畢竟Mariadb的成熟度高,使用廣柠逞,穩(wěn)定性好昧狮。當(dāng)然仍然無法解決海量的存儲(chǔ)問題。

后來筆者基于思路完成了大部分代碼板壮,近期把它開源了放在了github上:
https://github.com/herry2038/mysql-hbase-storage-plugin
主要是筆者覺得hbase這個(gè)思路不錯(cuò)逗鸣,一方面交流學(xué)習(xí),另一方面希望有機(jī)會(huì)能繼續(xù)完善項(xiàng)目绰精。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末撒璧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子笨使,更是在濱河造成了極大的恐慌卿樱,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件硫椰,死亡現(xiàn)場(chǎng)離奇詭異繁调,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)靶草,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門涉馁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人爱致,你說我怎么就攤上這事烤送。” “怎么了糠悯?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵帮坚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我互艾,道長(zhǎng)试和,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任纫普,我火速辦了婚禮阅悍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘昨稼。我一直安慰自己节视,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布假栓。 她就那樣靜靜地躺著寻行,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匾荆。 梳的紋絲不亂的頭發(fā)上拌蜘,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天杆烁,我揣著相機(jī)與錄音,去河邊找鬼简卧。 笑死兔魂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的举娩。 我是一名探鬼主播析校,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼晓铆!你這毒婦竟也來了勺良?” 一聲冷哼從身側(cè)響起绰播,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤骄噪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蠢箩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體链蕊,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年谬泌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了滔韵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡掌实,死狀恐怖陪蜻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贱鼻,我是刑警寧澤宴卖,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站邻悬,受9級(jí)特大地震影響症昏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜父丰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一肝谭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛾扇,春花似錦攘烛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蘑斧,卻和暖如春靖秩,著一層夾襖步出監(jiān)牢的瞬間须眷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工沟突, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留花颗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓惠拭,卻偏偏與公主長(zhǎng)得像扩劝,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子职辅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容