美文网首页程序员我爱编程
mysql-hbase存储引擎插件实现大容量数据存储

mysql-hbase存储引擎插件实现大容量数据存储

作者: 何约什 | 来源:发表于2017-09-05 10:53 被阅读283次

最近把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.jianshu.com/p/49c619d33307

4、总结

笔者在公司内部没有采用这个方案,最终选择了mariadb来解决这种日志型存储的问题,日志性的表可以选择tokudb引擎,一般能达到4倍以上的压缩比,好的情况下可以达到10倍。在公司现有业务场景下基本上能解决绝大多数问题了。毕竟Mariadb的成熟度高,使用广,稳定性好。当然仍然无法解决海量的存储问题。

后来笔者基于思路完成了大部分代码,近期把它开源了放在了github上:
https://github.com/herry2038/mysql-hbase-storage-plugin
主要是笔者觉得hbase这个思路不错,一方面交流学习,另一方面希望有机会能继续完善项目。

相关文章

  • mysql-hbase存储引擎插件实现大容量数据存储

    最近把hbase-storage-plugin代码分享到github 上,为了记录笔者当时的思路,所以写了这篇文章...

  • MySQL引擎

    存储引擎 1、存储引擎其实就是如何实现存储数据,如何为存储的数据建立索引以及如何更新,查询数据等技术实现的方法。 ...

  • mysql 高级

    关键文件 日志文件 数据文件 配置文件 存储引擎 mysql使用插件式的存储引擎,MySQL存储引擎有InnoDB...

  • mysql深入学习02

    1.各存储引擎特点 1.1存储引擎介绍 插拔式的插件方式存储引擎是指定在表上面的不管使用什么存储引擎,都会在数据区...

  • mysql进阶篇一

    一、存储引擎 存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式 。存储引擎是基于表的,而不是基于库的...

  • MySQL存储引擎、事务日志并发访问以及隔离级别

    MySQL存储引擎 MySQL是插件式存储存储引擎,支持多种存储引擎常见的存储引擎有:MyISAM, Aria, ...

  • MySql存储引擎简介-基础篇

    MySql数据库存储引擎 存储引擎就是如何存取数据,建立索引,更新和查询数据的实现方法. 存储引擎是针对表而言的,...

  • Mysql各种存储引擎对比总结(常用几种)

    存储引擎是数据库的核心,对于mysql来说,存储引擎是以插件的形式运行的。虽然mysql支持种类繁多的存储引擎,但...

  • MySQL存储引擎介绍

    数据库中都会有存储引擎的概念,MySQL的特点就是其存储引擎支持插件式的,存储引擎用来处理数据库相关的CRUD操作...

  • 数据库存储类型和数据类型

    什么是存储引擎? 存储引擎其实就是如何存储数据、如何为存储的数据建立索引和如何更新、查询数据等技术的实现方法。因为...

网友评论

    本文标题:mysql-hbase存储引擎插件实现大容量数据存储

    本文链接:https://www.haomeiwen.com/subject/gihujxtx.html