HBase RowKey设计

2020-09-23 16:16发布

2条回答
milkmilk
2楼 · 2020-09-23 16:22

与NoSQL数据库一样,RowKey是用来检索记录的主键。访问HBASE table中的行,只有三种方式:

1.通过单个RowKey访问  get 'student','1001'

2.通过RowKey的range    scan 'student',{STARTROW=>'1001',STOPROW=>'1003'}

3.全表扫描 :RowKey行键 (RowKey)可以是任意字符串(最大长度是64KB,实际应用中长度一般为 10-100bytes),在HBase内部,RowKey保存为字节数组。存储时,数据按照RowKey的字典序(byte order)排序存储。设计RowKey时,要充分利用排序存储这个特性,将经常一起读取的行存储放到一起。(位置相关性)


我的网名不再改
3楼 · 2020-09-27 11:43

1. HBase的存储形式

hbase的内部使用KeyValue的形式存储,其key时rowKey:family:column:logTime,value是其存储的内容。

其在region内大多以升序的形式排列,唯一的时logTime是以降序的形式进行排列。

所以,rowKey里越靠近左边的信息越容易被检索到。其设计时,要考虑把重要的信息放左边,不重要的信息放到右边。这样可以提高查询数据的速度。最重要的提高索引速度的就是设计合适的rowKey。

在做RowKey设计时,请先考虑业务是读比写多,还是读比写少,HBase本身是为写优化的,即便是这样,也可能会出现热点问题,而如果我们读比较多的话,除了考虑以上RowKey设计原则外,还可以考虑HBase的Coprocessor甚至elasticSearch结合的方法,无论哪种方式,都建议做实际业务场景下数据的压力测试以得到最优结果。

2. RowKey的设计原则

2.1 长度原则

rowKey是一个二进制,RowKey的长度被很多开发者建议说设计在10~100个字节,以byte[]形式保存,最大不能超过64kb建议越短越好,不要超过16个字节

太长的影响有几点点:

  • 一是HBase的持久化文件HFile是按照KeyValue存储的,如果RowKey过长,比如说500个字节,1000万列数据,光是RowKey就要占用500*1000万=50亿个字节,将近1G数据,极大影响了HFile的存储效率。

  • 二是缓存MemStore缓存部分数据到内存中,如果RowKey字段过长,内存的有效利用率会降低,系统无法缓存更多的数据,降低检索效率。

  • 目前操作系统都是64位系统,内存8字节对齐,控制在16字节,8字节的整数倍利用了操作系统的最佳特性。

注意:不仅RowKey的长度是越短越好,而且列簇名、列名等尽量使用短名字,因为HBase属于列式数据库,这些名字都是会写入到HBase的持久化文件HFile中去,过长的RowKey、列簇、列名都会导致整体的存储量成倍增加。

2.2 唯一原则

保证rowKey的唯一性。由于在HBase中数据存储是Key-Value形式,若HBase中同一表插入相同RowKey,则原先的数据会被覆盖掉(如果表的version设置为1的话)。

2.3 散列原则

设计的RowKey应均匀分布在各个HBase节点上。如RowKey是按系统时间戳的方式递增,RowKey的第一部分如果是时间戳的话,将造成所有新数据都在一个RegionServer堆积的热点现象,也就是通常说的Region热点问题,热点发生在大量的client直接访问集中在个别RegionServer上(访问可能是读、写或者其他操作),导致单个RegionServer机器自身负载过高,引起性能下降甚至Region不可用,常见的是发生jvm full gc或者显示region too busy异常情况。

3. 在不同访问模式下设计行健

3.1 为写优化(解决热点问题)

当往HBase表写入大量数据时,需要在RegionServer上分散负载来进行优化。这并不难,但是你可能不得不在读模式优化上付出代价。比如,时间序列数据的例子,如果你的数据直接使用时间戳做行健,在写入时在单个region上会遇到热点问题。

