索引最常用的三个类型:覆盖索引、全局索引、本地索引。
1) 覆盖索引Covered Index
如果创建的是覆盖索引,那么查询语句中的条件字段、返回字段都必须创建过索引,否则就会触发full table scan。如:
create index coverindex user_index on user (name) include (cellphone);
因此它的特点是:只需要通过索引就能返回所要查询的数据
2) 全局索引Global indexes
global是默认的索引格式。
全局索引适用于多读少写的场景,在写操作上会给性能带来极大的开销,因为所有的更新和写操作(DELETE,UPSERT VALUES和UPSERT SELECT)都会引起索引的更新,在读数据时,Phoenix将通过索引表来达到快速查询的目的。如;
CREATE INDEX USERIDINDEX ON CSVTABLES(USERID);
它有一个缺陷,如果查询语句中的条件字段或返回字段不是索引字段,就会触发全表扫描。
如:SELECT USERID,KEYWORD FROM CSVTABLES WHERE USERID='9bb8b2af92df3c3'
解决办法有两个:
一是和覆盖索引一样,创建索引时把相关字段include进来。
CREATE INDEX MYINDEX ON CSVTABLES(USERID) INCLUDE(KEYWORD);
二是强制使用索引,
SELECT /*+ INDEX(CSVTABLES,MYINDEX) */ KEYWORD FROM CSVTABLES WHERE USERID='9bb8b2af9275b84';
这样的查询语句会导致二次检索数据表,第一次检索是去索引表中查找符合USERID='9bb8b2af9275b84'的数据,这时候发现 KEYWORD 字段并不在索引字段中,会去CSVTABLES 表中第二次扫描,
因此只有当用户明确知道符合检索条件的数据较少的时候才适合使用,否则会造成全表扫描,对性能影响较大。
例子:
示例:创建一个全局索引
create index "user_index" on "harve_user"("prop"."name") ;
创建前查询一条记录需要6秒左右:
select * from "harve_user" where "name"='15001339718';
创建索引后,查询时间为32毫秒
但如果返回值中有其他字段,会全表扫描:
通过执行计划可以证明这一点:
Sql前添加:explain
强制使用索引:
select /*+ index("harve_user" "user_index") */* from "harve_user" where "name"='15001339718';
select /*+ INDEX("harve_user","user_index") */ * from "harve_user" where "name"='15001339718';
通过执行计划可以看出,先扫描索引表,然后通过rowkey去查原表。
3) 本地索引Local Indexing
与Global Indexing不同,当使用Local Indexing的时候即使查询的所有字段都不在索引字段中时也会用到索引进行查询(这是由Local Indexing自动完成的)。
本地索引适用于写多读少的场景,Phoneix在查询时会自动选择是否使用本地索引,为避免进行写操作所带来的网络开销,索引数据将放到表数据所在的服务器中。由于无法预先确定region的位置,所以在读取数据时会检查每个region上的数据因而带来一定性能开销。
CREATE LOCAL INDEX MYINDEX ON CSVTABLES(USERID);
create local index "userIndex" on "harve_user"("prop"."name");
4) 查看表索引
!index "harve_user";
5) 删除索引
drop index MYINDEX ON CSVTABLES;
如果表中的一个索引列被删除,则索引也将被自动删除,如果删除的是
覆盖索引上的列,则此列将从覆盖索引中被自动删除。
6) 索引的优化
1.index.builder.threads.max:(默认值:10)
根据主表的更新来确定更新索引表的线程数
2.index.builder.threads.keepalivetime:(默认值:60)
builder线程池中线程的存活时间
3.index.write.threads.max:(默认值:10)
更新索引表时所能使用的线程数(即同时能更新多少张索引表),其数量最好与索引表的数量一致
4.index.write.threads.keepalivetime(默认值:60)
更新索引表的线程所能存活的时间
5.hbase.htable.threads.max(默认值:2147483647)
每张索引表所能使用的线程(即在一张索引表中同时可以有多少线程对其进行写入更新),增加此值可以提高更新索引的并发量
6.hbase.htable.threads.keepalivetime(默认值:60)
索引表上更新索引的线程的存活时间
7.index.tablefactoy.cache.size(默认值:10)
允许缓存的索引表的数量
增加此值,可以在更新索引表时不用每次都去重复的创建htable,由于是缓存在内存中,所以其值越大,其需要的内存越多
为多个列创建一个索引:
create index my_idx on exampleTable(m.c0, m.c1, m.c2)
它的本质是将多个列顺序拼到索引表的rowkey中,因此使用时也就明白了,即查询时必须含有这几个列的条件,至少需要c0列,如果只有c1列那就是全表扫描了。
7) 重建索引
Phoenix的索引重建是把索引表清空后重新装配数据
alter index index1_local on tablename rebuild;
8) 效果测试
创建二级索引前测试:
User表中有一百多万数据,通过name查询:
select * from "harve_user" where "name"='13851810130';
查询两次,用时都是24秒左右。
通过rowkey查询:
只需要0.1秒左右。
创建本地索引后通过name查询:
平均0.1秒多一点