一. Hbase读流程
- META表记录着表的原信息,根据rowkey查询META表,获取所在region信息
- 客户端去相应的regionServer查询数据,先查询memStore(memstore是一个按key排序的树形结构缓冲区),有就返回,没有继续查找
- 查询regionServer的读缓存BlockCache是否存在rowkey的对应数据,有就返回,没有就继续查询。每次get一次时,hbase把相邻的一段数据也放到内存中缓存起来,方便顺序读
- 查询HFile中是否有相应记录
(1)hfile的block索引会放在内存中,block是一段氛围内的键值对,通过rowkey找到block
(2)下载hfile查找记录
二. 写流程
- client向region server提交写请求
- region server找到目标region
- region检查数据是否与schema一致
- 如果客户端没有指定版本,则获取当前系统时间作为数据版本
- 将更新写入WAL log (hlog)
- 将更新写入Memstore
- 判断Memstore的是否需要flush为Store文件。
每 一个put的操作实际上是RPC的操作,它将客户端的数据传送到服务器然后返回,这只适合小数据量的操作,如果有个应用程序需要每秒存储上千行数据到 HBase表中,这样处理就不太合适了。HBase的API配备了一个客户端的写缓冲区,缓冲区负责收集put操作,然后调用RPC操作一次性将put送 往服务器。默认情况下,客户端缓冲区是禁止的。可以通过自动刷写设置为FALSE来激活缓冲区。 table.setAutoFlush(false);void flushCommits () throws IOException这个方法是强制 将数据写到服务器。用户还可以根据下面的方法来配置客户端写缓冲区的大小。 void setWritaeBufferSize(long writeBufferSize) throws IOException;默认大小是 2MB,这个也是适中的,一般用户插入的数据不大,不过如果你插入的数据大的话,可能要考虑增大这个值。从而允许客户端更高效地一定数量的数据组成一组通 过一次RPC请求来执行。给每个用户的HTable设置一个写缓冲区也是一件麻烦的事,为了避免麻烦,用户可以在
三.Hbase优化
-
行健设计
(1)使用复合行健 (使用更多的条件查询) eg: 上述问题采用( 手机号倒置 : 时间戳倒置 )
(2)行健要尽量离散而不是连续 , 使得连续插入的记录分散到多个HregionServer中 , 查询时达到并发查
(3)尽量保证行健的数据长度短 , 减少Hfile加载到内存的内存空间表存储数据,按照行健存储,划分region时,也是按照region划分
热点问题 : hbase的查询插入集中在某几个region中,不能分散.
(时间戳 手机号) 存储
eg: 不能用时间戳作为行健, 时间戳单调递增, 添加数据时会全部写在一个region, 降低并发查询添加效率
可以把手机号倒置作为行健, 因为手机号末尾数字1-9等概率,可以做到分散存储 -
布隆过滤器
(用一个数组,数组的每个元素的值是0或1,表示该位置是否存在数据, 数组角标是被判断集合元素值的hash值与数组长度取模)
用极小的空间判断一个元素不在一个集合中
(1)极小空间 : 一个位数组
(2)判断不在一个集合 : 这个位数组, 元素值只是0/1表示该位是否有元素; 元素哈希值对维数组长度取模
(3)把位数组模位置上的元素置1 . 当查询时发现该位置为0说明元素不在集合中 . 这种过滤器不能100%确定元素的存在, 但是能发现元素不在集合中【注】:客户端查数据,先查到数据属于哪个region,根据region找到hfile ,结合block的索引和hfile在磁盘中找到block块, 再根据行健,列祖,列标识符找到block中的kv键值对. hbase把查到的数据缓存在内存中. 使用bloom过滤器. 在查找结果缓存阶段,可以快速判断要查找的键值对是否已经在内存中
可以设置对每个行健设置bloom过滤器. 也可以对每个列设置bloom过滤器 -
列族的块
(1)HFile读取数据的最小单位是block , 这个block默认大小是64k , 查找某一个值时 , 要遍历这个块中的所有<k,v>
(2)如果存在大量随机读 , block块小一点好
(3)如果存在大量顺序读 , block块大一点好 -
设置autoFlush=false