许多使用场景下,并不需要基于单个时间戳访问数据。你可能要运行一个作业在一个时间区间上做聚合计算,如果对时间延迟不敏感,可以考虑跨多个region做并行扫描来完成任务。但问题是,应该如何把数据分散在多个region上呢?有几个选项可以考虑,答案取决于你想让行健包含什么信息。

  1. 散列 。 如果你愿意在行健里放弃时间戳信息(每次你做什么事情都要扫描全表,或者每次要读数据时你都知道精确的键,这些情况下也是可行的),使用原始数据的散列值作为行健是一种可能的解决方案:

          

           每次当你需要访问以这个散列值为键的行时,需要精确知道“TheRealMT”。时间序列数据一般不这样处理。当你访问数据时,可能记住了一个时间范围,但不大可能知道精确的时间戳。但是有些情况下,能够计算散列值从而找到正确的行。为了得到一种跨所有region的、优秀的分布策略,你可以使用MD5、SHA-1或者其他提供随机分布的散列数。

        2.salting。当你思考行健的构成时,salting是另一种技巧。让我们考虑之前的时间序列数据例子。假设你在读取时知道时间范围,但不想做全表扫描。对时间戳做散列运算然后把散列值作为行健的做法需要做全表扫描,这是很低效的,尤其是在你有办法限制扫描范围的时候。使用散列值作为行健在这里不是办法,但是你可以在时间戳前面加上一个随机数前缀。

         例如,你可以先计算时间戳的散列码,然后用RegionServer的数量取模来生成随机salt数:

         

           取到salt数后,加到时间戳的前面生成行健:

           

   现在行健如下所示:

          

   你可以想到,这些行将会基于键的第一部分,也就是随机salt数,分布在各个region。

   0|timestamp1,0|timestamp5和0|timestamp6将进入一个region,除非发生region拆分(拆分的情况下会分散到两个region)。1|timestamp2,1|timestamp9进入另一个不同的region,2|timestamp4,2|timestamp8进入第三个region。连续时间戳的数据散列进入了多个region。

   但并非一切都是完美的。现在读操作需要把扫描命令分散到所有region上来查找相应的行。因为它们不再存储在一起,所以一个短扫描不能解决问题了。这是一种权衡,为了搭建成功的应用你需要做出选择。这是一个利用信息的位置来获得跨region分布的经典例子。

           3. Reverse反转。针对固定长度的RowKey反转后存储,这样可以使RowKey中经常改变的部分放在最前面,可以有效的随机RowKey。反转RowKey的例子通常以手机举例,可以将手机号反转后的字符串作为RowKey,这样就避免了以手机号那样比较固定开头导致热点问题。这样做的缺点是牺牲了RowKey的有序性。

3.2 为读优化

           时间戳反转。一个常见的数据处理问题是快速获取数据的最新版本,使用反转的时间戳作为RowKey的一部分对这个问题十分有用,可以用Long.Max_Value - timestamp追加到key的末尾。举例,在设计推帖流表时,你的焦点是为读优化行健,目的是把推帖流里最新的推帖存储在一起,以便于它们可以被快速读取,而不用做开销很大的硬盘搜索。在推贴流表里,你使用倒序时间戳(Long.MAX_VALUE - 时间戳)然后附加上用户ID来构成行健。现在你基于用户ID扫描紧邻的n行就可以找到用户需要的n条最新推帖。这里行健的结构对于读性能很重要。把用户ID放在开头有助于你设置扫描,可以轻松定义起始键。

4. HBase的RowKey设计应用实例

4.1 设计订单状态表

设计模式:反转+时间戳反转

RowKey:reverser(order_id) + (Long.MAX_VALUE - timestamp)

这样设计的好处一是通过reverse订单号避免Region热点,二是可以按时间倒排显示,可以获取到最新的订单。

