Phoenix二级索引及基于Tephra的事务实现分享

较早之前的内部分享。

Phoenix是Apache基金的顶级项目。基于JDBC API操作HBase的开源SQL引擎。

基本架构

二级索引

Global Indexes 全局索引

全局索引擅长读多写少的场景,索引的所有性能损失都发生在写入时。

每个全局索引都会建一个单独的HBase表存储索引数据,也即数据和索引是分别存储在不同表中不同region中。

写数据时,通过数据表region的CP去写索引数据。

读数据时,数据和索引不在一个region甚至不在一个RS中,所以client先读索引表,然后根据索引再读数据表。

索引表rowkey的组成:{索引列的值,可以是多列} {数据表rowkey}。

Local Indexes 局部索引

局部索引擅长写多读少的场景。

局部索引将索引数据存储于同一数据表中不同CF,无需独立表,索引数据存储于L#列族里。

索引表rowkey组成是:{region的startKey} {index_id} {索引值的值,可以多列} {数据表rowkey}。

rowkey以region的startkey做前缀,保证了数据和索引必然在同一个region里,避免了写数据时的网络开销。

但在读数据时,因为没法确定索引数据的确切位置,所以必须检查每个region,所以局部索引的读开销比较大,大表尤甚。

Covered Indexes 覆盖索引

索引表中除了必须的索引列数据,还可以放其他列的数据在索引表中,减少查询时的回表操作,提高读性能。

scss 复制代码
CREATE INDEX my_index ON my_table (v1,v2) INCLUDE(v3)

v1,v2是索引列,多存储了v3列

这个应该是对Global Indexes更适用的。

Functional Indexes 函数式索引

支持在任意的表达式上建索引。当对表中数据使用固定表达式做查询时,就可以用这种索引,对表达式的计算结果做索引,避免直接查数据表计算表达式结果。

sql 复制代码
CREATE INDEX UPPER_NAME_IDX ON EMP (UPPER(FIRST_NAME||' '||LAST_NAME))

把FIRST_NAME和LAST_NAME两列表示全名的表达式做索引

SELECT EMP_ID FROM EMP WHERE UPPER(FIRST_NAME||' '||LAST_NAME)='JOHN DOE'

Index Population

索引创建默认是同步执行的,对于大数据表不友好。4.5版本开始,索引初始化可以异步执行。

sql 复制代码
CREATE INDEX async_index ON my_schema.my_table (v) ASYNC
加ASYNC关键字异步初始化索引

然后手动使用MR任务去生成索引表

shell 复制代码
${HBASE_HOME}/bin/hbase org.apache.phoenix.mapreduce.index.IndexTool
  --schema MY_SCHEMA --data-table MY_TABLE --index-table ASYNC_IDX
  --output-path ASYNC_IDX_HFILES

一致性保证(数据和索引之间)

  • 全局索引:因为索引和数据分在不同表中,所以出现异常无法保证一致性。
  • 局部索引:4.8版本后,可以保证索引和数据的一致性。
  • 事务表的全局索引:总可以保证一致性。要求数据表开启事务,由Tephra保证跨表事务一致性。

总结

  • 全局事务和局部事务各有优劣,对应使用场景不同。对于普通用户来说,理解和使用上可能会有障碍。
  • 数据和索引的一致性需要依赖事务来保证。不一致之后,会根据配置产生不同后果:
    • 索引失效,直至数据表和索引表一致。
    • 数据表不可写。

Tephra

Apache基金会孵化器项目。被用于Phoenix为HBase提供跨行跨表的全语义ACID事务支持。

基于HBase原生提供的多版本并发控制,Tephra实现了快照隔离的并发事务支持。

架构

Tephra Components

分为3部分组件

  • Client

    • 和Tx Manager交互,协调事务生命周期
    • 在请求中注入事务信息,甚至改写请求
    • 包装HBase原生API,读写HBase
  • Tx Manager,单独的Server,一主一备,利用ZK选主探活。

    • 管理分配事务ID,保证全局唯一
    • 管理事务状态
    • 冲突检查
  • Coprocessor

    • 在读请求里加Filter,过滤掉不可见数据
    • 清理无效数据

事务生命周期

注:事务支持两大类:

  • long-running:特指像MR任务这样的大量修改的事务,容易出现事务冲突,所以并不维护change keys,也不支持回滚。如果abort了,会一直处于对其他事务不可见的状态。
  • short:会有个timeout,超时如果还没结束,会被置为invalided状态。下面主要讨论的就是这类。
