Hbase学习笔记
表存储的内容本身上没有类型,都是字节数组.
存储的每一条数据都有一个行键,根据行键来划分给对应的regionserver来负责.
它是面向列的存储,具体来说是面向列族的存储,每一个列族的数据,根据rowkey进行分发,
分给特定的region.
Hbase 是面向列族存储的,同属一个列族的数据是存在一起的。
建议同一个列族的成员尽可能属性和大小近似,如经常访问的数据放在一个列族,
属性比较固定的放在一起.
表被自动地水平化分成regions.每一个regions包含一张表的部分行。
regions是如何拆分的?
region 是hbase分布式的单元。
Hbase的体系架构
Hbase 也是cs架构,有master,regionserver
hmaster的作用是把region分配给regionserver.
负责regionserver的恢复。
master 负载比较轻相对。
Regionserver,
处理数据的读写请求,
管理region splits
zookeeper 存储着hbase的meta 目录表的位置.
regions的分配也是通过zookeeper进行中转的,以防参与分配的regionserver 崩溃,
可以恢复。
hbase的逻辑模型
-root-来存储.memta.的位置,而-root-的位置又存储在zookeeper上面,root本身不会拆分成多个region.
新版本已经去除-root-的设计思路,只保留了meta表
meta表会随着表的增大和增多而扩展为多个region.
所以查找需要三级跳,root,meta,table.这样才能获取到你想要的数据。
meta表存储的是当前列表,状态,和所有用户空间regions的位置。
由于hregion server本身状态的变化,meta表本身存储的信息也在不断变化。
hbase:backup
hbase:meta The hbase:meta table holds references to all User Table regions
hbase:namespace The hbase:namespace table holds information about namespaces.
如何清除过期的region in transition?
rowkey,timestamp(version),column family,column, then get what you want.
Hregionserver
hlog,lsm
memory store, then storefile.
one storefile keep one column family's data.
in hbase life cycle, 它不断地整理他的文件,去掉该去掉的版本和该删除的行。
DataWrite
首先会写到Hlog里面,然后添加到内存的memstore,当memstore达到一定的阀值就会输出到磁盘文件storefile.
因为hlog是在hdfs上的,所以可以应对当前hregionserver的宕机。
A background process compacts flush files once their number has exceeded a threshold,
rewriting many files as one, because the fewer files a read consults, the more performant it
will be. On compaction, the process cleans out versions beyond the schema-configured
maximum and removes deleted and expired cells. A separate process running in the
regionserver monitors flush file sizes, splitting the region when they grow in excess of the
configured maximum.
一个后端进程压缩storefile,当它们的数量达到一定阀值,重读写成一个文件 ,这样可以减少读数据访问时间,同时移除过期和被删除的数据。
另一个单独的进程会监控storefile的总大小,达到一定配置的值时,就把它进行拆分。
hbase shell
hbase master
hbase regionserver
hbase-daemon.sh start master
habase-daemon.sh start regionserver
hbase classpath
hbase version
create 'test', 'data'
list
put 'test', 'row1', 'data:1', 'value1'
put 'test', 'row2', 'data:2', 'value2'
put 'test', 'row3', 'data:3', 'value3'
get 'test', 'row1'
drop 'test'
hbase regionserver crash
MasterServer的角度看来只有一种现象,那就是RegionServer在Zookeeper上面注册的Node消失了.
zookeeper.session.timeout:默认值3分钟,不可配置太短,避免session超时,hbase停止服务,线上生产环境由于配置为1分钟
,出现过2次该原因导致的hbase停止服务,也不可配置太长,如果太长,当rs挂掉,zk不能快速知道,从而导致master不能及时对region进行迁移。
因为使用过一次graceful_stop 可能造成balance_switch关闭了,而且机器重器后,节点的regions分配不均匀,容易造成负载重的机器,
gc长时间没影响,与zk的连接超时,最后crashed.
balance_switch true
balancer
1.hbase创建的表的元数据存在哪里?
存储在zookeeper上面,
但phonix会创建自己的元数据表
它类似于mysq,sqlserver等数据库,自己也存在shcema为system的几个数据表,用来存储创建的表等信息.
如catalog等.
2.hbase shell看到的表与phoenix看到的表不一样,为什么?
phoenix中创建的表,存在于自己的元数据表中一份,同时也在hbase:meta中有信息,可以在shell中看到.
而hbase shell中创建的表 hive中创建的hbase的表是无法在phoenix中看到 ,它没有更新到phoenix的元数据信息表中.
行键是不可分割的字节数组,行是按字典排序由低到高存储在表中的.一个空的数组是用来表示开始或结尾的.
cell中的version是用一个长整型表示的,这个值使用java.util.date.gettime来产生,它的含义是
当前时间和1970-01-01UTC时间差,单位毫秒.
在Hbase中,版本是按倒序排列的,最先找到的是最近的版本.
一个表存在多列族,注意基数(如, 行数). 如果列族A有100万行,列族B有10亿行,列族A可能被分散到很多很多区(及区服务器)。这导致扫描列族A低效。
这个问题存在吗?hbase不是只面向列族进行存储吗?列族间关系如何?
关于行键设计,首先要理解hbase是如何根据行键分配到regionserver的?
行键设计
单调递增行键/时序数据存储的时候,容易把负载压在一台机器上,要尽量避免这样的key.
如果要导入时间序列的数据,可以学习opentsdb的做法
opentsdb的key的格式是[metric_type][event_timestamp]
有成百上千的metric_type就可以把压力分散到各个Region了.
尽量最小化行键和列的大小设计,
因为他们本身存储的次数非常多,而且行键用于索引,如果建二级索引,列信息也会存在内存中,
越大越占用内存,所以慎用.
列族,属性都尽量短小,如d:via |data:veryimportantattribute
倒序时间戳
[key][reverse_timestamp](long.max_value - timestamp)
相同的行键可以在同一个表的不同列族中存在而不会冲突.
行键不能改变.
版本的默认数量是3.
支持数据类型?
可以是字符串,数字,复杂对象,图像,只要他们能转换成字节
TTL(存活时间)
列族可以设置TTL数,hbase会在超时后自动删除过期数据.
二级索引
最简单的方式是,同一份数据,换一个行键,再存储一次,就相当于建了一个二级索引.
定期更新第二索引
二级索引可以在同一个表中创建,插入的时候,多次插入不同行键的数据即可.
也可以在不同的表中创建,并通过MAPREDUCE任务定期更新.
双写第二索引
将数据写到集群的同时创建并写到索引表
协处理(类似rdbms的触发器)方式的二级索引
Block Cache /bloom Filters/Compress
对大容量查询优化
Mapreduce/spark 交互操作
Phoenix
hbase的适用场景
数量量上. 至少上千万上亿条数据,hbase是很好的备选.
千万以下的数据,rdbms就会是更好的选择,因为数据可能在
少量节点保存,其他节点闲置.
目录表
Hbase:meta表被hbase shell的list命令过滤掉了,但它和其他表一样存在.
连接池:
多线程访问应用(如web服务器或服务器需要在一个jvm服务很多应用线程),
可以使用HTablePool
有各种各样的过滤器来过滤列值,或过滤列名的.方便在scan的时候过滤数据.
一个regsion server默认1g的堆大小,block cache size达到217M(约25%).
hbase在内存中的数据有
1.行键,列族,列,时间戳信息
2.hfile index信息
3.catalog table信息
Hbase的每一个region server会将更新(put,deletes)先记录到日志中(WAL),然后再更新.
这样保证了写的可靠性,当region server宕掉的时候,Memstore还没有flush,storeFile还没有保存,
数据就会丢失.
Hlog是hbase的一个wal实现,每一个region server都有一个hlog实例,WAL保存在/hbase/.logs里面,
每一个region一个文件.
一个表有多少个Region,就像rdbms里面的类似,一开始是一个,慢慢分开的.
Region split
Region server来负责管理,先将region下线,然后切割,将元信息添加到meta中,
再后再上线.再通知master.
有关region的分割策略?
要想查看hfile的文本化内容,可以使用
${HBASE_HOME}/bin/hbase org.apache.hadoop.hbase.io.hfile.Hfile -v -f hdfs://…/..
BlockSize 针对每一个列族 进行配置.
压缩也是在blocklevel.
Compact
分两种类型,major and minor
Minor 通常会将数个小的相邻的文件合并成一个大的.不会删除打上删除标记的数据.
也不会删除TTL过期的数据.
Major 会删除过期的数据和打上删除标记的数据.
在大型系统中,通常会自己来管理 major compact,因为它的负载很大,他会将一个store
中的数据全部重写,影响很大.
有关性能调优
OS:
小心交换,将交换区设置为0
Network:
使用好的交换机
多机架的配置,可能导致性能低的原因来自以下两个方面:
1.较低的交换机容量性能
2.到其他机架上的上行链路不足java:
关注gc日志,尝试调优配置gc算法
region大小可以通过每一张表设置,如果需要与默认不同,则可以设置HtableDescriptor的set
Filesize.
java代码
在循环内和mapreduce工作内,将列族名,列名转换为字节数组的代价是昂贵的.请注意这一点.
最好使用字节数组常量.
当你进行大量put的时候,请确认你的HTable的setAutoFlush是关闭着的.
否则的话每一次put都会向regionServer发送一个请求.
在代码中,一定要关闭ResultScanners
这是为了避免发生性能问题,如果你忘记关闭了,会致导regionserver出现问题,所以建议
把ResultScanner放在try/catch块中.
什么问题,连接数过多,浪费资源.
开启布隆过滤器,可以节省必须读磁盘过程,可以有助于改进读取延迟.
常见的RS自杀的原因
1.低层的hdfs出了问题
2.一个长时间的GC,zk会话时长超时.
如何对hbase进行性能监控?
Major compact自己控制,就是根据自己对业务的了解情况,禁用hbase自身的自动进行的
Major compact,在特定时间中去用命令显式触发Major compact.这样不影响用户使用.
在大型系统中基本都这么来做的.
如果你感到某表的region数量过多,可以使用Merge来进行合并,以减少region的数量.
监控度量非常重要,如下度量
Hbase:
Requests
Compactions queue
Os:
Io wait
User cpu
Java
Gc
2016年阿里的同学对hbase做了一个牛逼的改进,对于L2缓存,
Bucket cache,原来的hbase已经支持offheap 的方式进行缓存,但他们发现有问题,
问题在于hbase region server还是需要从off heap的directbytebuffer 读到l1的onheap buffer,
这个情况在他们的量级下非常严重,造成严重的浪费与gc问题.
它们改进后,可以直接从l2的offheap memory中读取,避免了这个问题.
2017 hbase有了一个mob的改进,专门针对大块的数据,如图片,音频与文档,500k到10m
之间的数据进行了优化,出了专门的region.
Hbase 在我看来就是一个灵活的分区表,类比rdbms而言.它的随机访问能力强,还有一点
它的表现性比较强,不需要提前预定列.
如果使用phoenix的话,是不是它的不需要提前固定列的优势就被抹灭了呢?
不是的,早在1.2版本时,它就开始支持动态的列了.
对于phoenix,它使用隐藏的列族的方式,把要索引的数据存放在这个里面,这是其中一个
名叫本地索引的处理方式.
而且phoenix的索引,现在默认都是同步索引,即数据是同步进行抽取的.
不是异步地去触发mapreduce作业去抽取数据到索引表中.
对于不变的表(即相同的rowkey,列族和列值不变的)
简单的说,就是只增加值,不删除更新值的表.
可以做一些优化来减小写时候的负载.
如日志数据,时序数据,
Create table my_table (k varchar primary key, v varchar) immutable_rows=true;
默认情况下,表认为是可变的.
并没有真正的守卫去做检测,保证你声明的不可变的表做变化的时候提出错误.
同时,基于这样的表创建的索引,它也是不可变的.这样如果你真的更新了表值,
那么它的索引是不同步的.是有问题的.对于非事务的可变表 关于它的索引情况
它是在WAL成功后,分别向其主表和索引表并发写入数据,
我们不保证其主表和索引表在所有相同时刻都是同步的.
最终会是数据一致的,但是会有一定短时间的数据不同步.
根据机器配置本身的情况,部分的phoenix的配置参数可以修改,以提高效率.
如机器的配置高,就可以把创建 index的线程数调高一些.