同样适用于需要保存一个用户的操作记录,按照操作时间倒序排序。设计的rowKey为:reverser(userId) + (Long.MAX_VALUE - timestamp)。如果需要查询某段时间的操作记录,startRow是[userId反转][Long.MAX_VALUE - 结束时间],stopRow是[userId反转][Long.MAX_VALUE - 起始时间]。

4.2 登录、下单等等统称事件(event)的临时存储

HBase只存储了最近10分钟的热数据

设计模式:salt加盐

RowKey:两位随机数Salt + eventId + Date + kafka的Offset

这样设计的好处是:设计加盐的目的是为了增加查询的并发性,假如Salt的范围是0~n,那我们在查询的时候,可以将数据分为n个split同时做scan操作。经过我们的多次测试验证,增加并发度能够将整体的查询速度提升5~20倍以上。随后的eventId和Date是用来做范围Scan来使用的。在我们的查询场景中,大部分都是指定了eventId的,因此我们在eventId放在了第二个位置上,同时呢,通过Salt + eventId的方式可以保证不会形成热点。把date放在RowKey的第三个位置上可以实现date做scan,批量Scan性能甚至可以做到毫秒级返回。

这样的RowKey设计能够很好的支持如下几个查询场景:

  1. 全表scan。在这种情况下,我们仍然可以将全表数据切分成n份并发查询,从而实现查询的实时响应。

  2. 只按照event_id查询。

  3. 按照event_id和date查询。

5. HBase索引设计

数据库查询可简单分解为两个步骤:1)键的查找;2) 数据的查找

因这两种数据组织方式的不同,在RDBMS领域有两种常见的数据组织表结构:

索引组织表:键与数据存放在一起,查找到键所在的位置则意味着查找到数据本身。

堆表:键的存储与数据的存储是分离的。查找到键的位置,只能获取到数据的物理地址,还需要基于该地址去获取数据。

HBase数据表其实是一种索引组织表结构:查找到RowKey所在的位置则意味着找到数据本身。因此,RowKey本身就是一种索引

5.1 RowKey查询的局限性/二级索引需求背景

如果提供的查询条件能够尽可能丰富的描述RowKey的前缀信息,则查询时延越能得到保障。如下面几种组合条件场景:

  * Name + Phone + ID
  * Name + Phone
       * Name

如果查询条件不能提供Name信息,则RowKey的前缀条件是无法确定的,此时只能通过全表扫描的方式来查找结果。

一种业务模型的用户数据RowKey,只能采用单一结构设计。但事实上,查询场景可能是多纬度的。例如,在上面的场景基础上,还需要单独基于Phone列进行查询。这是HBase二级索引出现的背景。即,二级索引是为了让HBase能够提供更多纬度的查询能力。

注:HBase原生并不支持二级索引方案,但基于HBase的KeyValue数据模型与API,可以轻易的构建出二级索引数据。Phoenix提供了两种索引方案,而一些大厂家也都提供了自己的二级索引实现。

5.2 HBase 二级索引方案

5.2.1 基于Coprocessor方案

从0.94版本,HBase官方文档已经提出了HBase上面实现二级索引的一种路径:

  • 基于Coprocessor(0.92版本引入,达到支持类似传统RDBMS的触发器的行为)。

  • 开发自定义数据处理逻辑,采用数据“双写”策略,在有数据写入同时同步到二级索引表。

5.2.1.1 开源方案:

业界比较知名的基于Coprocessor的开源方案:

  • 华为的hindex:基于0.94版本,但版本比较旧,github上几年都没更新过。

  • Apache Phoenix:功能围绕SQL On HBase,支持和兼容多个hbase版本,二级索引只是其中一块功能。二级索引的创建和管理直接有SQL语法支持,适用起来简便,该项目目前社区活跃度和版本更新迭代情况都比较好。

Apache Phoenix在目前开源的方案中,是一个比较优的选择,主打SQL On HBase,基于SQL能完成HBase的CRUD操作,支持JDBC协议。

