一、集群化方案
1、MySQL应用的演化
MySQL与HBase说到最核心的点,是一种数据存储方案。方案本身没有对错、没有好坏,只有合适与否。相信多数公司都与MySQL有着不解之缘,部分学校的课程甚至直接以SQL语言作为数据库讲解。我想借自身经历,先来谈谈MySQL应用的演化。
只有MySQL
笔者之前曾在一家O2O创业公司工作,公司所有数据都存储在同一个MySQL里,而且没有任何主备方案。相信这是很多初创公司会用到的一个典型解决办法,当时这台MySQL为用户、订单、物流服务,同时也为线下分析服务。
单实例的问题:


主从方案
随着业务增加,单个DB是无法承载这么多请求的。于是就有了主从复制、读写分离的解决方案。
master只负责写请求,slave同步master用来服务读请求:



主从功能的问题:


垂直拆分
业务继续增长,master甚至无法承载所有的写请求,数据库需要按业务拆分。
垂直拆分的问题:


水平拆分
业务继续增长,订单表有大量的并发写入,而且已经有了几千万行数据。





参考:大众点评订单系统分库分表实践
https://zhuanlan.zhihu.com/p/24036067
水平分库/分表带来的问题:



扩容:需要再次水平拆分的:修改map,迁移数据……
2、MySQL的问题
MySQL的主要瓶颈,单机单进程。CPU有限、内存/磁盘功能、连接数有限、网卡吞吐有限……
集群的限制点:
关系型数据库,纵向的外键相互join;
范式参考链接:https://zhuanlan.zhihu.com/p/20028672



集群的方案:





服务稳定性:主节点挂了,Proxy会将从节点升级为主节点;从节点挂了会被其它从节点替换。
3、HBase集群化解决方案
水平拆分:




单个region过大,RegionServer会将region均分为两个(自动、手工)。然后更新.META.表。
扩容方案:
RegionServer向HMaster汇报状态。HMaster为RegionServer负载均衡,调整其负责的region 。
增/删RegionServer后,会为重新调整region的分配方式。
服务稳定性:
RegionServer只是计算单元,挂掉后Hmaster可以随便再找一个节点代替坏节点服务。
事务性:
HBase只保证行级事务,单行数据肯定存在同一台机器(单机事务很好做)。
备份容灾:

RegionServer只是计算单元,挂掉后不影响服务。
二、性能取舍
1、数据请求流程
HBase:



MySQL:

Rquest的路由流程,MySQL与HBase基本一致,那么RegionServer与MySQL的性能差异如何呢?
2、Hbase写得快
新增
为什么MySQL建议自增主键?(MySQL随机插入的代价)


辅助索引,插入基本是随机的:

HBase可以随机插入:




修改
MySQL数据变化引起存储变动:


HBase直接将变化写入到memstore,没有其它开销。
删除
MySQL数据删除:


HBase只是在memstore记录删除标记,没有其它开销。
3、结论
HBase写入内存+后台刷盘(最多是WAL,磁盘顺序写);MySQL需要维护B+树,大量的磁盘随机读写。
MySQL要求尽量追加写(自增 ID),速度较慢;HBase可以随机插入,速度很快。
MySQL读得快
MySQL数据是本地存储的,HBase是基于HDFS,有可能数据不在本地。
B+ 树天然的全局有序
根据主键查询,可以快速定位到数据所在磁盘块,只需要极少的磁盘IO即可拿到数据:通过缓存高层节点,主健查询只需要一次磁盘IO就可拿到数据;MySQL单表行数一般建议不会超过2千万,千万行以下的大表,B+树只需2~3层即可;

索引,提供快速定位能力:辅助索引B+树,可以快速定位到最终所需的主键ID,根据主键ID可以快速拿到所需信息。
HBase只有局部信息,没有辅助索引


MySQL成也B+,败也B+;HBase成也LSM,败也LSM。
4、附录

B+ 树
查询“值为25”的节点,只需要2次定位即可。
LSM树
查询“值为25”的节点,只需要4次定位即可。
三、优化思路
1、HBase优化点 (主要是读)
异步化



数据就近



