Loading

Phoenix 异步创建索引

date: 2020-11-07 15:34:00
updated: 2020-11-13 17:17:00

Phoenix 异步创建索引

当表数据量过大的时候,创建索引会报错,可以修改服务器端的 hbase.rpc.timeout,默认是1分钟,可以自定义时间。也可以异步创建索引,通过在语句后面添加async 关键字。

需要注意的是:

  1. 异步创建索引只支持全局索引
  2. 执行async语句只是第一步,还需要通过执行jar包来保证索引真正的建立

1. 为什么只支持全局索引?

首先是本地索引和全局索引的一些概念和区别

  • 本地索引
    • 适合写多读少的情况
    • 索引数据直接写在原表里,不会新建一张表。在 phoenix-sqlline 里执行 !tables 的确会发现创建的本地索引表,但是那个只是一个映射,并不是单独存在的。由于索引数据直接写在表里,所以原表的数据量=原始数据+索引数据。
    • 本地索引rowkey的设计规则: 原数据region的start key+"\x00"+二级索引字段1+"\x00"+二级索引字段2(复合索引)…+"\x00"+原rowkey。
    • 索引数据和真实数据存放在同一台机器上,减少了网络传输的开销。同理,创建索引后的rowkey的最开始的部分是 原数据region的start key,这样在通过二级索引定位到数据后,可以在当前的region中直接找到数据,减少网络开销。减少网络开销,也意味着写入的速度会变快。但是多了一步通过rowkey查找数据的过程,所以读的过程就不如直接读取列族里的数据的速度快。
  • 全局索引
    • 适合读多写少的情况
    • 索引数据会单独存在一张表里。
    • 全局索引必须是查询语句中所有列都包含在全局索引中,它才会生效。

      Select * 不会命中索引
      select 具体的字段 from table where col ...
      col 必须是第一个主键或者是索引里包含的字段才会命中索引
      如果索引表包含 a、b 三个字段,where 里有 a 和 c 两个字段,那么也不会走索引,因为c不在索引里,发现索引走不通,只能走全表

    • 为了命中索引,要把需要查询的字段通过 include 关键字来一起写入索引表里,也就是覆盖索引。
    • 写入数据的同时需要往索引表同步写数据,而索引表是分布在不同的数据节点上的,跨节点的数据传输带来了较大的性能消耗,所以写慢;但是查询的时候,如果命中了索引表,那就直接把数据带出来了,读会快。

综上,本地索引不是表,全局索引才是表,而async是针对表的一种方式,所以只能作用于全局索引

2. 如何执行async

  1. 首先是需要创建一个全局索引,同时使用 async

create index XXX on database.tablename(col1, col2) include(col3, col4) async

此时去看这个表,会发现 index_state 字段的值是 building,说明索引表还没创建好,这是因为 async 关键字会初始化一个mr作业,只是把创建索引的数据文件准备好,还没有正式开始

  1. 执行mr作业
hbase org.apache.phoenix.mapreduce.index.IndexTool \
--schema 库名 --data-table 表名 --index-table 索引表名 \
--output-path hdfs路径指向一个文件件即可

库名、表名、索引表名尽量都不要小写

这个命令执行后可能会报错,遇到 org.apache.phoenix.mapreduce.index.IndexTool 依赖的jar没法加载,那就可以换一个方式执行

java -cp ./本地文件夹路径:/data1/cloudera/parcels/PHOENIX/lib/phoenix/phoenix-5.0.0-cdh6.2.0-client.jar  org.apache.phoenix.mapreduce.index.IndexTool   --schema 库名 --data-table 表名 --index-table 索引表名     --output-path hdfs路径指向一个文件件即可

本地文件夹里需要包含 hbase yarn hdfs 的配置文件

如果遇到 java.io.IOException: Can't get Master Kerberos principal for use as renewer 说明缺少yarn的配置文件

如果遇到 org.apache.hadoop.security.AccessControlException: Permission denied: user=phoenix, access=WRITE, inode="/user":hdfs:supergroup:drwxr-xr-x 需要在 hbase-site.xml 文件里添加 hbase.fs.tmp.dir 配置项,值是hdfs上一个有读写权限的目录路径。
原因:从 org.apache.phoenix.mapreduce.index.IndexTool 开始追代码,会找到 org.apache.hadoop.hbase.mapreduce.HFileOutputFormat2,在配置mr作业的时候,configurePartitioner() 方法里 String hbaseTmpFsDir = conf.get("hbase.fs.tmp.dir", HConstants.DEFAULT_TEMPORARY_HDFS_DIRECTORY); 会去读取配置文件里的这个值,默认是 "/user/" + System.getProperty("user.name") + "/hbase-staging"

3. 附

  1. 查询执行计划,判断是否命中索引表
内容 含义
CLIENT 表明操作在客户端执行还是服务端执行,客户端尽量返回少的数据。若为 SERVER 表示在服务端执行。
FILTER BY expression 返回和过滤条件匹配的结果。
FULL SCAN OVER tableName 表明全表扫描某张业务表。
RANGE SCAN OVER tableName [ … ] 表明代表范围扫描某张表,括号内代表 rowkey 的开始和结束。
ROUND ROBIN 无 ORDER BY 操作时, ROUND ROBIN 代表最大化客户端的并行化。
x-CHUNK 执行此操作的线程数。
PARALLEL x-WAY 表明合并多少并行的扫描。
EST_BYTES_READ 执行查询时预计扫描的总字节数。
EST_ROWS_READ 执行查询时预计扫描多少行。
EST_INFO_TS 收集查询信息的 epoch time
  1. 在创建索引的过程中,发现了一个可能是版本bug的地方,已提官网issue,链接如下

官网issue地址

问题:如果在创建本地索引时,有一个字段设置了default value,在生成的索引表里就只会显示默认值,不管是什么类型;如果这个类型是tinyint的话,还可能会造成之后主键的数据,原表的数据是对的,但是索引表是错的,如果命中了索引表,那么就返回的是错误的数据。

posted @ 2020-11-12 17:44  猫熊小才天  阅读(1892)  评论(0编辑  收藏  举报