5.2.1.2 Phoenix二级索引特点:

  • Covered Indexes(覆盖索引):把关注的数据字段也附在索引表上,只需要通过索引表就能返回所要查询的数据(列),所以索引的列必须包含所需查询的列(SELECT的列和WHERE的列)。

  • Functional Indexes(函数索引):索引不局限于列,支持任意的表达式来创建索引。

  • Global Indexes(全局索引):适用于读多写少场景。通过维护全局索引表,所有的更新和写操作都会引起索引的更新,写入性能受到影响。在读数据时,Phoenix SQL会基于索引字段,执行快速查询。

  • Local Indexes(本地索引):适用于写多读少场景。在数据写入时,索引数据和表数据都会存储在本地。在数据读取时,由于无法预先确定region的位置,所以在读取数据时需要检查每个region(以找到索引数据),会带来一定性能(网络)开销。

5.2.2 非Coprocessor方案

选择不基于Coprocessor开发,自行在外部构建和维护索引关系也是另外一种方式。

常见的是采用底层基于Apache Lucene的ElasticSearch(下面简称ES)或Apache Solr,来构建强大的索引能力、搜索能力,例如支持模糊查询、全文检索、组合查询、排序等。

其实对于在外部自定义构建二级索引的方式,有自己的大数据团队的公司一般都会针对自己的业务场景进行优化,自行构建ES/Solr的搜索集群。例如数说故事企业内部的百亿级数据全量库,就是基于ES构建海量索引和检索能力的案例。主要有优化点包括:

  • 对企业的索引集群面向的业务场景和模式定制,对通用数据模型进行抽象和平台话复用

  • 需要针对多业务、多项目场景进行ES集群资源的合理划分和运维管理

  • 查询需要针对多索引集群、跨集群查询进行优化

  • 共用集群场景需要做好防护、监控、限流

下面显示了数说基于ES做二级索引的两种构建流程,包含:

  • 增量索引:日常持续接入的数据源,进行增量的索引更新

  • 全量索引:配套基于Spark/MR的批量索引创建/更新程序,用于初次或重建已有HBase库表的索引。

数据查询流程:

6. HBase表设计关注点

HBase表设计通常可以是宽表(wide table)模式,即一行包括很多列。同样的信息也可以用高表(tall table)形式存储,通常高表的性能比宽表要高出50%以上,所以推荐大家使用高表来完成表设计。表设计时,我们也应该要考虑HBase数据库的一些特性:

  1. 在HBase表中是通过RowKey的字典序来进行数据排序的。

  2. 所有存储在HBase表中的数据都是二进制的字节。

  3. 原子性只在行内保证,HBase不支持跨行事务。

  4. 列簇(Column Family)在表创建之前就要定义好

  5. 列簇中的列标识(Column Qualifier)可以在表创建完以后动态插入数据时添加。

总结