快速检索
基于bloomfilter过滤:
正常检索,RegionServer会遍历所有Hfile查询所需数据。其中,需要遍历Hfile的索引块才能判断Hfile中是否有所需数据;
BloomFiler存储HFile的摘要,可以通过极少磁盘IO,快速判断当前HFile是否有所需数据:
基于timestamp过滤:


HFile存储结构:
HFile存储格式
参考链接:
https://link.zhihu.com/?target=https%3A//blog.csdn.net/yangbutao/article/details/8394149



MetaIndex存储Meta的索引信息,Meta存储一系列元信息;MetaIndex功能类似于B+树的非叶子节点;Meta存储bloomfiler等辅助信息。
2、MySQL优化点(主要是写)
查询缓存
将SQL执行结果放入缓存。
缓存B+高层节点
一千万行的大表,一般只需要一棵3层的B+树,其中索引节点 (非叶子节点) 的大小约20MB。完全可以考虑将大部叶子节点缓存,基于主键查询只需要一次IO。
减少随机写——缓冲:延迟写/批量写
上节提到,B+树通过自增主键大量减少随机插入。由于辅助索引的存在,插入、修改、删除操作,辅助索引可能引起大量的随机IO。


减少随机读——MRR
SELECT * FROM t WHERE key_part1 >= 1000 AND key_part1 < 2000 AND key_part2 = 10000;
# 普通操作分解:
key_part1= 1000, key_part2=1000, id = 1
select * from t where id=1
key_part1= 1001, key_part2=1000, id = 10
select * from t where id=10
...
# MRR 操作分解:
SELECT * FROM t WHERE key_part1 >= 1000 AND key_part1 < 2000 AND key_part2 = 10000;
key_part1= 1000, key_part2=1000, id = 1
buffer.append(1)
key_part1= 1000, key_part2=1000, id = 10
buffer.append(10)
...
sort(buffer)
select * from t where id in (buffer)
AI 代码解读
索引下推

引擎层根据过滤条件过滤掉无用的行,减少数据量,进而优化server的性能。
3、集群化数据库的辅助索引
InnoDB的辅助索引
B+树全局有序,叶子节点存储的是主键。基于辅助索引定位主键,再用主键定位数据。MySQL水平切分后,没办法跨库维持建立全局有序索引:


HBase相同问题

可以做到全局信息的维护,但没法保证事务性。
4、HBase异步合并带来的好处


版本数量:基于后台合并,可以将太旧版本干掉。
四、总结
不知道BigTable的前辈们是出于什么思路,本人冒昧揣测一下,多少应该是受到SQL数据库的影响。个人感觉,这些或许就是一脉相承的演进,至少用这种思路学习不显突兀。HBase不是凭空而来,也绝对不是解决所有问题的万能灵丹。
最直接的存储思路肯定是“文件”,当“文件”不能满足需求,就有了数据的组织方式,进而演进到关系数据库如MySQL。
MySQL以其“单机”很好地解决了ACID问题,但是,性能再好的“单机”势必演变成“单点”瓶颈,进而,分布式思路成为必然。
最简单的是扩展读,“无限”挂slave;进而拆分写节点,多点写入:垂直拆库、水平拆库。一旦选择分布式,就涉及如何主从一致、如何发现节点、如何运维、ACID的如何保证等问题。
进而就是一系列分布式方案,而HBase就是其中一种解决思路——只读主库保证一致,水平拆分、zk等机制保证自动运维、单行级ACID。至于性能方面,由于存储思路不同,MySQL与HBase分别取舍了不同的读写性能。继而,就衍生出了如何针对性进行优化。
以这种思路,HBase不是凭空出现。以个人浅显的目光所及,没有完美的架构,也没有绝对厉害的设计。固然SQL类数据库有其独领风骚的场景,NoSQL数据库自然也有纵横驰骋的疆域,无论是哪种架构,都有自己鞭长莫及的角落。
所以,应该说任何一种方案都没有完美,只有合适。而所有的合适都是演变而来,万变不离其宗:更好的解决问题。
原文发布时间为:2018-06-19
本文作者:杨宏志