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

MySQL HBase HDFS LevelDB 中國大數據 中國大數據 2017-09-07

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來存儲。

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

code-hot-data.png

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

2、主從庫採用不同引擎

主從庫採用不同的引擎,在主庫中採用Innodb,並且只保留近期數據。從庫中用hbase引擎存儲所有數據,歷史數據從主庫刪除的時候,不刪除從庫中的表。

這樣也可以達到數據長期保存的效果,而且還可以防止因hbase引擎代理問題,影響線上業務。

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

master-slave.png

3、集中存儲,數據共享

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

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

sharingstorage.png

2、hbase存儲引擎的開發

2.1 主數據存儲格式

首先,每一張MySQL表,對應在Hbase中建立一張對應的表,所以在MySQL的增刪改查都會對應到Hbase表中的操作。

Bbase只有一個rowkey用來定位數據,而MySQL的鍵可以有多個字段組成,為了實現鍵查詢和鍵 前綴查詢,筆者首先按照MySQL主鍵字段順序逐個組織成一個字節數組,也就是最後要存儲到Hbase中的RowKey。

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

rowkey.png

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

MYSQL_TYPE_LONGMYSQL_TYPE_LONGLONGMYSQL_TYPE_TINYMYSQL_TYPE_SHORTMYSQL_TYPE_INT24MYSQL_TYPE_TIMEMYSQL_TYPE_DATETIMEMYSQL_TYPE_TIMESTAMPMYSQL_TYPE_VAR_STRINGMYSQL_TYPE_VARCHARMYSQL_TYPE_BITMYSQL_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,如下圖所示:

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

thrift-api-change.png

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

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

4、總結

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

後來筆者基於思路完成了大部分代碼,近期把它開源了放在了github上:

https://github.com/herry2038/mysql-hbase-storage-plugin

主要是筆者覺得hbase這個思路不錯,一方面交流學習,另一方面希望有機會能繼續完善項目。

相關推薦

推薦中...