索引太大 如何建立索引二级索引

专注大数据分布式处理--Spark生态、机器学习、人工智能
HBase建立二级索引的一些解决方案(Solr+hbase方案等)
HBase的一级索引就是rowkey,我们只能通过rowkey进行检索。如果我们相对hbase里面列族的列列进行一些组合查询,就需要采用HBase的二级索引方案来进行多条件的查询。
HBase建立二级索引的一些解决方案
//-------------------------------------------------------------------
常见的二级索引方案有以下几种:
1.MapReduce方案
2.ITHBASE方案
3.IHBASE方案
4.Coprocessor方案
5.Solr+hbase方案
MapReduce方案
IndexBuilder:利用MR的方式构建Index
优点:可以并发批量构建Index
缺点:当对hbase插入一条数据时,不能实时构建Index
有三个学生号123,要去查看张三这个人的学号是多少时,若数据量非常大时,全表扫描不太现实,便想怎么样构建一个索引表,如下:创建一个反向索引表123,把张三作为rowkey,学号为列,就可通过rowkey张三查到这个记录。通过反向索引可以对某些列进行查询。
Demo:执行这个程序 $ hbase IndexBuilder 参数
()package IndexD
import java.io.IOE
import java.util.HashM
import java.util.M
import java.util.S
import org.apache.commons.collections.map.HashedM
import org.apache.hadoop.conf.C
import org.apache.hadoop.hbase.HBaseC
import org.apache.hadoop.hbase.client.HC
import org.apache.hadoop.hbase.client.HConnectionM
import org.apache.hadoop.hbase.client.P
import org.apache.hadoop.hbase.client.R
import org.apache.hadoop.hbase.client.S
import org.apache.hadoop.hbase.io.ImmutableBytesW
import org.apache.hadoop.hbase.mapreduce.MultiTableOutputF
import org.apache.hadoop.hbase.mapreduce.TableInputF
import org.apache.hadoop.hbase.mapreduce.TableMapReduceU
import org.apache.hadoop.hbase.mapreduce.TableM
import org.apache.hadoop.hbase.util.B
import org.apache.hadoop.mapreduce.J
import org.apache.hadoop.util.GenericOptionsP
public class IndexBuilder {
private String rootD
private String zkS
private HConnection hConn =
private IndexBuilder(String rootDir,String zkServer,String port) throws IOException{
this.rootDir = rootD
this.zkServer = zkS
this.port =
conf = HBaseConfiguration.create();
conf.set("hbase.rootdir", rootDir);
conf.set("hbase.zookeeper.quorum", zkServer);
conf.set("hbase.zookeeper.property.clientPort", port);
hConn = HConnectionManager.createConnection(conf);
static class MyMapper extends TableMapper&ImmutableBytesWritable, Put&{
//记录了要进行索引的列
private Map&byte[], ImmutableBytesWritable& indexes = new HashMap&byte[], ImmutableBytesWritable&();
private String familyN
protected void map(ImmutableBytesWritable key, Result value,
Context context) throws IOException, InterruptedException {
//原始表列
Set&byte[]& keys = indexes.keySet();
//索引表的rowkey是原始表的列,索引表的列是原始表的rowkey
for (byte[] k : keys){
//获得新建索引表的表名
ImmutableBytesWritable indexTableName = indexes.get(k);
//Result存放的是原始表的数据
//查找到内容
根据列族 和 列 得到原始表的值
byte[] val = value.getValue(Bytes.toBytes(familyName), k);
if (val != null) {
Put put = new Put(val);//索引表行键
原始表的行键
put.add(Bytes.toBytes("f1"),Bytes.toBytes("id"),key.get());
context.write(indexTableName, put);
//真正运行Map之前执行一些处理。
protected void setup(Context context) throws IOException,
InterruptedException {
//通过上下文得到配置
Configuration conf = context.getConfiguration();
//获得表名
String tableName = conf.get("tableName");
//String family = conf.get("familyName");
//获得列族
familyName = conf.get("columnFamily");
String[] qualifiers = conf.getStrings("qualifiers");
for (String qualifier : qualifiers) {
//建立一个映射,为每一个列创建一个表,表的名字tableName+"-"+qualifier
//原始表的列
索引表新建表名
indexes.put(Bytes.toBytes(qualifier),
new ImmutableBytesWritable(Bytes.toBytes(tableName+"-"+qualifier)));
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
String rootDir = "hdfs://hadoop1:8020/hbase";
String zkServer = "hadoop1";
String port = "2181";
IndexBuilder conn = new IndexBuilder(rootDir,zkServer,port);
String[] otherArgs = new GenericOptionsParser(conn.conf, args).getRemainingArgs();
//至少传三个参数IndexBuilder: TableName,ColumnFamily,Qualifier
if(otherArgs.length&3){
System.exit(-1);
String tableName = otherArgs[0];
String columnFamily = otherArgs[1];
conn.conf.set("tableName", tableName);
conn.conf.set("columnFamily", columnFamily);
可能存在多个列
String[] qualifiers = new String[otherArgs.length-2];
for (int i = 0; i & qualifiers. i++) {
qualifiers[i] = otherArgs[i+2];
conn.conf.setStrings("qualifiers", qualifiers);
@SuppressWarnings("deprecation")
Job job = new Job(conn.conf,tableName);
job.setJarByClass(IndexBuilder.class);
job.setMapperClass(MyMapper.class);
job.setNumReduceTasks(0);//由于不需要执行reduce阶段
job.setInputFormatClass(TableInputFormat.class);
job.setOutputFormatClass(MultiTableOutputFormat.class);
Scan scan = new Scan();
scan.setCaching(1000);//一次性批量读取多少条记录
//初始化MR
TableMapReduceUtil.initTableMapperJob(tableName,scan,
MyMapper.class, ImmutableBytesWritable.class, Put.class, job);
job.waitForCompletion(true);
创建原始表
hbase(main):002:0& create 'studentinfo','f1'
0 row(s) in 0.6520 seconds
=& Hbase::Table - studentinfo
hbase(main):003:0& put 'studentinfo','1','f1:name','zhangsan'
//0 row(s) in 0.1640 seconds
hbase(main):004:0& put 'studentinfo','2','f1:name','lisi'
//0 row(s) in 0.0240 seconds
hbase(main):005:0& put 'studentinfo','3','f1:name','wangwu'
//0 row(s) in 0.0290 seconds
hbase(main):006:0& scan 'studentinfo'
COLUMN+CELL
column=f1:name, timestamp=3, value=zhangsan
column=f1:name, timestamp=2, value=lisi
column=f1:name, timestamp=0, value=wangwu
//3 row(s) in 0.0530 seconds创建索引表
hbase(main):007:0& create 'studentinfo-name','f1'
//0 row(s) in 0.7740 seconds
=& Hbase::Table - studentinfo-name执行结果
& scan 'studentinfo-name'
ITHBASE方案
//------------------------------------------------------------------------------
优点:ITHBase(Indexed Transactional HBase)是HBase的一个事物型的带索引的扩展。
缺点:需要重构hbase,几年没有更新。
http://github.com/hbase-trx/hbase-transactional-tableindexedIHBASE方案
//------------------------------------------------------------------------------
**优点:**IHBase(Indexed HBase)是HBase的一个扩展,用干支持更快的扫描。
缺点:需要重构hbase,版本老旧。
原理:在Memstore满了以后刷磁盘时,IHBase会进行拦截请求,并为这个memstore的数据构建索引,索引另一个CF的方式存储在表内。scan的时候,IHBase会结合索引列中的标记,来加速scan。
http://github.com/ykulbak/ihbaseCoprocessor方案
//------------------------------------------------------------------------------
HIndex–来自华为的HBase二级索引
http://github.com/Huawei-Hadoop/hindex
The solution is 100% Java, compatible with Apache HBase 0.94.8, and is open sourced under ASL.
Following capabilities are supported currently.
1.multiple indexes on table,
2.multi column index,
3.index based on part of a column value,
4.equals and range condition scans using index, and
5.bulk loading data to indexed table (Indexing done with bulk load).Solr+hbase方案
//------------------------------------------------------------------------------
Solr是一个独立的企业级搜索应用服务器,它对并提供类似干Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果。
Solr是一个高性能,采用Java5开发,基干Lucene的全文搜索服务器。同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能节理界面,是一款非常优秀的全文搜索引擎。
HBase无可置疑拥有其优势,但其本身只对rowkey支持毫秒级的快速检索,对于多字段的组合查询却无能为力。
基于Solr的HBase多条件查询原理很简单,将HBase表中涉及条件过滤的字段和rowkey在Solr中建立索引,通过Solr的多条件查询快速获得符合过滤条件的rowkey值,拿到这些rowkey之后在HBASE中通过指定rowkey进行查询。
本文转载自:http://blog.csdn.net/scgaliguodong123_/article/details/
关于使用hbase进行多维度条件实时查询的方案调研。
1.MapReduce方案
优点:并发批量构建Index
缺点:不能实时构建Index
2.ITHBASE方案
缺点:需要重构hbase,几年没有更新。
3.IHBASE方案
缺点:需要重构hbase。
4.Coprocessor方案
华为的HBase二级索引采用此方案(hindex 代码开源)。
1)、索引和数据分别放在不同表里;
  2)、所有的运算逻辑全都放在服务端;
  3)、需要修改HBase源码,侵入性大
  4)、 查询时无需指定,即可自动使用最优索引
缺点:代码很复杂,代码量非常多。一下子要弄明白原理可能比较困难。hindex和公司的HBase版本不兼容性
5.Solr+hbase方案
缺点:对Solr不熟悉
缺点: 如存储开销比较大,尤其是当索引列比较多的时候,空间开销会更大;索引更新代价比较高,会影响系统的吞吐量;索引创建以后,不能够动态增加或修改。
7.360的hbase二级索引
360二级索引的特点如下:
  1)、索引和Rowkey在同一个表里;
  2)、支持多范围与操作优化;
  3)、支持索引重建
缺点:没有开源,需要按照他的思想去实现,原理不是太清楚,只明白一点点,按照这个思想来重新搭建也可能非常耗时间。
8.phoenix的二级索引
好处:开源,自带二级索引。
现状:公司的hbase集群,资源有限。目前主要是提供给dmp在使用。刚好能撑住目前的服务。
偶尔有压力的时候,还会挂掉几台机器。
按照目前的需求,只有两条方案:一个是按照上面的思想自己开发一个hbase的二级索引工具,另外一个是使用phoenix自带二级索引。
依照目前的hbase的集群使用情况,就算自己开发出来了二级索引,估计集群资源不够用的前提下,也发挥不出二级索引的速度优势。
所以只能暂且在phoenix的现有资源上优化我们的程序性能,尽量减少检索时间。
phoenix为 HBase建立二级索引表
华为hbase二级索引(secondary index)细节分析
HBase + Solr Cloud实现HBase二级索引
360 hbase二级索引总结
Hbase solr 二级索引
Solr+Hbase多条件查(优劣互补)
HBase+Solr 的 二级索引 实时查询
基于solr实现hbase的二级索引
Hbase二级索引方案Solr key value index
使用solr构建hbase二级索引
没有更多推荐了,
(window.slotbydup=window.slotbydup || []).push({
id: '5865575',
container: s,
size: '300,250',
display: 'inlay-fix'查看: 85887|回复: 24
hbase如何创建二级索引以及创建二级索引实例
主题听众收听
本帖最后由 pig2 于
11:20 编辑
1.如何建立全局二级索引?
2.如何对一个表建立二级索引?
3.如何卸载二级索引?
环境:hadoop2.2+hbase0.94不过同样应该适用于hadoop2.4及hbase0.985,未测试
二级索引可以对单个表建立索引,也可以全局建立索引,也就是对所有表:
1.全局建立索引,可以修改hbase-site.xml文件
&property&
&name&hbase.coprocessor.region.classes&/name&
&value&org.apache.hadoop.hbase.coprocessor.AggregateImplementation&/value&
&/property& 为所有table加载了一个cp class,可以用”,”分割加载多个class复制代码
2.单个表建立索引
1.首先disable ‘表名’
2.然后修改表
alter 'LogTable',METHOD=&'table_att','coprocessor'=&'hdfs:///test.jar|www.aboutyun.com.hbase.HbaseCoprocessor|1001'复制代码
3.enable '表名'
3.卸载索引
alter 'LogTable', METHOD =& 'table_att_unset', NAME =& 'coprocessor$1‘
复制代码
4.建立索引实例:
建立二级索引,首先如下程序:
import java.io.IOE
import java.util.I
import java.util.L
import org.apache.hadoop.conf.C
import org.apache.hadoop.hbase.C
import org.apache.hadoop.hbase.KeyV
import org.apache.hadoop.hbase.client.HT
import org.apache.hadoop.hbase.client.P
import org.apache.hadoop.hbase.coprocessor.BaseRegionO
import org.apache.hadoop.hbase.coprocessor.ObserverC
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorE
import org.apache.hadoop.hbase.regionserver.wal.WALE
public class HbaseCoprocessor extends BaseRegionObserver {
& && &&&public void prePut(final ObserverContext&RegionCoprocessorEnvironment& e,
& && && && && && && && &final Put put, final WALEdit edit, final boolean writeToWAL)
& && && && && && && && &throws IOException {
& && && && && & // set configuration
& && && && && & Configuration conf = new Configuration();
& && && && && & // need conf.set...
& && && && && & String colName = &columnName&;
& && && && && & HTable table = new HTable(conf, &indexTableName&);
& && && && && & List&Cell& kv = put.get(&familyName&.getBytes(), colName.getBytes());
& && && && && & Iterator&Cell& kvItor = kv.iterator();
& && && && && & while (kvItor.hasNext()) {
& && && && && && && && &Cell tmp = kvItor.next();
& && && && && && && && &Put indexPut = new Put(tmp.getValue());
& && && && && && && && &indexPut.add(&familyName&.getBytes(), &columnName&.getBytes(),
& && && && && && && && && && && && && & tmp.getRow());
& && && && && && && && &table.put(indexPut);
& && && && && & }
& && && && && & table.close();
& && &&&}
}
复制代码
根据自己的实际情况修改,然后打包test.jar,放到hdfs文件系统中。
进入hbase&&shell,执行如下命令:
alter 'LogTable',METHOD=&'table_att','coprocessor'=&'hdfs:///test.jar|www.aboutyun.com.hbase.HbaseCoprocessor|1001'复制代码
创建索引成功.png (19.22 KB, 下载次数: 36)
07:14 上传
查看是否创建成功
chuangjianchenggong.png (29.83 KB, 下载次数: 33)
07:17 上传
HBaseCoprocessor pdf
1.png (91.95 KB, 下载次数: 42)
11:20 上传
(1.29 MB, 下载次数: 305, 售价: 1 云币)
11:18 上传
点击文件名下载附件
售价: 1 云币 &
主题听众收听
高级会员, 积分 1995, 距离下一级还需 3005 积分
高级会员, 积分 1995, 距离下一级还需 3005 积分
好东西。。。。。
主题听众收听
高级会员, 积分 1995, 距离下一级还需 3005 积分
高级会员, 积分 1995, 距离下一级还需 3005 积分
不错。。。。。
主题听众收听
中级会员, 积分 355, 距离下一级还需 645 积分
中级会员, 积分 355, 距离下一级还需 645 积分
非常的不错哦,谢谢
主题听众收听
新手上路, 积分 10, 距离下一级还需 40 积分
新手上路, 积分 10, 距离下一级还需 40 积分
kettle,你值得拥有
主题听众收听
中级会员, 积分 513, 距离下一级还需 487 积分
中级会员, 积分 513, 距离下一级还需 487 积分
学习了,真心赞
主题听众收听
中级会员, 积分 916, 距离下一级还需 84 积分
中级会员, 积分 916, 距离下一级还需 84 积分
很好的东西分享。。。谢谢
主题听众收听
中级会员, 积分 448, 距离下一级还需 552 积分
中级会员, 积分 448, 距离下一级还需 552 积分
主题听众收听
中级会员, 积分 968, 距离下一级还需 32 积分
中级会员, 积分 968, 距离下一级还需 32 积分
感谢分享。
主题听众收听
高级会员, 积分 1335, 距离下一级还需 3665 积分
高级会员, 积分 1335, 距离下一级还需 3665 积分
学习学习~~~~~~~~
经常参与各类话题的讨论,发帖内容较有主见
经常帮助其他会员答疑
活跃且尽责职守的版主
为论坛做出突出贡献的会员
站长推荐 /4
会员注册不成功的原因
新手获取积分方法
hadoop3.0学习:零基础安装部署hadoop集群
about云课程:大数据日志实时分析
Powered by下次自动登录
现在的位置:
& 综合 & 正文
Cassandra中的二级索引
怎么去给一个Row的Column建立二级索引是Cassandra中一个常见拟题。下面的这个帖子来讲一个实现方式,当然不是只有这一种才能实现。对于有经验的Cassandra用户来讲,这个帖子应该会提起兴趣哦,这里描述的实现方式根本不用Super Column,也就不会有使用Super Column带来的复杂度和约束了。此外,应该指出的是,Cassandra0.7版本以上都会实现原生的二级索引,它使下面讲述的东西更加简单了,但是这个思路对于考虑Cassandra的二级索引任然是非常有效的,还是可以在很多场景得到应用。
首先,我们假设如下一个场景。有一个Container(比如:一个部门),该Container中包含众多的Items(比如:部门中的用户),每一个用户(user)有任意的属性集合,也可以根据Container中的上下文的值来搜索。Items也可以是别的Container的成员,但是在这里先不考虑这种情况。
在Cassandra中,一种建模方式是使用2个ColumnFamilies(下面将简称为CF)。第一个CF会描述Item的属性,名称为Item_Properties,它用Cassandra的最简答的数据模型,在Item_Properties的Row能通过一个Key来找到,在这里例子中将使用UUID来描述这个Key,在Item_properties中的列是Item的属性名,列的值是对应的属性的值。
CF: Item_Properties
Key: item_id
Compare with: BytesType
property_name
property_value
第二个CF是均对Container的,它包括Items的集合,名称为Container_Items。Container_Items中的列为Item_properties中的行的Key。在Cassandra中,这是一个让人难以理解的地方。当你把Column Family当做一个简单的关系数据库的表,把CF中的行也作为一个关系数据库中的一条记录来使用的时候,每一个行可以作为一个简单的表,甚至是一个连接的表。在Container_Items中,每一个列名用Item_Properties的行Key,列值则填充插入时候的当前时间戳。Container_Items的行可以增长的相当大,由于每一个列大概有42个字节(UUID+timestamp),在Cassandra0.7以下的版本,最大能允许4000万条Items,对于一个部门中的User来讲这个也许是一个合理的限制,但是如果你用这个方式去存放Status的对应信息(比如Tweets),那就是一个不可以接受的了,对于一个状态的Tweets肯定会超过这个限制。不过,在Cassandra0.7及其以后的版本中,就没有这个限制了,一行最多能够存20亿列。
CF: Container_Items
Key: container_id
Compare with: TimeUUIDType
insertion_timestamp
到目前为止,这些都是相当基础的Cassandra数据模型。当一个人想从Container中根据指定属性值来查找Items的时候,事情就会变复杂了。为了实现这个目标,你需要管理你自己的索引,大大超过了Cassandra的最简设计了。需要创建另外两个ColumnFamily来实现这个目标。第一个CF存放实际的索引,用Container_ID和想去索引的Item_Properties中的属性名称作为行Key。结构如下表:
CF: Container_Items_Property_Index
Key: container_id + property_name
Compare with: compositecomparer.CompositeType
composite(property_value, item_id, entry_timestamp)
这里描述的索引技术与其它地方有点不同的是索引中每一列是怎么构成的。Cassandra提供了一套用于对行中的列进行排序的Column Type。你能在CF在被创建的时候指定一种排序类型,Cassandra也允许自定义Column Typesz,正如上面所使用到的组合类型。组合类型的列可以让我们将几个不同的成分进行组合为一个列,并且还可以按照该列进行排序。这使得可以让我们建立唯一的列,就算是那些列原本是存在不唯一的值也没有问题,不过需要添加一些额外的值去加以区别。
最后一个问题要处理的是属性值需要改变并且索引值必须被更新的时候会发生什么。答案很简单,你在Container_Items_Property_Index列族的把新值作为一个列插入,并删除旧值列。然而,Cassandra的最终一致性模型和事务缺乏相关的原因,简单地从Item_Properties取以前的值,然后再更新,然后删除Container_Items_Property_Index索引条目中的老得值将无法可靠地工作。To dothis we maintain a list of previous values
for the specific property of a givenitem and use that to remove these values from the index before adding the newvalue to the index. These are stored in the following CF:
CF: Container_Item_Property_Index_Entries
Key: container_id + item_id + property_name
Compare with: LongType
entry_timestamp
property_value
在取出这些列之后就删除掉,所以这些行绝不会变得太大,在大多数情况下,绝不会超过1到2列,如果修改得比较频繁则会大一些。通过这个方法, it's areally good idea to make sure you understand why this CF is necessary becauseyou can use variations of it to solve a lot of problems with "eventualconsistency" datastores.
所以,整体看来,主要有两个基本的操作:(1)给Container中的一个Item设置属性值 (2)从Container中取得匹配特定Value的Items列表信息。这些看起来像这样:
给Container中的Item的属性(property_name)设置值(property_value)的过程如下:
1、取得新增加的实体(entry)的timestamp作为当前的时间戳的值;
2、以Container_ID+item_ID+property_name为Key用get_Slice方法去Container_item_Property_index_entries中查找符合条件的列信息;
3、调用batch_mutate方法去批量完成如下步骤:
从Container_Items_Property_index中删除掉那些从Container_item_Property_index_entries中找到的先前步骤中的列信息;
从Container_item_Property_index_entries中删除掉先前查找出来的列信息;
向Item_properties中插入列(列名为Property_name,值为property_value);
向Container_Items_Property_Index中插入新的索引记录信息;
向Container_Item_Property_Index_Entries插入新的值信息,供后续修改时使用;
按照Property_value来查询Container中的Items过程如下:
1、以container_id + property_name为Key从Container_Items_Property_Index列簇中调用方法Get_Slice来找到匹配的Property_Value
看起来有很多步骤,但实际上,所有的步骤都被中间件包装了,外面都不可见。你能从 中找到Composite column比较的具体实现,能从 ..中找到上述索引技术的简单实现
pointsout that, since Cassandra already stores a timestamp along with the columnvalue, that it's redundant to store in the column value as well and can beomitted in the Container_Items
and Container_Item_Property_Index_Entries columnfamilies, which would reduce storage space by about 20%.
翻译的很丑陋,自己都觉得不爽了,只是还是想坚持下去!
大家可以参考原文:
【上篇】【下篇】http://github.com/TaoXiao
利用Phoenix为HBase创建二级索引
为什么需要Secondary Index
对于HBase而言,如果想精确地定位到某行记录,唯一的办法是通过rowkey来查询。如果不通过rowkey来查找数据,就必须逐行地比较每一列的值,即全表扫瞄。对于较大的表,全表扫瞄的代价是不可接受的。
但是,很多情况下,需要从多个角度查询数据。例如,在定位某个人的时候,可以通过姓名、身份证号、学籍号等不同的角度来查询,要想把这么多角度的数据都放到rowkey中几乎不可能(业务的灵活性不允许,对rowkey长度的要求也不允许)。
所以,需要secondary index来完成这件事。secondary index的原理很简单,但是如果自己维护的话则会麻烦一些。现在,Phoenix已经提供了对HBase secondary index的支持,下面将说明这样用Phoenix来在HBase中创建二级索引。
配置HBase以支持Secondary Index
在每一个RegionServer的hbase-site.xml中加入如下的属性:
&property&
&name&hbase.regionserver.wal.codec&/name&
&value&org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec&/value&
&/property&
&property&
&name&hbase.region.server.rpc.scheduler.factory.class&/name&
&value&org.apache.hadoop.hbase.ipc.PhoenixRpcSchedulerFactory&/value&
&description&Factory to create the Phoenix RPC Scheduler that uses separate queues for index and metadata updates&/description&
&/property&
&property&
&name&hbase.rpc.controllerfactory.class&/name&
&value&org.apache.hadoop.hbase.ipc.controller.ServerRpcControllerFactory&/value&
&description&Factory to create the Phoenix RPC Scheduler that uses separate queues for index and metadata updates&/description&
&/property&
&property&
&name&hbase.coprocessor.regionserver.classes&/name&
&value&org.apache.hadoop.hbase.regionserver.LocalIndexMerger&/value&
&/property&
在每一个Master的hbase-site.xml中加入如下的属性:
&property&
&name&hbase.master.loadbalancer.class&/name&
&value&org.apache.phoenix.hbase.index.balancer.IndexLoadBalancer&/value&
&/property&
&property&
&name&hbase.coprocessor.master.classes&/name&
&value&org.apache.phoenix.hbase.index.master.IndexMasterObserver&/value&
&/property&
Global Indexing v.s. Local Indexing
Global Indexing
Global indexing targets read heavy, low write uses cases. With global indexes, all the performance penalties for indexes occur at write time. We intercept the data table updates on write (DELETE, UPSERT VALUES and UPSERT SELECT), build the index update and then sent any necessary updates to all interested index tables. At read time, Phoenix will select the index table to use that will produce the fastest query time and directly scan it just like any other HBase table. By default, unless hinted, an index will not be used for a query that references a column that isn’t part of the index.
Local Indexing
Local indexing targets write heavy, space constrained use cases. Just like with global indexes, Phoenix will automatically select whether or not to use a local index at query-time. With local indexes, index data and table data co-reside on same server preventing any network overhead during writes. Local indexes can be used even when the query isn’t fully covered (i.e. Phoenix automatically retrieve the columns not in the index through point gets against the data table). Unlike global indexes, all local indexes of a table are stored in a single, separate shared table. At read time when the local index is used, every region must be examined for the data as the exact region location of index data cannot be predetermined. Thus some overhead occurs at read-time.
为已有的Phoenix表创建secondary index
首先,在Phoenix中创建一个salted table。
create table EXAMPLE (my_pk varchar(50) not null, M.C0 varchar(50), M.C1 varchar(50), M.C2 varchar(50), M.C3 varchar(50), M.C4 varchar(50), M.C5 varchar(50), M.C6 varchar(50), M.C7 varchar(50),M.C8 varchar(50), M.C9 varchar(50)
constraint pk primary key (my_pk)) salt_buckets=10;
这里创建的表名为EXAMPLE,有10个列(列名为{M.C0, M.C1, ···, M.C9})。
然后,再将一个CSV文件中的数据通过Bulkload的方式导入到该表中。
该CSV文件中有1000万条记录,在HDFS中的路径为/user/tao/xt-data/data-10m.csv,则导入的命令为
HADOOP_CLASSPATH=$(hbase classpath) hadoop jar phoenix-4.3.1-client.jar org.apache.phoenix.mapreduce.CsvBulkLoadTool -libjars 3rdlibs/commons-csv-1.0.jar,3rdlibs/joda-time-2.7.jar --table EXAMPLE --input /user/tao/xt-data/data-10m.csv
由于导入的过程用到了一些其他的类,所以需要通过-libjars 3rdlibs/commons-csv-1.0.jar,3rdlibs/joda-time-2.7.jar来将相关的Jar包传给这个MapReduce任务。另外,中提到的方法,要求对于Phoenix 4.0+的版本,指定 HADOOP_CLASSPATH=$(hbase mapredcp),但是通过实践,发现这样不行,应该用HADOOP_CLASSPATH=$(hbase classpath)。
在为表EXAMPLE创建secondary index之前,先看看查询一条数据所需的时间:
可以看到,对名为M.C0的列进行按值查询需要7秒多。
现在,为表EXAMPLE的列M.C0创建Index,如下:
create index my_index on example (m.c0);
此时,查看HBase,会发现其中多了一个名为MY_INDEX的表。而从Phoenix中则看不到该表。
在为EXAMPLE创建了index之后,我们再来进行查询。
这次,查询时间从7秒多降到了0.097秒。
确保Query使用Index
By default, a global index will not be used unless all of the columns referenced in the query are contained in the index.
在上例中,由于我们只对M.C0创建了索引,所以如果查询项中包含其他列的话(主键MY_PK除外),是不会使用index的。此外,如果查询项不包含其他列,但是条件查询语句中包含了其他列(主键MY_PK除外),也会引发全表扫瞄。如下:
要让一个查询使用index,有三种方式:
创建 convered index;
在查询中提示其使用index;
创建 local index
Covered Index
如果在某次查询中,查询项或者查询条件中包含除被索引列之外的列(主键MY_PK除外)。默认情况下,该查询会触发full table scan,但是使用covered index则可以避免全表扫描。
例如,我们按照EXAMPLE的方式创建了另一张表EXAMPLE_2,表中的数据和表的schema与EXAMPLE都是相同的,不同之处在于EXAMPLE_2对其M.C1这一列创建了covered index。
create index my_index_2 on example_2 (m.c0) include (m.c1);
现在,如果查询项中不包含除M.C0和M.C1之外的列,而且查询条件不包含除M.C0之外的列,则可以确保该查询使用Index,如下:
#方式2 - Hint
在select和column_name之间加上/*+ Index(&表名& &index名&)*/,如下:
This will cause each data row to be retrieved when the index is traversed to find the missing M.C1 column value. This hint should only be used if you know that the index has good selective (i.e. a small number of table rows have a value of ‘c0_’ in this example), as otherwise you’ll get better performance by the default behavior of doing a full table scan
#方式3 - Local Index
Unlike global indexes, local indexes will use an index even when all columns referenced in the query are not contained in the index. This is done by default for local indexes because we know that the table and index data coreside on the same region server thus ensuring the lookup is local.
对于HBase 0.98.6的版本,似乎不支持创建local index,如下:
Functional Index
Another useful feature that was introduced in the 4.3 release is functional indexes. Functional indexes allow you to create an index not just on columns, but on an arbitrary expressions. Then when a query uses that expression, the index may be used to retrieve the results instead of the data table. For example, you could create an index on UPPER(FIRST_NAME||' '||LAST_NAME) to allow you to do case insensitive searches on the combined first name and last name of a person.
Phoenix supports two types of indexing techniques: global and local indexing. Each are useful in different scenarios and have their own failure profiles and performance characteristics.
下面为表EXAMPLE创建一个Functional Index,如下:
create index index_upper_c2 on example (upper(m.c2)) include m.c2
这里,我们实际上为表EXAMPLE又创建了一个名为INDEX_UPPER_C2的Index。也就是说,可以为同一张表创建多个Index。
Index 排序
create index my_index on example (M.C1 desc, M.C0) include (M.C2);
drop index `index-name` on `table-name`
索引表属性
create table和create index都可以将一些属性传递给对应的HBase表,例如:
CREATE INDEX my_index ON my_table (v2 DESC, v1) INCLUDE (v3)
SALT_BUCKETS=10, DATA_BLOCK_ENCODING='NONE';
对于global indexes,如果primary table是salted table,则index会自动地成为salted index。对于local indexes,则不允许指定salt_buckets。
Immutable Indexing
For a table in which the data is only written once and never updated in-place, certain optimizations may be made to reduce the write-time overhead for incremental maintenance. This is common with time-series data such as log or event data, where once a row is written, it will never be updated. To take advantage of these optimizations, declare your table as immutable by adding the IMMUTABLE_ROWS=true property to your DDL statement:
CREATE TABLE my_table (k VARCHAR PRIMARY KEY, v VARCHAR) IMMUTABLE_ROWS=true;
All indexes on a table declared with IMMUTABLE_ROWS=true are considered immutable (note that by default, tables are considered mutable). For global immutable indexes, the index is maintained entirely on the client-side with the index table being generated as change to the data table occur. Local immutable indexes, on the other hand, are maintained on the server-side. Note that no safeguards are in-place to enforce that a table declared as immutable doesn’t actually mutate data (as that would negate the performance gain achieved). If that was to occur, the index would no longer be in sync with the table.
如果创建的表是immutable table,如下:
create table my_tablek(VARCHAR PRIMARY KEY, v VARCHAR) IMMUTABLE_ROWS=true
那么,为该表创建的所有index都是immutable indexes。
可以将一个已有的immutable table转变为mutable table,可以通过如下命令:
alter table my_table set IMMUTABLE_ROWS=false
能不能为多个column建立index(在同一个index table中)?
例如,如下命令似乎是可以对两个column创建index:
create index my_idx on example(m.c0, m.c1)
但是,似乎只有列m.c0的真正具有索引,列m.c1似乎没有索引:
答案是:可以为多个column创建索引,但是在查询时要按照索引列的顺序来查询。例如,为M.C0、M.C1和M.C2建立索引:
create index idx on example (m.c0, m.c1, m.c2)
在查询时,可以同时根据将这3列作为条件,且顺序不限。但是,第一列必须是M.C0。
这是因为:当为多列建立索引时,rowkey实际上是这些column的组合,并且是按照它们的先后顺序的组合。
如果查询时第一列不是M.C0,那么就要进行full scan,速度会很慢。而如果查询的第一列是M.C0,就可以直接将满足关于M.C0的数据记录找出来。即使后面还有没有被索引的列,也可以很快得到结果,因为满足关于M.C0的结果集已经不大了(如果是这种情况的话),对其再进行一次查询不会是full scan。
Bulkload的数据的index能不能自动同步?
维护Index的原理:当对data table执行写入操作时,delete、upsert values和upsert select会被捕获,并据此来更新data table所对应的index。
We intercept the data table updates on write (DELETE, UPSERT VALUES and UPSERT SELECT), build the index update and then sent any necessary updates to all interested index tables
当以bulkload的方式来将数据导入到data table时,会绕开HBase的常规写入路径(client –& buffer –& memstore –& HLog –& StoreFile –& HFile),直接生成最终的HFiles。对于bulkload,对data table的更新能不能被捕获,进而自动维护相应index呢?我们来验证。
首先建立一个空的data table:
create table EXAMPLE (PK varchar primary key,
M.C0 varchar, M.C1 varchar, M.C2 varchar, M.C3 varchar, M.C4 varchar, M.C5 varchar, M.C6 varchar, M.C7 varchar, M.C8 varchar, M.C9 varchar)
salt_buckets = 20
再为其创建2个index:
create index IDX_C0 on EXAMPLE(M.C0);
create index IDX_C1 on EXAMPLE(M.C1);
现在用MapReduce将一个包含了1亿行记录的CSV文件bulkload到数据表EXAMPLE中去:
sudo -u hbase HADOOP_CLASSPATH=$(hbase classpath) hadoop jar phoenix-4.3.1-client.jar org.apache.phoenix.mapreduce.CsvBulkLoadTool -libjars 3rdlibs/commons-csv-1.0.jar,3rdlibs/joda-time-2.7.jar --table EXAMPLE --input /user/tao/data/data-100m.csv
从输出来看,一共启动了3个MR任务,分别针对数据表EXAMPLE、索引表IDX_C0和索引表IDX_C1,如下:
现在,再看看查询时间:
所以,一旦index创建之后,不论是否使用bulkload来更新data table,都会保证index的自动更新。
Phoenix Secondary Index
360HBase二级索引方案学习总结
phoenix 写二级索引的触发机制
Spring+HBase+phoenix踩过的坑
Phoenix(八)二级索引之— —Global Indexing
HBase phoenix二级索引
Phoenix二级索引那些事儿(下)
Phoenix二级索引
Phoenix二级索引(Secondary Indexing)的使用
Phoenix(十)二级索引之— —Append-only Data
没有更多推荐了,

我要回帖

更多关于 档案存放位置索引 的文章

 

随机推荐