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 小时前
shell脚本定义特殊字符导致执行mysql文件错误的问题
数据库·mysql
一勺菠萝丶1 小时前
MongoDB 常用操作指南(Docker 环境下)
数据库·mongodb·docker
m0_748244832 小时前
StarRocks 排查单副本表
大数据·数据库·python
C++忠实粉丝2 小时前
Redis 介绍和安装
数据库·redis·缓存
wmd131643067122 小时前
将微信配置信息存到数据库并进行调用
数据库·微信
是阿建吖!2 小时前
【Linux】基础IO(磁盘文件)
linux·服务器·数据库
凡人的AI工具箱3 小时前
每天40分玩转Django:Django国际化
数据库·人工智能·后端·python·django·sqlite
ClouGence3 小时前
Redis 到 Redis 数据迁移同步
数据库·redis·缓存
m0_748236583 小时前
《Web 应用项目开发:从构思到上线的全过程》
服务器·前端·数据库
苏三说技术3 小时前
Redis 性能优化的18招
数据库·redis·性能优化