很久以前就看到过关于Hbase的相关信息,那时以为这种需要大数据相关知识的数据库肯定是我这种业务开发程序员所接触不到的。
但是随着经历的公司多了,发现其实对于大数据这个概念而言,其实不过是Java开发的一个分支。你可以看到在很多的ToB业务中,都需要用到Hive,HBase,Spark等等这类常见的Hadoop系列的大数据操作工具。相对与ToC的业务,ToB需要看到数据的变化,越多的数据对于客户具有越高的价值。
例如,最近跑Demo的一个用户的店,广告系统就有60W的关键词信息,而且产品还要求每两个小时给用户跑一次。所以对于一般的程序员,大数据相关也是需要知道相关知识的。
有点扯远了,回到Hbase这个话题,这次通过这个项目对于Hbase有了一个完整的理解,所以一起来分析给大家。
如何学习新的知识
看了上一期的《奇葩说》,还是很有感触的。知识是无限的,而智慧才是有限的,不断的归纳总结是对知识提升到智慧最好的途径。
我总结归纳了一下如何学习一块新软件的系统,这里包括了语言,包块了数据库这类开箱即用的系统。
学会使用
在Hbase中是没有数据结构的,无论在那一行你都可以放下很长很长的列。这就意味着它很适合做一些数据仓库的业务场景,无须关注数据的结构,拿到数据就往里面扔。唯一需要设计的就是ROWKEY。简单的讲,数据是按照字典序来排放的,当你扫描相邻的数据就会很快。当然,它也是任使用者随意摆放的。
| ROW KEY | 数据内容 |
|---|---|
| aabb | 。。。 |
| abaa | 。。。 |
| ba | 。。。 |
| bbcc | 。。。 |
创建Hbase表
create 'campaigns',
{NAME =>'campaign', VERSIONS => 1, BLOCKCACHE => true, BLOCKSIZE => '65536', BLOOMFILTER => 'ROW'},
{SPLITS => ['00','01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17',
'18','19','20','21','22','23','24','25','26','27','28','29','30','31','32','33','34','35','36','37',
'38','39','40','41','42','43','44','45','46','47','48','49','50','51','52','53','54','55','56','57',
'58','59','60','61','62','63','64','65','66','67','68','69','70','71','72','73','74','75','76','77',
'78','79','80','81','82','83','84','85','86','87','88','89','90','91','92','93','94','95','96','97','98','99']}
campaigns表示表名,campaign表示列族名字。如果你不关注其他的信息,这就是你创建一个表所需要的。
那么剩下的是什么意思呢?容我一一道来。
BLOCKCACHE
开启BLOCKCACHE,我们都知道使用Hbase读取数据的时候往往是一块一块的读的,那么可以从磁盘上一次性将所需要的容量读出来。所以BLOCKCACHE一般设置为读取的量。
BLOOMFILTER
布隆过滤器,它说有不一定有,它说没有就一定没有。
使用一种数据结构来过滤大部分无效的读请求。
SPLITS
预分区,Hbase是面向分布式设计的。但是往往会出现热点数据,出现的结果就是某台服务器读写被打爆,发挥不出分布式的优点。可以预先设计一些分区,来提前规避一些热点问题,也可以减少region分裂的时间。
为什么列族只有一个
在一个Region中会有多个HStore,每个HStore对应一个Column Family。
HBase会在region下所有的StoreFile中最大的StoreFile大小超过阀值进行spliet。
而此时会出现一种情况,存在一大一小的HStore,而小的那个就被迫进行了spliet,导致查询小表的时候需要大量扫描,所有为的是对Column Family进行隔离。
查询
put "table","rowkey","columFamily:colum","value"
get "table","rowkey","columFamily:colum"
count "campaigns"
scan "campaigns", {}
更多的请看:www.cnblogs.com/nexiyi/p/hb…
有个小坑说下,使用truncate删除正表数据的时候,会将预分区都删除。所以还是最好drop,重新create。
最佳实践
最佳实践往往是踩坑过程中累计出来的。
Phoenix
背景是这样的,业务部门使用的是Python来读取Hbase,然而直接使用ROWKEY无法做一些计算(可以使用Hbase coprossors),同时需要二级索引。然后调研了一下,发现了Phoenix这个“神器”,在Hbase上面再搭建一层SQL层,同时Phoenix自带了Hbase coprossors节约编写代码的时间,就决定是你了皮卡丘,然后不断踩坑.
踩坑一:字符类型
我们都知道在Mysql可以指定各种类型,有Integer、Bigdcimal。然而放入Hbase我们使用的是byte[] s= Bytes.toBytes(“r1”);
使用Phoenix指定类型之后就发现报错了,phoenix无法识别,phoenix是需要固定的字节长度来读取有类型的字段的。你可以使用
github.com/apache/phoe… 来放入的时候固定类型长度。或者select SUM(TO_NUMBER(“impressions”)) from “data_report_campaigns”,在读取的时候使用Phoenix提供的函数。
踩坑二:二级索引
原先单纯的以为二级索引和Mysql一样,然而发现是我们太过天真了。Phoenix的二级索引就是建立一个新的表,将建立索引的表作为ROWKEY,类似于倒排索引的概念。
然而蛋疼的是如果你在查询的时候有包含非索引列,那么他便不会走索引。
另外,二级索引是Phoenix通过Hbase的coprossor生成的,所以只有通过phoenix才能会生成index数据。如果是通过Hbase来插入的数据,还需要在index表里面插数据。
踩坑三:phoenix 4.7的bug
Hbase的数据数量和Phoenix的数据量对不上了,Hbase只有60W的数据,Phoenix发现有800W。还以为是高并发导致数据错乱,结果怎么试都没有复现。怀疑是和时间相关的bug,手动触发Hbase的Major compaction,果然复现了。
解决方案:delete from system.stats where physical_name=’CUST.PROFILE’;
连接:stackoverflow.com/questions/3…
Hbase定期进行Major compaction之后,Phoenix并没有同步更新,所以需要删除数据。
Hbase coprossors 使用
原本对于coprossors还是带有抵触心理,因为将程序放在数据源觉得总会对数据造成影响。后来发现包括TiDB以及其他针对大数据计算的框架,都是有相同的思考,将计算放在数据源。这样能够减少大数据传输的带宽限制,从而提升速度。coprossors分为两种:
- EndPoint 实现计算,类似存储过程
- Observer 观察者模式,类似实现二级索引
使用的Demo: www.ibm.com/developerwo…
其他相关最佳实践
系统监控
这里包括的Hbase的容灾、性能监控,以及系统如何平稳升级
性能优化
HDFS相关配置优化、HBase服务端优化(GC优化、Compaction优化、硬件配置优化)、列族设计优化、客户端优化
整体理解
对于系统监控和性能优化,没有一个很好的实践也给不出啥有效的信息。而通过学会使用和最佳实践之后,一定是理解系统设计的理念,包括整体的架构、数据结构。
简单的讲就是理解:
- 一个Hbase的写过程是怎么样的?
- 一个Hbase的读过程是怎么样的?
感觉需要讲的有点多,先放一个Hbase的整体架构图,剩下的容我慢慢来讲。