HBase 精华一页纸
Posted 一页纸世界
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HBase 精华一页纸相关的知识,希望对你有一定的参考价值。
1、从关系型数据库到分布式数据库
I、行式 -> 列式
数据库最初的设计都是按行存储数据。但往往查询都只是某些常用列,并不是所有列。每次查询按行IO明显比较多。关系数据库发展成按例存储的方式。
列式存储的优势:只需要检索这列,减少了IO,效率得以提高;同时,相同数据存储,可以有很高的压缩率,同样的空间可以存储更多的数据。
II、规则 -> 反规则
语言的发展,从 C、java强规则的语言,到javascript、scala 这些弱规则的语言。比如数组为例,java一个数组只能定义一种类型,js可以混合。
数据库的发展也经历着这种过程,从二维表 规则的 关系型的数据库 到 key-value 非规则的数据库。
原因很简单,世界既有规则的部分;也有非规则的部分。如果硬要用规则来套用,则会产生很多浪费;比如 二维表存储不规则数据,需要包含所有列,浪费了很多空间。
III、共享 -> 分散
传统关系型数据库,往往分布的多个进程节点,都共享一套文件数据,即使从服务器到磁阵。容量和带宽还是受到限制,对于数据库的扩展有着很大的影响。
分布式数据库,采用hdfs分散的 文件存储,把数据分散在每个节点上;通过Zookeeper 调度,通过副本,保证数据不会丢失;
IV、分布式数据库HBase
基于 HDFS 文件系统,构建的分布式数据库
2、HBase的存储模型
I、逻辑模型
Namespace - Table - Rowkey + family + column + version(time) - cell
和关系数据库相比,HBase是半结构化的数据库:结构化的部分是 Rowkey + family,而column 是可选任意的,version是多个时间戳
和行列对应到一个 记录值不同, HBase是 四个纬度共同决定了一个 记录值,而每一层纬度看起来都是一个key-value结构,最外层是 rowkey 和列簇的key-value,在列簇这边是 colum - 多版本value的map,对于版本同样是这样的map
II、物理模型
HRegion - MemStore/BlockCache - HFile
HRegion 对应于 hdfs的一个目录
memstore 是数据在内存中的缓存,只有达到一定阈值才会写到文件 HFile中,当内容超过一定值,就写一个新文件
和mysql不同,mysql是一个表一个文件,而HBase是一个表多个region,每个region有多个文件。这么设计也是分布式数据库的一个特征;这样就带来一个问题,表的记录散落在集群各个节点,如何寻址?
III、HLog
类似于Mysql的 binlog,一般数据库的redolog,防止在写数据时出现异常
IV、工作机制
a、写文件
同时 写 HLog - Memstore,然后再写入 HFile
为了避免系统崩溃,提前写入本地 底层 原生 文件系统 WAL 文件(注意,每个节点写自己的 - 可以考虑hdfs写文件过程,是pipeline),只有本地写入成功才认为可以正确写入
每个节点所有的表共享一个 WAL列表,在宕机时从列表中恢复
b、读文件
为了提高读写效率,每个Region 有一个 BlockCache,缓存 LRU 数据(如果访问大块连续数据,大一点block比较好,如果频繁访问离散数据,小一点block)
首先检查 memstore 中等待修改的队列,再检查 BlockCache是否缓存,都没有,再去HFile中寻找
c、文件合并
delete也不会立即删除,值是打上标记,打上标记的记录get和scan是不能访问到的
小合并 - 把多个HFile合并成一个 大HFile (可以设置合并数目)
大合并 - 把region下一个列族所有的HFile进行合并,在合并时清理所有删除记录,还有多余的版本(版本默认3个,多月的会删除)
V、寻址
和HDFS 类似,HBase有两个系统表(元数据表) -ROOT- .META.
a、访问 Zookeeper 获取到 -ROOT- 表信息在哪里
b、通过查找 -ROOT-表 查找 具体表的记录 信息在 哪个 .META. 表的 某个region 块上
c、查找 .META. 获取具体记录数据需要读取哪个 region,在哪个RegionServer上
d、通过具体的 RegionServer 获取数据
由此可以看到,HBase是两级-三层的索引体系
VI、管理节点
和HDFS类似,有两个管理进程 HMaster(类似NameNode) HRegionServer(类似DataNode)
HMaster 管理 HRegionServer,分配 Region
HRegionServer 维护本地的 region,处理请求、合并等管理操作
3、表设计
不同于二维表的关系数据库,数据分散在集群中;如果设计不当,则效率非常低
I、宽表 or 高表
宽表:每行按照正常模型,设计多个 列族和列(限定符)
高表:则是压缩行的设计,比如把这些列族设计在行健中,这样就会出现 多行 一列的情况
优缺点
宽表,因为不同列族可能数据不在一个 HFile里,查询可能稍慢;高表,因为列族较少,通过行健就能快速定位,查询较快;但对于某些列族的修改,涉及多个行。
II、行键的设计
为写设计 - 避免热点数据,要分散
散列:如果考虑负载均衡,写时分散,考虑使用散列,查询优化可以考虑 多个字段散列 + 相加
加盐:通过根据算法为 时间戳等 加一个盐前缀,这样扫描的话,拆分多个扫描,作为读的权衡
为读设计 - 数据要尽量连续,方便连续读取,减少IO
比如把时间戳作为行键扫描,可以快速获取范围
行键设计,需要考虑放入哪些元素(一般情况下,用作常用搜索条件的字段都考虑放入行健,比如时间+用户ID)以及元素的位置(查询时条件的顺序,决定行健的元素顺序)
考虑使用散列函数MD5,SHA 等把行键 标准化(长度一致,效率可控;数据离散,均衡热点;按范围查找,需要全表扫描)
典型设计:哈希 + 时间戳 等关键字,对一些离散度不高的字段,可以采用反转等方式
III、列族设计
名称尽可能短 -- 因为存储时,每个单元格都是携带 这些名称的限定符;传输时,rowkey、列族、限定符 都一起传输,需要减少传输量
列族尽量不要太多。 因为不同的 列族可能会在不同HFile中,这样需要定位多个文件
4、HBase基本操作
I、表操作
表空间 create_namespace 'space'
创建表 create 'test_signal','cf' -- 可以指定表空间 create 'space:test_signal'
查询表 list
表结构 describe 'test_signal'
失效表 disable 'table_name'
删除表 drop 'table_name'
表存在 exists 'table_name'
更改表 alter 'table_name',{NAME=>'列族',METHOD=>'delete'}
清空表 truncate 'table_name'
II、单记录操作
插入值 put 'test_signal','first','cf:message','hello World'
查询值 get 'test_signal','first'
删除值 delete 'test_signal','first','cf:message'
注意没有单独的更新,因为HBase都采用追加,更新就是在原有记录上追加新的记录
III、批量操作
类似 jQuery 这些 选择器(限定条件) + 过滤器的操作思路
a、限定条件
限定列族 scan 'table',{COLUMNS=>’列族’}
查询记录数 scan 'table',LIMIT=>1
限定时间戳 scan 'table',{TIMERANGE=>[1448045892646,1448045892647]}
开始结束行 startRow - endRow (可以使用模糊匹配检索)
b、过滤器
Filter接口和 抽象类 FilterBase
filterRowKey - 根据行键过滤
filterKeyValue - 过滤行里面的每个元素
filterRow(List) - 可以转换过滤后的元素
filterRow -- 过滤行
filterAllRemaining - 过滤到剩下的
系统自带过滤器
RowFilter - 行过滤器,提供各种比较方式
PrefixFIlter - 前缀过滤器,提供 行键的开始 - 结束扫描范围,算是行过滤器的特例,实际过程中使用很多
QualifierFilter - 过滤 列族里面的列名称(限定符),可以获取指定列
ValueFilter - 值过滤器,对单元值过滤
TimestampsFilter - 时间戳过滤,对多个版本的时间进行过滤
组合过滤器 FilterList,可以组合多个过滤器
这些过滤器对 Get 也是适用的
其他操作参见
http://blog.csdn.net/w541826816/article/details/45691315
5、java 操作 HBase
I、初始化连接表
Configuration hbaseConf = HBaseConfiguration.create();
hbaseConf.set("hbase.zookeeper.quorum", "10.43.136.152");
hbaseConf.set("hbase.rpc.timeout", "3000");
hbaseConf.set("zookeeper.session.timeout", "3000");
table = new HTable(hbaseConf, "test_signal");
II、插入 & 更新
Put put = new Put(Bytes.toBytes("java api" ));
put.add(Bytes.toBytes("cf"), Bytes.toBytes("api"), Bytes.toBytes(System.currentTimeMillis()));
table.put(put);
III、查询
Get get = new Get(Bytes.toBytes("java api"));
Result r = table.get(get);
List<Cell> list = r.listCells();
for(Cell c : list){
Bytes.toString(c.getFamilyArray()),
Bytes.toString(c.getQualifierArray()),
Bytes.toString(c.getValueArray()),
new Date(c.getTimestamp()).toString();
}
IV、其他操作
删除
Delete del = new Delete(Bytes.toBytes("java api"));
table.delete(del);
6、对标关系数据库
I、对象对应关系
命名空间 - 表空间
表 - 表
主键 - 行键
列 - 列族 + 列限定符
外键 - (没有直接的映射,可以两张表,在java里面链接,或者 在同一个表存储多个 列族和列限定符)
II、ACID
A - 只有一行内的操作是原子的,行间操作不是原子的
CI - 全表扫描时,并不像数据库那样读取的是时间点的快照,只要没有读到的部分在更新,读取的都是新的值
行级别的ACID
III、索引
HBase只支持 rowkey的行健索引 + Scan全表扫描(过滤器),对于组合条件查询,一般考虑 设立 二级索引
二级索引的思想,就是 建立一个 value - rowkey 的映射表,这样在查询时,通过value找到rowkey,再根据rowkey找到对应的其他列族值。这个思路 和Mysql的辅助索引一样。
可以使用单独的索引表,也可以在同一个表内,把这个映射设计成单独的行健。
二级索引,有很多已经确认的开源实现方法
http://www.cnblogs.com/njuzhoubing/p/4549523.html
IV、关联查询
a、使用 MapReduce 关联
表作为map的输入,构建 key 为关联字段组合, value 需要查的字段
reduce阶段,把相同key的生成输出
b、HBase的 Sql接口 Phoenix ?
c、hive 绑定Hbase?
d、在代码中做关联合并?
关系数据的join也是 NLS Loop 这种方式,在代码层面一样可以进行关联merge
V、备份与恢复
a、HBase Relication
b、export/import
c、copyTable
http://blog.csdn.net/iam333/article/details/38232215
VI、MVCC
HBase采用了类似Mysql的 多版本并发控制,写时加锁,读时,读已提交的数据
VII、CAP
HBase的 强一致性主要是体现在 HBase Region层面的,底层的 HDFS分发,还是比较贴近 AP的
7、运维 & 优化
I、表预分区
默认情况下,创建表时,生成一个HRegion分区,所有数据写入时,都会往这个 HRegion写数据(到达门限再切分),这样造成机器和分区热点。
解决方法:
创建表时,可以显式指定分区,预先设定好分区数[],然后再给定每个分区的 key 值[]
分区策略,在设计行键时,对行键进行区间分隔,如果为了达到更好的分散效果,对行键还可以进行 hash
存储region时,每个region的 startKey endKey 都会存储在 -META- 等信息中,默认region endKey是无上界的
II、行键设计
散列 + 时间戳(其他字段)
所有存储的HFile都是 Rowkey+Value,所以 行键不能太大,也不能太小,方便索引定位
III、配置项
创建表时,使用 LZO 压缩
还可以使用布隆过滤器 BLOOMFILTER,增加对 value 是否在列族的cell中存在的检查
缓存 数据缓存 BLOCKCACHE | IN_MEMORY
IV、有效使用过滤器查询
V、客户端API使用注意事项
禁止自动刷新 setAutoFlush(false)
使用扫描缓存
限定扫描范围(过滤器)和获取数据
及时关闭 ResultScanner对象
create 'testtable',{NAME => 'coulmn1', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', VERSIONS => '10', COMPRESSION => 'LZO', TTL => '30000', IN_MEMORY => 'false', BLOCKCACHE => 'false'}, {NAME => 'coulmn', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', VERSIONS => '30', COMPRESSION => 'LZO', TTL => '30000', IN_MEMORY => 'true'} (其中的属性有versions:设置历史版本数,TTL:过期时间,COMPRESSION:压缩方式,当配置lzo的情况)
VI、合并HFile
major_compact 'testtable'
VII、刷新缓存memstore到HFile
flush 'testtable'
https://my.oschina.net/beiyou/blog/76456
以上是关于HBase 精华一页纸的主要内容,如果未能解决你的问题,请参考以下文章