java 复制代码
TransactionContext context = new TransactionContext(client, transactionAwareHTable);
try {
    context.start();
    transactionAwareHTable.put(new Put(Bytes.toBytes("row"));
    // ...
    context.finish();
} catch (TransactionFailureException e) {
    context.abort();
}

start tx

生成一个新事务Transaction,事务对象的主要要素:

  • 一个写指针:事务中所有写操作使用的版本号。基本可以认为是事务开始的时间戳。认为也是事务ID。
  • 一个读指针:事务中所有读操作不超过这个版本。已提交的事务中的最大写针指值。
  • excluded versions的集合:其他事务的写版本,本事务的读操作不可见,因为这些事务可能还未完成或没有回滚。还有些invalid状态的事务也不可见。

这个Transaction对象会在Tx Manager、Client、Coprocessor之间传输。

如果在now这个时间点开启一个新事物,3个属性的值应该分别是:

  • write pointer: now。
  • read pointer: t1。Committed状态的事务中,写指针最大值。
  • excluded versions: t2, t4。Invalid和In Progress状态的事务不可见。

do work

读写操作。

  • 所有操作都会加上attribute,序列化的Transaction对象,用于RS的Coprocessor识别。

  • Put操作,使用写指针值改写时间戳

  • Delete操作,被改写成Put操作。这里对原生API的读写会有影响。

    • DELETE_FAMILY,改成了CF,Column都是byte[0]的Put
    • DELETE_COLUMN,改成了Column是byte[0]的Put
    • 我理解这里的目的是为了方便的生成undo操作,用于回滚。Delete操作很难undo,但Put操作则可以通过生成Delete来做undo。
  • 所有写操作都会被记录,生成change keys。用于回滚和冲突检查。更具冲突级别分为两种:

    • NONE,ROW:table+rowkey+CF
    • COLUMN:table+rowkey+CF+column

try commit

将事务ID和change keys交给Tx Manager,做冲突检查。检查逻辑主要是:在该事务开始后,有没有其他已经commit的事务,如果有,change keys有没有重复。

try abort

尝试回滚。

主要是client端对所有写操作做undo,将Put转成Delete。

invalidate

主要是把事务ID加到invalidTxList。其他事物会把invalidTxList加到excluded versions里,避免读到非法数据。

变成invalid的事务的处理方法:

  • 手动处理,把事务ID从invalidTxList删掉,事务产生的数据仍然可能在hbase里,而且删掉之后就对其他事物可见了。
  • 依靠major compact,invalid数据被compact删掉之后,定时任务检查到了,从invalidTxList删掉。

快照隔离

总结

优点

  • 用于Phoenix,实现了跨行、跨表的完备的ACID语义。
  • 性能损失不大。

缺点

  • 删除操作对HBase数据有影响。
  • Tx Manager如果故障或重启,期间事务功能可能用不了。Tx Manager的状态数据使用了snapshot+WAL的方式来恢复,failover可能会比较慢。
  • 对于大集群、大事务可能支持的并不好。(不确定)
    • 事务的change keys都由tx manager内存存储一份
    • invalided状态事务的清理,依赖major compact。
  • 不同版本的hbase client对应不同版本的tephra client,原生无法兼容,集群升级对用户影响大。可能使用Phoenix可以解决。
  • 项目好像还在孵化器,社区不活跃。基本处于只是维护适配新hbase release版本的状态。

资料

相关推荐
阿华的代码王国1 小时前
MySQL ------- 索引(B树B+树)
数据库·mysql
Hello.Reader1 小时前
StarRocks实时分析数据库的基础与应用
大数据·数据库
执键行天涯1 小时前
【经验帖】JAVA中同方法,两次调用Mybatis,一次更新,一次查询,同一事务,第一次修改对第二次的可见性如何
java·数据库·mybatis
yanglamei19622 小时前
基于GIKT深度知识追踪模型的习题推荐系统源代码+数据库+使用说明,后端采用flask,前端采用vue
前端·数据库·flask
工作中的程序员2 小时前
ES 索引或索引模板
大数据·数据库·elasticsearch
严格格2 小时前
三范式,面试重点
数据库·面试·职场和发展
微刻时光2 小时前
Redis集群知识及实战
数据库·redis·笔记·学习·程序人生·缓存
单字叶2 小时前
MySQL数据库
数据库·mysql
mqiqe3 小时前
PostgreSQL 基础操作
数据库·postgresql·oracle
just-julie3 小时前
MySQL面试题——第一篇
数据库·mysql