五、HBase
1、hbase数据模型
- rowkey
- table的主键,table中的记录数按照rowkey的字典序排列的
- row key行键可以是任意字符串(最大长度64kb,实际应用一般长度为10kb-100kb)
- column family列族
- hbase的表中都归属于某一列族
- 列族是schema的一部分,建表的时候必须指定一个列族
- 列:指的是某一列族下的某一列,用列族名:列名表示
- cell单元格:指定rowkey、列簇、列可以确定一个单元格,cell中的数据是没有类型的,全部是以字节数据存储的
- 时间戳:可以对cell多次赋值,每次赋值的时间戳可以看作是版本号
hbase数据存储原理
- 1个HRegionServer负责管理多个region
- 1个region包含多个store
- 一个列族就划分盛一个store
- 一个store中只有一个memstore(内存区域:写入的数据会先写入到memstore进行缓存,然后把数据刷新到从磁盘)
- 一个store中有多个storefile,最后是以多个hfile这种数据结构保存在hdfs上
- storefile是hfile的抽象。每次memstore刷写数据到磁盘,就对应生成一个新的hfile文件出来
hbase读数据流程
- 客户端首先与zk建立连接;从zk中找到meta表的region位置。meta表的数据存储在某一个hregionserver上,客户端与此hregionserver建立连接,然后读取meta表中的数据;meta中存储了所有用户表的region信息,我们可以i通过scan ‘hbase:mata’查看meta信息
- 根据要查询的namespace、表名和rowkey信息。找到写入数据对应的region信息
- 找到这个region对应的regionserver,然后发送请求
- 查询并定位到对应的region
- 先从这个memstore查找数据,如果没有,再从blockcache上读取
- 内存分两部分:
- memstore用来写
- blockcache用来读数据
- 内存分两部分:
- 如果blockcache中也没有再从storefile上读取
- 从storeFile中读取到数据之后,不是直接把结果数据返回给客户端,而是把数据先写入到BlockCache中,目的是为了加快后续的查询;然后在返回结果给客户端。
hbase写数据流程
- 客户端首先从zk找到meta表的region位置,然后读取meta表中的数据,meta表中存储了用户表的region信息
- 根据namespace,表名和rowkey信息。找到写入数据对应的region信息
- 找到这个region对应的regionserver,然后发送请求
- 把数据分别写到hlog和memstore各一份
- memstore达到阈值后把数据刷到磁盘生成storefile文件
- 删除hlog中的历史数据(hlog:也成wal意为write ahead log,类似mysql的binlog,用来做容灾恢复时用的,记录所有的变更,一旦数据修改,可以从log中恢复)
HBase的flush、compact机制
flush
-
flush触发条件
- memstore级别限制,当region中任意一个memstore的大小达到了上线,会出发memstore刷新
- region级别限制,当regison中所有memstore的大小综合达到了上线,会触发memstore刷新
- region server级别刷新。当一个region server中所有memstore的大小总和超过低水位线阈值,regionserver开始强制flush
- 先flush memstore最大的region,在执行次大的,依次执行。
- 如写入速度大于flush写出的大小,导致总memstore大小超过高水位阈值。此时regionserver会阻塞更新并强制执行flush,知道总memstore大小低于低水位阈值
- hlog数量大小上线。当一个regsion server中的hlog数量达到上线时,系统会选取最早的一个hlog对应的一个或多大region进行flush
- 定期刷新memstore。默认周期为1小时,确保memstore不会长时间没有持久化。为避免所有memstore在同一时间都进行flush导致的问题,定期的flush操作有20000左右的随机延时
- 手动flush
-
flush的流程
- 为了减少flush过程对读写的影响,将整个flush过程分为3个阶段:
- prepare阶段:遍历当前region中所有的memstore,将memstore中当前数据集cellskiplistset做一个快照snapshot,然后在新建一个cellskiplistset。后期写入的数据都会写入新的cellskiplistset中。prepare阶段需要加一把updatelock对写请求阻塞,结束之后会释放该锁。因为次就单没有任何费时操作,因此持锁时间很短
- flush阶段:遍历所有的memstore,将prepare阶段生成的snapshot持久化为临时文件,临时文件会同意放入目录.tmp下。这个过程因为会涉及到io操作,因此相比较比较费时
- commit阶段:遍历所有memstore,将flush阶段生成的临时文件移动到columnfamily目录下,针对HFile生成对应的storefile和Reader,把storefile添加到HStore的storefiles列表中,最后再清空prepare阶段生成的snapshot。
- 为了减少flush过程对读写的影响,将整个flush过程分为3个阶段:
compact
compact合并机制
-
hbase为了防止小文件过多,以保证查询效率,hbase需要在必要的时候将这些小的store file合并成相对较大的文件,这个过程就称之为compaction。
-
minor compaction小合并
- 在这个过程中会选取一些小的、相邻的StoreFile将他们合并成一个更大的StoreFile,对于超过了TTL的数据、更新的数据、删除的数据仅仅只是做了标记。并没有进行物理删除,一次Minor Compaction的结果是更少并且更大的StoreFile。这种合并的触发频率很高。
-
minor compaction大合并
-
合并Store中所有的HFile为一个HFile
将所有的StoreFile合并成一个StoreFile,这个过程还会清理三类无意义数据:被删除的数据、TTL过期数据、版本号超过设定版本号的数据。合并频率比较低,默认7天执行一次,并且性能消耗非常大,建议生产关闭(设置为0),在应用空闲时间手动触发。一般可以是手动控制进行合并,防止出现在业务高峰期。
- 默认7天进行一次或者手动触发
-
拆分机制
-
region拆分机制
-
region中存储大量的rowkey数据,当region中的数据条数过多的时候。直接影响查询效率。当region过大的时候,hbase会拆飞region,这也是hbase的一个有点
-
1、ConstantSizeRegionSplitPolicy
- 0.94版本前默认切分策略
-
当region大小大于某个阈值(hbase.hregion.max.filesize=10G)之后就会触发切分,一个region等分为2个region。
-
但是在生产线上这种切分策略却有相当大的弊端:切分策略对于大表和小表没有明显的区分。阈值(hbase.hregion.max.filesize)设置较大对大表比较友好,但是小表就有可能不会触发分裂,极端情况下可能就1个,这对业务来说并不是什么好事。如果设置较小则对小表友好,但一个大表就会在整个集群产生大量的region,这对于集群的管理、资源使用、failover来说都不是一件好事。
-
-
2、IncreasingToUpperBoundRegionSplitPolicy
-
0.94版本~2.0版本默认切分策略
-
切分策略稍微有点复杂,总体看和ConstantSizeRegionSplitPolicy思路相同,一个region大小大于设置阈值就会触发切分。但是这个阈值并不像ConstantSizeRegionSplitPolicy是一个固定的值,而是会在一定条件下不断调整,调整规则和region所属表在当前regionserver上的region个数有关系.
-
region split的计算公式是: regioncount^3 * 128M * 2,当region达到该size的时候进行split 例如: 第一次split:1^3 * 256 = 256MB 第二次split:2^3 * 256 = 2048MB 第三次split:3^3 * 256 = 6912MB 第四次split:4^3 * 256 = 16384MB > 10GB,因此取较小的值10GB 后面每次split的size都是10GB了
-
-
3、SteppingSplitPolicy
-
2.0版本默认切分策略
- 这种切分策略的切分阈值又发生了变化,相比 IncreasingToUpperBoundRegionSplitPolicy 简单了一些,依然和待分裂region所属表在当前regionserver上的region个数有关系,如果region个数等于1, 切分阈值为flush size * 2,否则为MaxRegionFileSize。这种切分策略对于大集群中的大表、小表会比 IncreasingToUpperBoundRegionSplitPolicy 更加友好,小表不会再产生大量的小region,而是适可而止。
-
-
4、KeyPrefixRegionSplitPolicy
- 根据rowKey的前缀对数据进行分组,这里是指定rowKey的前多少位作为前缀,比如rowKey都是16位的,指定前5位是前缀,那么前5位相同的rowKey在进行region split的时候会分到相同的region中。
-
5、DelimitedKeyPrefixRegionSplitPolicy
-
保证相同前缀的数据在同一个region中,例如rowKey的格式为:userid_eventtype_eventid,指定的delimiter为 _ ,则split的的时候会确保userid相同的数据在同一个region中。
-
- 6、DisabledRegionSplitPolicy
- 不启用自动拆分, 需要指定手动拆分
-
region合并机制
region合并说明
- Region的合并不是为了性能, 而是出于维护的目的 .
- 比如删除了大量的数据 ,这个时候每个Region都变得很小 ,存储多个Region就浪费了 ,这个时候可以把Region合并起来,进而可以减少一些Region服务器节点
7.2 如何进行region合并
7.2.1 通过Merge类冷合并Region
-
执行合并前,==需要先关闭hbase集群==
-
创建一张hbase表:
create 'test','info1',SPLITS => ['1000','2000','3000']
- 查看表region
-
需求:
需要把test表中的2个region数据进行合并: test,,1565940912661.62d28d7d20f18debd2e7dac093bc09d8. test,1000,1565940912661.5b6f9e8dad3880bcc825826d12e81436.
-
这里通过org.apache.hadoop.hbase.util.Merge类来实现,不需要进入hbase shell,直接执行(==需要先关闭hbase集群==): hbase org.apache.hadoop.hbase.util.Merge test test,,1565940912661.62d28d7d20f18debd2e7dac093bc09d8. test,1000,1565940912661.5b6f9e8dad3880bcc825826d12e81436.
-
成功后界面观察
7.2.2 通过online_merge热合并Region
-
==不需要关闭hbase集群==,在线进行合并
-
与冷合并不同的是,online_merge的传参是Region的hash值,而Region的hash值就是Region名称的最后那段在两个.之间的字符串部分。
-
需求:需要把test表中的2个region数据进行合并: test,2000,1565940912661.c2212a3956b814a6f0d57a90983a8515. test,3000,1565940912661.553dd4db667814cf2f050561167ca030.
-
需要进入hbase shell:
merge_region 'c2212a3956b814a6f0d57a90983a8515','553dd4db667814cf2f050561167ca030' -
成功后观察界面
hbase 特点:
1、Hbase一个分布式的基于列式存储的数据库,基于Hadoop的 hdfs 存储,zookeeper 进行管理。
2、Hbase适合存储半结构化或非结构化数据,对于数据结构字段不够确定或者杂乱无章很难按一个概念去抽取的数据。
3、Hbase 为 null 的记录不会被存储。
4、基于的表包含 rowkey,时间戳,和列族。新写入数据时,时间戳更新, 同时可以查询到以前的版本。
5、hbase 是主从架构。hmaster 作为主节点,hregionserver 作为从节点。
hbase和hive的区别
共同点:
hbase与hive都是架构在hadoop之上的。都是用hadoop作为底层存储。
区别:
1、Hive是建立在Hadoop之上为了减少MapReducejobs编写工作的批处理系统,HBase是为了支持弥补Hadoop对实时操作的缺陷的项目 。
2、想象你在操作RMDB数据库,如果是全表扫描,就用Hive+Hadoop,如果是索引访问,就用HBase+Hadoop;
3、Hive query就是MapReduce jobs可以从5分钟到数小时不止,HBase是非常高效的,肯定比Hive高效的多;
4、Hive本身不存储和计算数据,它完全依赖于 HDFS 和 MapReduce,Hive中的表纯逻辑;
5、hive借用hadoop的MapReduce来完成一些hive中的命令的执行;
6、hbase是物理表,不是逻辑表,提供一个超大的内存hash表,搜索引擎通过它来存储索引,方便查询操作;
7、hbase是列存储;
8、hdfs 作为底层存储,hdfs 是存放文件的系统,而 Hbase 负责组织文件;
9、hive 需要用到 hdfs 存储文件,需要用到 MapReduce 计算框架。
hbase实时查询原理
实时查询,可以认为是从内存中查询,一般响应时间在 1 秒内。HBase 的机制是数据先写入到内存中,当数据量达到一定的量(如 128M),再写入磁盘中, 在内存中,是不进行数据的更新或合并操作的,只增加数据,这使得用户的写操作只要进入内存中就可以立即返回,保证了 HBase I/O 的高性能。
描述hbase的rowkey的设计原理
- rowkey 长度原则 rowkey 是一个二进制码流,可以是任意字符串,最大长度 64kb,实际应用中一般为 10-100bytes,以 byte[] 形式保存,一般设计成定长。建议越短越好,不要超过 16 个字节, 原因如下:
数据的持久化文件 HFile 中是按照 KeyValue 存储的,如果 rowkey 过长会极大影响 HFile 的存储效率 MemStore 将缓存部分数据到内存,如果 rowkey 字段过长,内存的有效利用率就会降低,系统不能缓存更多的数据,这样会降低检索效率
-
rowkey 散列原则 如果 rowkey 按照时间戳的方式递增,不要将时间放在二进制码的前面,建议将 rowkey 的高位作为散列字段,由程序随机生成,低位放时间字段,这样将提高数据均衡分布在每个 RegionServer,以实现负载均衡的几率。如果没有散列字段,首字段直接是时间信息,所有的数据都会集中在一个 RegionServer 上,这样在数据检索的时候负载会集中在个别的 RegionServer 上,造成热点问题,会降低查询效率。
加盐:如果rowkey按照时间戳的方式递增,不要将时间放在二进制码的前面,建议将rowkey的高位作为散列字段,由程序随机生成,低位放时间字段,这样将提高数据均衡分布在每个RegionServer,以实现负载均衡的几率。如果没有散列字段,首字段直接是时间信息,所有的数据都会集中在一个RegionServer上,这样在数据检索的时候负载会集中在个别的RegionServer上,造成热点问题,会降低查询效率加盐:这里所说的加盐不是密码学中的加盐,而是在rowkey的前面增加随机数,具体就是给rowkey分配一个随机前缀以使得它和之前的rowkey的开头不同。分配的前缀种类数量应该和你想使用数据分散到不同的region的数量一致。加盐之后的rowkey就会根据随机生成的前缀分散到各个region上,以避免热点
哈希:哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群,但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的rowkey,可以使用get操作准确获取某一个行数据
反转:第三种防止热点的方法时反转固定长度或者数字格式的rowkey。这样可以使得rowkey中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机rowkey,但是牺牲了rowkey的有序性。反转rowkey的例子以手机号为rowkey,可以将手机号反转后的字符串作为rowkey,这样的就避免了以手机号那样比较固定开头导致热点问题
时间戳反转:一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为rowkey的一部分对这个问题十分有用,可以用Long.Max_Value - timestamp 追加到key的末尾,例如[key][reverse_timestamp] ,[key] 的最新值可以通过scan [key]获得[key]的第一条记录,因为HBase中rowkey是有序的,第一条记录是最后录入的数据。比如需要保存一个用户的操作记录,按照操作时间倒序排序,在设计rowkey的时候,可以这样设计[userId反转][Long.Max_Value - timestamp],在查询用户的所有操作记录数据的时候,直接指定反转后的userId,startRow是[userId反转][000000000000],stopRow是[userId反转][Long.Max_Value - timestamp]如果需要查询某段时间的操作记录,startRow是[user反转][Long.Max_Value - 起始时间],stopRow是[userId反转][Long.Max_Value - 结束时间]
-
rowkey 唯一原则 必须在设计上保证其唯一性,rowkey 是按照字典顺序排序存储的,因此, 设计 rowkey 的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。
描述 Hbase 中 scan 和 get 的功能以及实现的异同
按指 定 RowKey 获 取 唯 一 一 条 记 录 , get 方法( org.apache.hadoop.hbase.client.Get ) Get的方法处理分两种 : 设置了ClosestRowBefore和没有设置的 rowlock 主要是用来保证行的事务性,即每个get 是以一个 row 来标记的.一个 row 中可以有很多 family 和 column。
按指定的条件获取一批记录,scan 方法(org.apache.Hadoop.hbase.client.Scan)实现条件查询功能使用的就是 scan 方式
- scan 可以通过 setCaching 与 setBatch 方法提高速度(以空间换时间);
- scan 可以通过 setStartRow 与 setEndRow 来限定范围([start,end]start? 是闭区间,end 是开区间)。范围越小,性能越高;
- scan 可以通过 setFilter 方法添加过滤器,这也是分页、多条件查询的基础。 3.全表扫描,即直接扫描整张表中所有行记录。
请描述HBase中scan对象的setCache和setBatch方法的使用?
实现条件查询功能使用的就是scan方式,scan在使用时有以下几点值得注意: 1、scan可以通过setCaching与setBatch方法提高速度(以空间换时间); 2、scan可以通过setStartRow与setEndRow来限定范围([start,end)start是闭区间,end是开区间)。范围越小,性能越高。 通过巧妙的RowKey设计使我们批量获取记录集合中的元素挨在一起(应该在同一个Region下),可以在遍历结果时获得很好的性能。 3、scan可以通过setFilter方法添加过滤器,这也是分页、多条件查询的基础。
scan中的setCaching与setBatch方法的区别是什么呢?
setCaching设置的值为每次rpc的请求记录数,默认是1;cache大可以优化性能,但是太大了会花费很长的时间进行一次传输。 setBatch设置每次取的column size;有些row特别大,所以需要分开传给client,就是一次传一个row的几个column。 batch和caching和hbase table column size共同决意了rpc的次数。
setCache用于设置缓存,即设置一次RPC请求可以获取多行数据。对于缓存操作,如果行的数据量非常大,多行数据有可能超过客户端进程的内存容量,由此引入批量处理这一解决方案。
setBatch 用于设置批量处理,批量可以让用户选择每一次ResultScanner实例的next操作要取回多少列,例如,在扫描中设置setBatch(5),则一次next()返回的Result实例会包括5列。如果一行包括的列数超过了批量中设置的值,则可以将这一行分片,每次next操作返回一片,当一行的列数不能被批量中设置的值整除时,最后一次返回的Result实例会包含比较少的列,如,一行17列,batch设置为5,则一共返回4个Result实例,这4个实例中包括的列数分别为5、5、5、2。
组合使用扫描器缓存和批量大小,可以让用户方便地控制扫描一个范围内的行键所需要的RPC调用次数。Cache设置了服务器一次返回的行数,而Batch设置了服务器一次返回的列数。
假如我们建立了一张有两个列族的表,添加了10行数据,每个行的每个列族下有10列,这意味着整个表一共有200列(或单元格,因为每个列只有一个版本),其中每行有20列。
① Batch参数决定了一行数据分为几个Result,它只针对一行数据,Batch再大,也只能将一行的数据放入一个Result中。所以当一行数据有10列,而Batch为100时,也只能将一行的所有列都放入一个Result,不会混合其他行;
② 缓存值决定一次RPC返回几个Result,根据Batch划分的Result个数除以缓存个数可以得到RPC消息个数(之前定义缓存值决定一次返回的行数,这是不准确的,准确来说是决定一次RPC返回的Result个数,由于在引入Batch之前,一行封装为一个Result,因此定义缓存值决定一次返回的行数,但引入Batch后,更准确的说法是缓存值决定了一次RPC返回的Result个数);
RPC请求次数 = (行数 * 每行列数) / Min(每行的列数,批量大小) / 扫描器缓存
下图展示了缓存和批量两个参数如何联动,下图中有一个包含9行数据的表,每行都包含一些列。使用了一个缓存为6、批量大小为3的扫描器,需要三次RPC请求来传送数据:
简述 HBASE 中 compact 用途是什么,什么时候触发,分为哪两种,有什么区别,有哪些相关配置参数?
在 hbase 中每当有 memstore 数据 flush 到磁盘之后,就形成一个 storefile, 当 storeFile 的数量达到一定程度后,就需要将 storefile 文件来进行 compaction 操作。Compact 的作用
1、合并文件
2、清除过期,多余版本的数据
3、提高读写数据的效率
Minor 操作只用来做部分文件的合并操作以及包括 minVersion=0 并且设置 TTL 的过期版本清理,不做任何删除数据、多版本数据的清理工作;
Major 操作是对 Region 下的 HStore 下的所有 StoreFile 执行合并操作, 最终的结果是整理合并出一个文件
简述 Hbase filter 的实现原理是什么?结合实际项目经验,写出几个使用filter 的场景。
HBase 为筛选数据提供了一组过滤器,通过这个过滤器可以在 HBase 中的数据的多个维度(行,列,数据版本)上进行对数据的筛选操作,也就是说过滤器最终能够筛选的数据能够细化到具体的一个存储单元格上(由行键, 列名,时间戳定位)。
RowFilter、PrefixFilter。hbase 的 filter 是通过 scan 设置的,所以是基于 scan 的查询结果进行过滤. 过滤器的类型很多,但是可以分为两大类——比较过滤器,专用过滤器。过滤器的作用是在服务端判断数据是否满足条件,然后只将满足条件的数据返回给客户端;如在进行订单开发的时候,我们使用 rowkeyfilter 过滤出某个用户的所有订单。
Hbase 内部是什么机制?
在 HBase 中无论是增加新行还是修改已有的行,其内部流程都是相同的。HBase 接到命令后存下变化信息,或者写入失败抛出异常。默认情况下,执行写入时会写到两个地方:预写式日志(write-ahead log,也称 HLog)和 MemStore。HBase 的默认方式是把写入动作记录在这两个地方,以保证数据持久化。只有当这两个地方的变化信息都写入并确认后,才认为写动作完成。
MemStore 是内存里的写入缓冲区,HBase 中数据在永久写入硬盘之前在这里累积。当MemStore 填满后,其中的数据会刷写到硬盘,生成一个HFile。HFile 是HBase 使用的底层存储格式。HFile 对应于列族,一个列族可以有多个 HFile,但一个 HFile 不能存储多个列族的数据。在集群的每个节点上,每个列族有一个MemStore。大型分布式系统中硬件故障很常见,HBase 也不例外。
设想一下,如果MemStore 还没有刷写,服务器就崩溃了,内存中没有写入硬盘的数据就会丢失。HBase 的应对办法是在写动作完成之前先写入 WAL。HBase 集群中每台服务器维护一个 WAL 来记录发生的变化。WAL 是底层文件系统上的一个文件。直到WAL 新记录成功写入后,写动作才被认为成功完成。这可以保证 HBase 和支撑它的文件系统满足持久性。
大多数情况下,HBase 使用Hadoop分布式文件系统(HDFS)来作为底层文件系统。如果 HBase 服务器宕机,没有从 MemStore 里刷写到 HFile 的数据将可以通过回放 WAL 来恢复。你不需要手工执行。Hbase 的内部机制中有恢复流程部分来处理。每台 HBase 服务器有一个 WAL,这台服务器上的所有表(和它们的列族)共享这个 WAL。你可能想到,写入时跳过 WAL 应该会提升写性能。但我们不建议禁用 WAL, 除非你愿意在出问题时丢失数据。如果你想测试一下,如下代码可以禁用 WAL: 注意:不写入 WAL 会在 RegionServer 故障时增加丢失数据的风险。关闭 WAL, 出现故障时 HBase 可能无法恢复数据,没有刷写到硬盘的所有写入数据都会丢失。
HBase 宕机如何处理?
宕机分为 HMaster 宕机和 HRegisoner 宕机.
如果是 HRegisoner 宕机,HMaster 会将其所管理的 region 重新分布到其他活动的 RegionServer 上,由于数据和日志都持久在 HDFS 中,该操作不会导致数据丢失,所以数据的一致性和安全性是有保障的。
如果是 HMaster 宕机, HMaster 没有单点问题, HBase 中可以启动多个HMaster,通过 Zookeeper 的 Master Election 机制保证总有一个 Master 运行。即ZooKeeper 会保证总会有一个 HMaster 在对外提供服务。
ZooKeeper 会监控 HRegionServer 的上下线情况,当 ZK 发现某个 HRegionServer 宕机之后会通知 HMaster 进行失效备援;
HRegionServer 会停止对外提供服务,就是它所负责的 region 暂时停止对外提供服务
HMaster 会将该 HRegionServer 所负责的 region 转移到其他 HRegionServer 上,并且会对 HRegionServer 上存在 memstore 中还未持久化到磁盘中的数据进行恢复;
这个恢复的工作是由 WAL重播 来完成,这个过程如下:
1. wal实际上就是一个文件,存在/hbase/WAL/对应RegionServer路径下
2. 宕机发生时,读取该RegionServer所对应的路径下的wal文件,然后根据不同的region切分成不同的临时文件recover.edits
3. 当region被分配到新的RegionServer中,RegionServer读取region时会进行是否存在recover.edits,如果有则进行恢复
为什么不建议在 HBase 中使用过多的列族
在 Hbase 的表中,每个列族对应 Region 中的一个Store,Region的大小达到阈值时会分裂,因此如果表中有多个列族,则可能出现以下现象:
一个Region中有多个Store,如果每个CF的数据量分布不均匀时,比如CF1为100万,CF2为1万,则Region分裂时导致CF2在每个Region中的数据量太少,查询CF2时会横跨多个Region导致效率降低。 如果每个CF的数据分布均匀,比如CF1有50万,CF2有50万,CF3有50万,则Region分裂时导致每个CF在Region的数据量偏少,查询某个CF时会导致横跨多个Region的概率增大。 多个CF代表有多个Store,也就是说有多个MemStore(2MB),也就导致内存的消耗量增大,使用效率下降。 Region 中的 缓存刷新 和 压缩 是基本操作,即一个CF出现缓存刷新或压缩操作,其它CF也会同时做一样的操作,当列族太多时就会导致IO频繁的问题。
Hbase热点(数据倾斜)问题 读写请求会集中到某一个RegionServer上 如何处理
讲一下Hbase,Hbase二级索引用过吗
默认情况下,Hbase只支持rowkey的查询,对于多条件的组合查询的应用场景,不够给力。
如果将多条件组合查询的字段都拼接在RowKey中显然又不太可能
全表扫描再结合过滤器筛选出目标数据(太低效),所以通过设计HBase的二级索引来解决这个问题。
这里所谓的二级索引其实就是创建新的表,并建立各列值(family:column)与行键(rowkey)之间的映射关系。这种方式需要额外的存储空间,属于一种以空间换时间的方式
请列举几个HBase优化方法
1)减少调整
减少调整这个如何理解呢?HBase中有几个内容会动态调整,如region(分区)、HFile,所以通过一些方法来减少这些会带来I/O开销的调整。
① Region
如果没有预建分区的话,那么随着region中条数的增加,region会进行分裂,这将增加I/O开销,所以解决方法就是根据你的RowKey设计来进行预建分区,减少region的动态分裂。
② HFile
HFile是数据底层存储文件,在每个memstore进行刷新时会生成一个HFile,当HFile增加到一定程度时,会将属于一个region的HFile进行合并,这个步骤会带来开销但不可避免,但是合并后HFile大小如果大于设定的值,那么HFile会重新分裂。为了减少这样的无谓的I/O开销,建议估计项目数据量大小,给HFile设定一个合适的值。
2)减少启停
数据库事务机制就是为了更好地实现批量写入,较少数据库的开启关闭带来的开销,那么HBase中也存在频繁开启关闭带来的问题。
① 关闭Compaction,在闲时进行手动Compaction。
因为HBase中存在Minor Compaction和Major Compaction,也就是对HFile进行合并,所谓合并就是I/O读写,大量的HFile进行肯定会带来I/O开销,甚至是I/O风暴,所以为了避免这种不受控制的意外发生,建议关闭自动Compaction,在闲时进行compaction。
② 批量数据写入时采用BulkLoad。
如果通过HBase-Shell或者JavaAPI的put来实现大量数据的写入,那么性能差是肯定并且还可能带来一些意想不到的问题,所以当需要写入大量离线数据时建议使用BulkLoad。
3)减少数据量
虽然我们是在进行大数据开发,但是如果可以通过某些方式在保证数据准确性同时减少数据量,何乐而不为呢?
① 开启过滤,提高查询速度
开启BloomFilter,BloomFilter是列族级别的过滤,在生成一个StoreFile同时会生成一个MetaBlock,用于查询时过滤数据 ② 使用压缩
一般推荐使用Snappy和LZO压缩
4)合理设计
在一张HBase表格中RowKey和ColumnFamily的设计是非常重要,好的设计能够提高性能和保证数据的准确性
① RowKey设计:应该具备以下几个属性
散列性:散列性能够保证相同相似的rowkey聚合,相异的rowkey分散,有利于查询。
简短性:rowkey作为key的一部分存储在HFile中,如果为了可读性将rowKey设计得过长,那么将会增加存储压力。
唯一性:rowKey必须具备明显的区别性。
业务性:举例来说:
假如我的查询条件比较多,而且不是针对列的条件,那么rowKey的设计就应该支持多条件查询。
如果我的查询要求是最近插入的数据优先,那么rowKey则可以采用叫上Long.Max-时间戳的方式,这样rowKey就是递减排列。
② 列族的设计:列族的设计需要看应用场景
优势:HBase中数据时按列进行存储的,那么查询某一列族的某一列时就不需要全盘扫描,只需要扫描某一列族,减少了读I/O;其实多列族设计对减少的作用不是很明显,适用于读多写少的场景
劣势:降低了写的I/O性能。原因如下:数据写到store以后是先缓存在memstore中,同一个region中存在多个列族则存在多个store,每个store都一个memstore,当其实memstore进行flush时,属于同一个region的store中的memstore都会进行flush,增加I/O开销。
Hbase中的memstore是用来做什么的?
hbase为了保证随机读取的性能,所以hfile里面的rowkey是有序的。当客户端的请求在到达regionserver之后,为了保证写入rowkey的有序性,所以不能将数据立刻写入到hfile中,而是将每个变更操作保存在内存中,也就是memstore中。memstore能够很方便的支持操作的随机插入,并保证所有的操作在内存中是有序的。当memstore达到一定的量之后,会将memstore里面的数据flush到hfile中,这样能充分利用hadoop写入大文件的性能优势,提高写入性能。
由于memstore是存放在内存中,如果regionserver因为某种原因死了,会导致内存中数据丢失。所有为了保证数据不丢失,hbase将更新操作在写入memstore之前会写入到一个write ahead log(WAL)中。WAL文件是追加、顺序写入的,WAL每个regionserver只有一个,同一个regionserver上所有region写入同一个的WAL文件。这样当某个regionserver失败时,可以通过WAL文件,将所有的操作顺序重新加载到memstore中。
如何提高HBase客户端的读写性能?请举例说明
① 开启bloomfilter过滤器,开启bloomfilter比没开启要快3、4倍
② Hbase对于内存有特别的需求,在硬件允许的情况下配足够多的内存给它
③ 通过修改hbase-env.sh中的 export HBASE_HEAPSIZE=3000 #这里默认为1000m
④ 增大RPC数量
通过修改hbase-site.xml中的hbase.regionserver.handler.count属性,可以适当的放大RPC数量,默认值为10有点小。
rowkey问题
region中的rowkey是有序存储,若时间比较集中。就会存储到一个region中,这样一个region的数据变多,其它的region数据很少,加载数据就会很慢,直到region分裂,此问题才会得到缓解。
请描述如何解决HBase中region太小和region太大带来的冲突?( ☆☆☆☆☆)
Region过大会发生多次compaction,将数据读一遍并重写一遍到hdfs 上,占用io,region过小会造成多次split,region 会下线,影响访问服务,最佳的解决方法是调整hbase.hregion.
二级索引设计
HBase二级索引的设计 涉及到多个条件组合查询时,hbase就有点力不从心了,因为hbase支持的是三种方式,上面已经提到了,对于多条件组合查询来说,如果通过某个条件filter出来一部分数据,然后再一个个判断,这样比较影响性能。但又不能将 这些条件都设计到rowkey中去,这时可以考虑到用二级索引。 二级索引的实现:设计两个列族,一个列族什么数据都不存,存放索引的key就对应这个列族,另一个列族就对应主数据,主数据的rowkey就是之前业务正常设计好的,现在通过二级索引来满足多条件组合查询。数据还是和之前不变,只是多用一些资源来存放索引的rowkey。先通过多条件找到二级索引的rowkey,然后再截取到最终查询主数据的rowkey,这样就可以实现多条件组合查询了。 其实也可以将这些组合条件放入mysql,通过mysql找到查询主数据的rowkey,然后用这个rowkey去hbase中查询,得到最终结果。 www.cnblogs.com/MOBIN/p/557…
hbase缓存
hbase的缓存由两部分组成,一部分是memstore,一部分是blockcache。一个regionServer对应多个region和一个blockcache,每个region对应一个memstore,写入数据的时候,是先写预写日志,上文已经说了,然后再写入memstore,当memstore达到阈值,将数据写入hfile中。读取数据的时候先到memstore中找,没有的话再去blockcache中找,最后再去hfile中读取。这里就有一个LRU算法问题,确认是blockcache是经常查询的数据。 blog.csdn.net/yx_keith/ar…
调优
1、Scan 缓存
如果HBase的输入源是一个MapReduce Job,要确保输入的Scan的setCaching值要比默认值0要大。使用默认值就意味着map-task每一行都会去请求一下region-server。可以把这个值设为500,这样就可以一次传输500行。当然这也是需要权衡的,过大的值会同时消耗客户端和服务端很大的内存,不是越大越好。
2、Scan 属性选择
当Scan用来处理大量的行的时候(尤其是作为MapReduce的输入),要注意的是选择了什么字段。如果调用了 scan.addFamily,这个列族的所有属性都会返回。如果只是想过滤其中的一小部分,就指定那几个column,否则就会造成很大浪费,影响性能。
3、关闭 ResultScanners
这与其说是提高性能,倒不如说是避免发生性能问题。如果你忘记了关闭ResultScanners,会导致RegionServer出现问题。所以一定要把ResultScanner包含在try/catch 块中…
4、块缓存
Scan实例可以在RegionServer中使用块缓存,可以由setCacheBlocks方法控制。如果Scan是MapReduce的输入源,要将这个值设置为 false。对于经常读到的行,就建议使用块缓冲。
5、行键的负载优化
当scan一个表的时候, 如果仅仅需要行键(不需要no families, qualifiers, values 和 timestamps),在加入FilterList的时候,要使用Scanner的setFilter方法的时候,要填上MUST_PASS_ALL操作参数(译者注:相当于And操作符)。一个FilterList要包含一个 FirstKeyOnlyFilter 和一个 KeyOnlyFilter.通过这样的filter组合,就算在最坏的情况下,RegionServer只会从磁盘读一个值,同时最小化客户端的网络带宽占用。
6、表创建: 延迟log刷写
Puts的缺省行为使用 Write Ahead Log (WAL),会导致 HLog 编辑立即写盘。如果采用延迟刷写,WAL编辑会保留在内存中,直到刷写周期来临。好处是集中和异步写HLog,潜在问题是如果RegionServer退出,没有刷写的日志将丢失。但这也比Puts时不使用WAL安全多了。延迟log刷写可以通过 HTableDescriptor 在表上设置,hbase.regionserver.optionallogflushinterval缺省值是1000ms。
7、HBase 客户端: 自动刷写
当你进行大量的Put的时候,要确认你的HTable的setAutoFlush是关闭着的。否则的话,每执行一个Put就要想区域服务器发一个请求。通过 htable.add(Put) 和 htable.add( Put)来将Put添加到写缓冲中。如果 autoFlush = false,要等到写缓冲都填满的时候才会发起请求。要想显式的发起请求,可以调用flushCommits。在HTable实例上进行的close操作也会发起flushCommits
hbase 预分区
每一个region维护着startRow与endRowKey,如果加入的数据符合某个region维护的rowKey范围,则该数据交给这个region维护。那么依照这个原则,我们可以将数据所要投放的分区提前大致的规划好,以提高HBase性能。
预分区是否可以基于时间戳作为ROWKEY?
不推荐基于时间戳来作为ROWKEY,其原因很简单,很容易出现数据倾斜。随着数据量的增大,Region也会被切分,而基于时间戳切分的Region有一个明显特点,前面的Region的数据固定,后面的Region数量不断有数据写入导致数据倾斜现象,从而频繁发生Region的切分。
如何规划预分区?
这需要您对数据的总量有一个判断,举个例子,目前HBase有50亿条数据,每条数据平均大小是10k,那么数据总量大小为5000000000*10k/1024/1024=47683G(约47T).我们假设每个Region可以存储100G的数据,那么要存储到现有数据只需要477个Region足以。
但是这个Region数量仅能存储现有数据,实际情况我们需要预估公司未来5-10年的数量量的增长,比如每年增长10亿条数据,按5年来做预估,那么5年后的数据会增加50亿条数据,因此预估5年后总数居大小为10000000000*10k/1024/1024=95367(约95T)。
综上所述,按照每个Region存储100G的数据,最少需要954个Region,接下来我们就得考虑如何将数据尽量平均的存储在这954个Region中,而不是将数据放在某一个Region中导致数据倾斜的情况发生。