二级索引与索引Join是Online业务系统要求存储引擎提供的基本特性。RDBMS支持得比较好,NOSQL阵营也在摸索着符合自身特点的最佳解决方案。
这篇文章会以HBase做为对象来探讨如何基于Hbase构建二级索引与实现索引join。文末同时会列出目前已知的包括0.19.3版secondary index,?ITHbase, Facebook和官方Coprocessor方案的介绍。
理论目标
在HBase中实现二级索引与索引Join需要考虑三个目标:
1,高性能的范围检索。
2,数据的低冗余(存储所占的数据量)。
3,数据的一致性。
性能与数据冗余,一致性是相互制约的关系。
如果想实现了高性能地范围检索,必然需要依赖冗余索引数据来提升性能,而数据冗余会导致更新数据时难以实现一致性,特别是分布式场景下。
如果对范围检索的性能要求不高,那么可以不考虑冗余数据,一致性问题也可以间接避免,毕竟share nothing是公认的最简单有效的解决方案。
理论结合实际,下文会以实例的方式来阐述各个方案是如何选择偏重点。
这些方案是经过笔者资料查阅和同事的不断交流后得出的结论,如有错误,欢迎指正:
1,按索引建表
每一个索引建立一个表,然后依靠表的row key来实现范围检索。row key在HBase中是以B+ tree结构化有序存储的,所以scan起来会比较效率。
单表以row key存储索引,column value存储id值或其他数据 ,这就是Hbase索引表的结构。
如何Join?
多索引(多表)的join场景中,主要有两种参考方案:
1,按索引的种类扫描各自独立的单索引表,最后将扫描结果merge。
这个方案的特点是简单,但是如果多个索引扫描结果数据量比较大的话,merge就会遇到瓶颈。
比如,现在有一张1亿的用户信息表,建有出生地和年龄两个索引,我想得到一个条件是在杭州出生,年龄为20岁的按用户id正序排列前10个的用户列表。
有一种方案是,系统先扫描出生地为杭州的索引,得到一个用户id结果集,这个集合的规模假设是10万。
然后扫描年龄,规模是5万,最后merge这些用户id,去重,排序得到结果。
这明显有问题,如何改良?
保证出生地和年龄的结果是排过序的,可以减少merge的数据量?但Hbase是按row key排序,value是不能排序的。
变通一下 – 将用户id冗余到row key里?OK,这是一种解决方案了,这个方案的图示如下:
merge时提取交集就是所需要的列表,顺序是靠索引增加了_id,以字典序保证的。
2, 按索引查询种类建立组合索引。
在方案1的场景中,想象一下,如果单索引数量多达10个会怎么样?10个索引,就要merge 10次,性能可想而知。
解决这个问题需要参考RDBMS的组合索引实现。
比如出生地和年龄需要同时查询,此时如果建立一个出生地和年龄的组合索引,查询时效率会高出merge很多。
当然,这个索引也需要冗余用户id,目的是让结果自然有序。结构图示如下:
这个方案的优点是查询速度非常快,根据查询条件,只需要到一张表中检索即可得到结果list。缺点是如果有多个索引,就要建立多个与查询条件一一对应的组合索引,存储压力会增大。
在制定Schema设计方案时,设计人员需要充分考虑场景的特点,结合方案一和二来使用。下面是一个简单的对比:
单索引 | 组合索引 | |
检索性能 | 优异 | 优异 |
存储 | 数据不冗余,节省存储。 | 数据冗余,存储比较浪费。 |
事务性 | 多个索引保证事务性比较困难。 | 多个索引保证事务性比较困难。 |
join | 性能较差 | 性能优异 |
count,sum,avg,etc | 符合条件的结果集全表扫描 | 符合条件的结果集全表扫描 |
从上表中可以得知,方案1,2都存在更新时事务性保证比较困难的问题。如果业务系统可以接受最终一致性的话,事务性会稍微好做一些。否则只能借助于复杂的分布式事务,比如JTA,Chubby等技术。
count, sum, avg, max, min等聚合功能,Hbase只能通过硬扫的方式,并且很悲剧,你可能需要做一些hack操作(比如加一个CF,value为null),否则你在扫描时可能需要往客户端传回所有数据。
当然你可以在这个场景上做一些优化,比如增加状态表等,但复杂性带来的风险会更高。
还有一种终极解决方案就是在业务上只提供上一页和下一页,这或许是最简单有效的方案了。
2,单张表多个列族,索引基于列
Hbase提供了列族Column Family特性。
列索引是将Column Family做为index,多个index值散落到Qualifier,多个column值依据version排列(CF, Qualifer, Version Hbase会保证有序,其中CF和Qualifier正序,Version倒序)。
举个典型的例子,就是用户卖了很多商品,这些商品的标题title需要支持like %title%查询。传统基于RDMBS就是模糊查询,基于search engine就是分词+倒排表。
在HBase中,模糊查询显然不满足我们的要求,接下来只能通过分词+倒排的方式来存储。基于CF的倒排表索引结构见下图:
取数据的时候,只需要根据用户id(row key)定位到一个row,然后根据分词定位到qualifier,再通过version的有序list,取top n条记录即可。不过大家可能会发现个问题,version list的总数量是需要scan全version list才能知道的,这里需要业务系统本身做一些改进。
如何join?
实现方式同方案1里的join,多个CF列索引扫描结果后,需要走merge,将多个索引的查询结果conjunction。
两个方案的对比似乎变化就是一个表,一个列,但其实这个方案有个最大的好处,就是解决了事务性的问题,因为所有的索引都是跟单个row key绑定的,我们知道单个row的更新,在hbase中是保证原子更新的,这就是这个方案的天然优势。当你在考虑单索引时,使用基于列的索引会比单表索引有更好的适用性。
而组合索引在以列为存储粒度的方案里,也同样可以折中实现。理解这种存储模式的同学可能已经猜到了,就是基于qualifier。
下表对比了表索引和列索引的优缺点:
列索引 | 表索引 | |
检索性能 | 检索数据需要走多次scan,第一次scan row key,第二次scan qualifier,第三次scan version。 | 只需要走一次row key的scan即可。 |
存储 | 在没有组合索引时,存储较节省 | 在没有组合索引时,存储较节省 |
事务性 | 容易保证 | 保证事务性比较困难 |
join | 性能较差,只有在建立组合条件Qualifier的时候性能会有所改善 | 性能较差,只有在建立组合表索引的时候性能会有所改善 |
额外的问题 | 1,同一个row里每个qualifier的version是有大小限制的,不能超过Int的最大值。(别以为这个值很大,对于海量数据存储,上亿很平常) 2,version的count总数需要额外做处理获取。 3,单个row数据超过split大小时,会导致不能compaction或compaction内存吃紧,增加风险。 |
|
count,sum,avg,etc | 符合条件的结果集全表扫描 | 符合条件的结果集全表扫描 |
虽然列索引缺点这么多,但是存储节省带来的成本优势有时还是值得我们去这么做的,何况它还解决了事务性问题,需要用户自己去权衡。
值得一提的是,Facebook的消息应用服务器就是基于类似的方案来实现的。
3,ITHBase
方案一中的多表,解决了性能问题,同时带来了存储冗余和数据一致性问题。这两个问题中,只要解决其中一项,其实也就满足了大多数业务场景。
本方案中,着重关注的是数据一致性。ITHbase的全称是 Indexed Transactional HBase,从名字中就能看出,事务性是它的重要特性。
ITHBase的事务原理简介
建一张事务表__GLOBAL_TRX_LOG__,每次开启事务时,在表中记录状态。因为是基于Hbase的HTable,事务表同样会写WAL用于恢复,不过这个日志格式被ITHbase改造过,它称之为THLog。
客户端对多张表更新时,先启动事务,然后每次PUT,将事务id传递给HRegionServer。
ITHbase通过继承HRegionServer和HReogin类,重写了大多数操作接口方法,比如put, ?update, delete, 用于获取transactionalId和状态。
当server收到操作和事务id后,先确认服务端收到,标记当前事务为待写入状态(需要再发起一次PUT)。当所有表的操作完成后,由客户端统一做commit写入,做二阶段提交。
4,Map-reduce
这个方案没什么好说的,存储节省,也不需要建索引表,只需要靠强大的集群计算能力即可导出结果。但一般不适合online业务。
5,Coprocessor协处理器
官方0.92.0新版正在开发中的新功能-Coprocessor,支持region级别索引。详见:
https://issues.apache.org/jira/browse/HBASE-2038
协处理器的机制可以理解为,server端添加了一些回调函数。这些回调函数如下:
The Coprocessor interface defines these hooks:
- preOpen, postOpen: Called before and after the region is reported as online to the master.
- preFlush, postFlush: Called before and after the memstore is flushed into a new store file.
- preCompact, postCompact: Called before and after compaction.
- preSplit, postSplit: Called after the region is split.
- preClose and postClose: Called before and after the region is reported as closed to the master.
The RegionObserver interface is defines these hooks:
- preGet, postGet: Called before and after a client makes a Get request.
- preExists, postExists: Called before and after the client tests for existence using a Get.
- prePut and postPut: Called before and after the client stores a value.
- preDelete and postDelete: Called before and after the client deletes a value.
- preScannerOpen postScannerOpen: Called before and after the client opens a new scanner.
- preScannerNext, postScannerNext: Called before and after the client asks for the next row on a scanner.
- preScannerClose, postScannerClose: Called before and after the client closes a scanner.
- preCheckAndPut, postCheckAndPut: Called before and after the client calls checkAndPut().
- preCheckAndDelete, postCheckAndDelete: Called before and after the client calls checkAndDelete().
利用这些hooks可以实现region级二级索引,实现count, sum, avg, max, min等聚合操作而不需要返回所有的数据,详见 https://issues.apache.org/jira/browse/HBASE-1512。
二级索引的原理猜测
因为coprocessor的最终方案还未公布,就提供的这些hooks来说,二级索引的实现应该是拦截同一个region的put, get, scan, delete等操作。与此同时在同一个reigon里维护一个索引CF,建立对应的索引表。
基于region的索引表其实有很多局限性,比如全局排序就很难做。
不过我觉得Coprocessor最大的好处在于其提供了server端的完全扩展能力,这对于Hbase来说是一个大的跃进。
如何join?
目前还未发布,不过就了解很难从本质上有所突破。解决方案无非就是merge和composite index,同样事务性是需要解决的难题之一。
业界已经公开的二级索引方案罗列:
0.19.3版Secondary Index
一直关注HBase的同学,或许知道,早在HBase 0.19.3版发布时,曾经加入过secondary index的功能,Issue详见这里。
它的使用例子也很简单:http://blog.rajeevsharma.in/2009/06/secondary-indexes-in-hbase.html
0.19.3版Secondary Index通过将列值以row key方法存储,提供索引scan。
但HBase早期的需求主要来自Hadoop。事务的复杂性以及当时发现hadoop-core里有个很难解决的与ITHBase兼容的问题,致使官方在0.20.0版将其核心代码移出了hbase-core,改为contrib第三方扩展,Issue详见这里。
Transactional tableindexed-ITHBase
这个方案就是在0.19.3版被官方剥离出核心的第三方扩展,它的方案上面已经介绍过了。目前支持最新的Hbase 0.90。
是否具备工业强度的稳定性是用户选择它的主要障碍。
https://github.com/hbase-trx/hbase-transactional-tableindexed
Facebook方案
facebook采用的是单表多列索引的解决方案,上面已经提到过了。很完美地解决了数据一致性问题,这主要跟他们的使用场景有关。
感兴趣的同学可以看下这篇blog,本文不作详述:
HBase官方方案 0.92.0 版开发中 – Coprocessor协处理器
还未发布,不过hbase官方blog有篇介绍:http://hbaseblog.com/2010/11/30/hbase-coprocessors
Lily Hbase indexing Library
这是一个索引构建,查询,管理的框架。结构上,就是通过一张indexmeta表管理多张indexdata索引表。
特点是,有一套非常完善的针对int, string, utf-8, decimal等类型的row key排序机制。这个机制在这篇博文中有详细介绍:
此外,框架针对join场景(原理=merge),提供了封装好的conjunction和disjunction工具类。
针对索引构建场景,Hbase indexing library也提供了很方便的接口。
IHbase
global ordering | yes | no | IHBase has an index for each region. The flip side of not having global ordering is compatibility with the good old HRegion: results are coming back in row order (and not value order as in ITHBase) |
Full table scan? | no | no | THbase does a partial scan on the index table. ITHBase supports specifying start/end rows to limit the number of scanned regions |
Multiple Index Usage | no | yes | IHBase can take advantage of multiple indexes in the same scan. IHBase IdxScan object accepts an Expression which allows intersection/unison of several indexed column criteria |
Extra disk storage | yes | no | IHBase indexes are created when the region starts/flushes and do not require any extra storage |
Extra RAM | yes | yes | IHBase indexes are in memory and hence increase the memory overhead. THBbase indexes increase the number of regions each region server has to support thus costing memory too |
Parallel scanning support | no | yes | In ITHBase the index table needs to be consulted and then GETs are issued for each matching row. The behavior of IHBase (as perceived by the client) is no different than a regular scan and hence supports parallel scanning seamlessly.?parallel GET can be implemented to speedup THbase scans |
scan的时候,IHBase会结合索引列中的标记,来加速scan。
转载请注明原文链接:http://kenwublog.com/hbase-secondary-index-and-join
相关推荐
### HBase二级索引与JOIN知识点详解 #### HBase简介 - **定义**: HBase是一种分布式、面向列的NoSQL数据库系统,它基于Google Bigtable论文实现。 - **底层架构**: HBase的数据存储依赖于Hadoop Distributed File ...
类比于传统型数据库里的一些查询方式,本文对Hbase的存储原理进行了研究,借助分布式计算框架Mapreduce在Hbase上构建了二级索引,就可以对表进行有针对性的定位和高效率的查找,同时也减轻zookeeper服务对资源调度的压力...
3. **二级索引与聚合**:Phoenix支持二级索引和聚合查询,扩展了HBase的功能,增强了查询能力。 4. **全链路实时系统**:例如在支付宝智能搜索平台,HBase与搜索引擎的实时链路中,通过Phoenix实现了数据源的HBase...
- **二级索引**:虽然HBase支持构建二级索引,但它并不像关系型数据库那样支持复杂的SQL查询和JOIN操作。 - **不支持的特性**:HBase不支持传统关系型数据库中的某些特性,如SQL查询、JOIN操作等。 #### HBase在...
- **第二索引和替代查询路径**:介绍除主键外的索引构建方法。 - **限制**:列举 HBase 在设计和使用时应遵循的限制条件。 - **模式设计用例**:提供具体的模式设计实例,帮助理解设计原则。 #### 八、HBase 与 ...
`phoenix-hbase-2.2-5.1.2-bin.tar.gz`这个压缩包包含了Phoenix 2.2版本与HBase 5.1.2版本的二进制文件,用户可以解压后在本地或集群环境中安装和运行。安装步骤通常包括配置环境变量、创建HBase和Phoenix的目录结构...
4. **索引管理**:Phoenix支持二级索引,可以创建覆盖索引、全局唯一索引和本地索引,以提升查询效率。这些索引是物理存储在HBase中的,不同于传统关系数据库的B树索引,但原理类似。 5. **连接器支持**:Phoenix...
- 使用HBase的二级索引和Filter,配合Hive的JOIN和GROUP BY等操作,实现复杂查询。 - 注意Hive的倾斜表问题,避免数据不均衡导致的性能瓶颈。 6. **应用场景** - Hive用于离线数据分析,例如日志分析、用户行为...
- 二级索引:提供非主键查询的方式。 - 约束:数据的完整性约束。 - 案例研究:不同的schema设计案例。 在操作和性能配置选项方面,了解如何调整相关参数,对系统性能至关重要。指南中会介绍一些特殊情况下的配置...
- **Secondary Indexes and Alternate Query Paths**(二级索引和替代查询路径):虽然HBase主要依赖行键进行查询,但也可以通过其他方式提高查询效率。 - **Constraints**(约束):介绍了一些限制数据插入的方法...
2. **索引**:Phoenix 支持创建二级索引,提高查询性能。索引可以是覆盖索引、全局索引或本地索引。 3. **分区**:Phoenix 支持对表进行分区,以优化查询性能并提高数据分布的均匀性。 4. **自动分页**:Phoenix ...
- **不支持的功能**:当前版本不支持二级索引、多行事务、复杂的用户权限管理等。 - **后续工作方向**:增强数据稳定性、提升性能、实现备份/恢复功能、支持多数据中心部署。 #### 八、监控与运维 - **监控工具**:...
Phoenix支持二级索引,能并行执行操作,将计算推向数据端,进行谓词下推和semi-join优化,适用于处理海量数据和点查场景。 4. **OLAP分析 - Kylin** Apache Kylin是一个分布式的OLAP分析工具,它可以与HBase和...
2. **索引管理**:Phoenix提供了二级索引功能,允许用户创建覆盖索引,以加速特定查询。这在处理大量数据时非常有用,因为可以直接定位到所需数据,而无需扫描整个表。 3. **并发与性能优化**:Phoenix通过并行执行...
- **非主键索引**(二级索引):叶子节点存储主键值。 6. **MyISAM 和 InnoDB 的索引差异**: - **InnoDB**:B+树索引的叶子节点包含数据,数据文件即索引文件。 - **MyISAM**:B+树索引叶子节点存储数据地址,...
3. **索引优化**:Phoenix支持创建二级索引,显著提高了查询性能,尤其是对于复杂查询和JOIN操作。 4. **性能提升**:通过将SQL查询转换为HBase的多行扫描和过滤器,Phoenix能减少I/O操作,从而提高查询速度。 5. ...
- **轻量级事务/二级索引/动态列等特性**:这些特性使得 Phoenix 更加灵活且易于使用。 - **JDBC Driver**:提供了 JDBC 接口,使得 Phoenix 可以被大多数 Java 应用程序轻松集成。 - **Metadata 管理**:对元数据...
3. **Hbase协处理器与二级索引** - 了解HBase协处理器(Coprocessor)的概念及其应用场景。 - 学习如何实现二级索引来提高查询性能。 4. **过滤器查询** - 学习使用Filter进行复杂的查询操作。 - 掌握不同类型的...
首先,腾讯广告的数据处理涉及原始曝光、点击和转化日志的Join操作,通过Spark Streaming进行实时处理,将数据存储在HBase中,利用Phoenix作为SQL查询引擎。Hermes用于索引和快速检索,同时进行了Dremel类似的分析。...