相关问题推荐

  • 什么是大数据时代?2021-01-13 21:23
    回答 100

    大数据(big data)一词越来越多地被提及,人们用它来描述和定义信息爆炸时代产生的海量数据,而这个海量数据的时代则被称为大数据时代。随着云时代的来临,大数据(Big data)也吸引了越来越多的关注。大数据(Big data)通常用来形容一个公司创造的大量非结...

  • 回答 84

    Java和大数据的关系:Java是计算机的一门编程语言;可以用来做很多工作,大数据开发属于其中一种;大数据属于互联网方向,就像现在建立在大数据基础上的AI方向一样,他两不是一个同类,但是属于包含和被包含的关系;Java可以用来做大数据工作,大数据开发或者...

  • 回答 52
    已采纳

    学完大数据可以从事很多工作,比如说:hadoop 研发工程师、大数据研发工程师、大数据分析工程师、数据库工程师、hadoop运维工程师、大数据运维工程师、java大数据工程师、spark工程师等等都是我们可以从事的工作岗位!不同的岗位,所具备的技术知识也是不一样...

  • 回答 29

    简言之,大数据是指大数据集,这些数据集经过计算分析可以用于揭示某个方面相关的模式和趋势。大数据技术的战略意义不在于掌握庞大的数据信息,而在于对这些含有意义的数据进行专业化处理。大数据的特点:数据量大、数据种类多、 要求实时性强、数据所蕴藏的...

  • 回答 14

    tail -f的时候,发现一个奇怪的现象,首先 我在一个窗口中 tail -f test.txt 然后在另一个窗口中用vim编辑这个文件,增加了几行字符,并保存,这个时候发现第一个窗口中并没有变化,没有将最新的内容显示出来。tail -F,重复上面的实验过程, 发现这次有变化了...

  • 回答 18

    您好针对您的问题,做出以下回答,希望有所帮助!1、大数据行业还是有非常大的人才需求的,对于就业也有不同的岗位可选,比如大数据工程师,大数据运维,大数据架构师,大数据分析师等等,就业难就难在能否找到适合的工作,能否与你的能力和就业预期匹配。2、...

  • 回答 17

    最小的基本单位是Byte应该没多少人不知道吧,下面先按顺序给出所有单位:Byte、KB、MB、GB、TB、PB、EB、ZB、YB、DB、NB,按照进率1024(2的十次方)计算:1Byte = 8 Bit1 KB = 1,024 Bytes 1 MB = 1,024 KB = 1,048,576 Bytes 1 GB = 1,024 MB = 1,048,576...

  • 回答 33

    大数据的定义。大数据,又称巨量资料,指的是所涉及的数据资料量规模巨大到无法通过人脑甚至主流软件工具,在合理时间内达到撷取、管理、处理、并整理成为帮助企业经营决策更积极目的的资讯。大数据是对大量、动态、能持续的数据,通过运用新系统、新工具、新...

  • 回答 5

    MySQL是一种关系型数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。MySQL的版本:针对不同的用户,MySQL分为两种不同的版本:MySQL Community Server社区版本,免费,但是Mysql不提供...

  • mysql安装步骤mysql 2022-05-07 18:01
    回答 2

    mysql安装需要先使用yum安装mysql数据库的软件包 ;然后启动数据库服务并运行mysql_secure_installation去除安全隐患,最后登录数据库,便可完成安装

  • 回答 5

    1.查看所有数据库showdatabases;2.查看当前使用的数据库selectdatabase();3.查看数据库使用端口showvariableslike'port';4.查看数据库编码showvariableslike‘%char%’;character_set_client 为客户端编码方式; character_set_connection 为建立连接...

  • 回答 5

    CREATE TABLE IF NOT EXISTS `runoob_tbl`(    `runoob_id` INT UNSIGNED AUTO_INCREMENT,    `runoob_title` VARCHAR(100) NOT NULL,    `runoob_author` VARCHAR(40) NOT NULL,    `submission_date` DATE,    PRI...

  • 回答 9

    学习多久,我觉得看你基础情况。1、如果原来什么语言也没有学过,也没有基础,那我觉得最基础的要先选择一种语言来学习,是VB,C..,pascal,看个人的喜好,一般情况下,选择C语言来学习。2、如果是有过语言的学习,我看应该一个星期差不多,因为语言的理念互通...

  • 回答 7

    添加语句 INSERT插入语句:INSERT INTO 表名 VALUES (‘xx’,‘xx’)不指定插入的列INSERT INTO table_name VALUES (值1, 值2,…)指定插入的列INSERT INTO table_name (列1, 列2,…) VALUES (值1, 值2,…)查询插入语句: INSERT INTO 插入表 SELECT * FROM 查...

  • 回答 5

    看你什么岗位吧。如果是后端,只会CRUD。应该是可以找到实习的,不过公司应该不会太好。如果是数据库开发岗位,那这应该是不会找到的。

  • 回答 7

    查找数据列 SELECT column1, column2, … FROM table_name; SELECT column_name(s) FROM table_name 

没有解决我的问题,去提问