Photo By Melody
HBase是构建在HDFS之上的、支持随机插入和删除的列簇式Key-Value存储系统。良好的rowkey设计可以提高HBase读写性能,只有在理解了HBase的数据模型后,才知道从哪几个维度思考这个问题,因此本文首先介绍HBase的数据模型,包括逻辑模型和物理模型,然后才讨论rowkey设计优化方案。
逻辑模型
从逻辑层看,HBase表由多个行构成,每行有一个rowkey,以及多个column family,每个column family包含多个列。
rowkey:rowkey类似RDBMS的主键,唯一标识该行,是定位该行的索引column family:同一column family中的数据物理上存储在一个文件中column qualifier:列标识,表的每列数据可通过family:qualifier唯一标识cell:通过rowkey、family和qualifier可唯一定位一个cell,一个cell内部根据时间戳保存多个版本timestamp:cell内部数据是多版本的,时间戳作为版本号,默认最大保留三个版本,如果读取数据时未指定版本号,只会返回最新版本的值在把HBase看做一个key-value存储系统时,可以把rowkey当做key,其他部分当做value,也可以将[rowkey,column family,coluomn qualifier,timestamp]看做key,Cell中的值作为value。
物理模型
我们说HBase是一个列式存储数据库,其实际上是一个列簇式存储数据库,同一列簇中的数据会单独存储,但列簇内数据是行式存储。即以column famliy为单位存储数据,每个column family以key-value格式保存,key value组成结构:[rowkey,column family,column qualifier,timestamp] =>value。
从存储结构上可知,每行数据中不同版本的cell value会重复保存rowkey,column family,column qualifier,数据在进行持久化到HFile文件时、读缓存(BlockCache)、写缓存(MemStore)都会用这几个字段,为了节省存储空间、内存空间和网络传输数据量,这几个字段值应尽可能短。
HBase 表中的数据是按照rowkey数据字典顺序升序排列,而且是按照rowkey的范围将数据划分成多个region(类似于分区),根据算法,region会被均匀的分布到RegionServer上。
结合以上知识,rowkey既要方便查找和定位,又要保证数据均衡分布在RegionServer上(分区均匀),避免数据热点、数据倾斜。可行的rowkey优化方式有下面几种方式:
规整化rowkey
有时作为rowkey的字段长度不一样,比如user_id,而通过对rowkey进行规整化,能够避免rowkey长度不一致,导致每次请求返回的数据量不一,可将组合的rowkey映射成等长hash值。
编码rowkey
如果rowkey是以字符串形式保存,比如日期格式(yyyy-MM-dd HH:mm:ss),会造成大量存储空间的浪费,因此可以对字符串进行数值编码,将编码后的数保存到rowkey中,对不同数据类型有不同的编码方式,后续会专门开篇讨论编码问题。
高基维度
如果rowkey由多个字段组成,需要把高基维度放到最前面,也就是distinct的字段数量在千万以上,比如user_id放到前面,这样字段能在过滤中起到很大作用、大幅缩小查询范围
加盐
如果组合rowkey的第一部分是时间戳,HBase又是按照rowkey排序的,很可能邻近的数据存到一个RegionServer里,考虑到最新的数据访问频率最高,将导致某个RegionServer读请求负载过重,产生热点问题,一个可行的方案是rowkey前缀加随机数,这可以保证数据均匀分布,但对数据读取造成了麻烦。
和加盐类似的方案有对rowkey进行哈希、翻转rowkey(经常改变的部分放到前面)等。
结束语:矛和盾
一方面我们要将经常读取的数据存储到一块、将最近可能会被访问的数据放到一起,提高范围扫描效率;另一方面,如果这样存储,这些数据一般都会集中在一个RegionServer上,导致负载集中在个别节点上,造成热点问题,又会降低查询效率。rowkey的设计就要在读写性能、数据热点、范围扫描之间进行取舍的艺术。