Hbase利用单行ACID,实现跨表跨库最终一致性,替代分布式事务

本文介绍如何利用HBase的单行ACID事务特性来实现跨表甚至跨数据源的最终一致性分布式事务。通过在HBase中巧妙地利用多列簇和多列的功能,可以实现在一张表中插入数据的同时确保另一张表的相关数据也被正确更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

hbase只提供了单行ACID的事务,很多人梦想能实现跨行,跨表,甚至与其他数据源实现分布式事务。

其实,大规模分布式,强一致性事务,理论上就不能完美实现,CAP原理已经指出了。

但是利用hbase提供的单行事务,同样可以实现传统两个数据库之间的最终一致性分布式事务。

原理就是利用,hbase多列簇,多列的灵活。

目标,在hbase中t1插入一条记录时,同时要保证在t2插入一条相关数据。

伪码:
var t1=”表1”
var t2=”表2”
var logid=”生成的唯一id”
var row=”行键”
var f=”数据列族”
var log=”日志列族”
var c=”数据字段”
var v1=”数据1”
var v2=”数据2”

multiple {//单行原子批处理
put(t1,row,f,c,v1) //插入数据
put(t1,row,log,logid,v2) //插入日志
}

//单独插入另外一张表
put(t2,row,f,c,v2)

//删除日志
del(t1,row,log,logid)

//上述后两步可能被打断,另外一个定时恢复进程

//搜索t1中log列族,超时的字段
for (row,logid,v2 in scan(“t1中log列族”) ){
//再次写t2
put(t2,row,f,c,v2)

//删除日志
del(t1,row,log,logid)
}

这种方式实际与
多数据源之间不使用分布式事务实现异步最终一致性

完全一致!所以不光可以实现hbase跨行,跨表,还可实现hbase与数据库,redis等等之间实现最终一致性,用来作为分布式事务的一种替代。

当然hbase单行事务,用起来还是有些麻烦,比如目前提供了一堆checkAndXXX来完成原子操作,如果能像redis这样提供一个内置脚本,就比较理想了 。

总结,只要提供了即便受限制的ACID操作,也都有可能方便扩展。

Tephra 在 Apache HBase 的基础上提供了全局一致性事务支持。HBase 提供了强一致性的基于行和区域的 ACID 操作支持,但是牺牲了在区域操作的支持。这就要求应用开发者花很大力气来确保区域边界上操作的一致性。而 Tephra 提供了全局事务支持,可以夸区域、以及多个 RPC 上简化了应用的开发。示例代码:  /**    * A Transactional SecondaryIndexTable.    */   public class SecondaryIndexTable {     private byte[] secondaryIndex;     private TransactionAwareHTable transactionAwareHTable;     private TransactionAwareHTable secondaryIndexTable;     private TransactionContext transactionContext;     private final TableName secondaryIndexTableName;     private static final byte[] secondaryIndexFamily =       Bytes.toBytes("secondaryIndexFamily");     private static final byte[] secondaryIndexQualifier = Bytes.toBytes('r');     private static final byte[] DELIMITER  = new byte[] {0};     public SecondaryIndexTable(TransactionServiceClient transactionServiceClient,                                HTable hTable, byte[] secondaryIndex) {       secondaryIndexTableName =             TableName.valueOf(hTable.getName().getNameAsString()   ".idx");       HTable secondaryIndexHTable = null;       HBaseAdmin hBaseAdmin = null;       try {         hBaseAdmin = new HBaseAdmin(hTable.getConfiguration());         if (!hBaseAdmin.tableExists(secondaryIndexTableName)) {           hBaseAdmin.createTable(new HTableDescriptor(secondaryIndexTableName));         }         secondaryIndexHTable = new HTable(hTable.getConfiguration(),                                           secondaryIndexTableName);       } catch (Exception e) {         Throwables.propagate(e);       } finally {         try {           hBaseAdmin.close();         } catch (Exception e) {           Throwables.propagate(e);         }       }       this.secondaryIndex = secondaryIndex;       this.transactionAwareHTable = new TransactionAwareHTable(hTable);       this.secondaryIndexTable = new TransactionAwareHTable(secondaryIndexHTable);       this.transactionContext = new TransactionContext(transactionServiceClient,                                                        transactionAwareHTable,                                                        secondaryIndexTable);     }     public Result get(Get get) throws IOException {       return get(Collections.singletonList(get))[0];     }     public Result[] get(List<Get> gets) throws IOException {       try {         transactionContext.start();         Result[] result = transactionAwareHTable.get(gets);         transactionContext.finish();         return result;       } catch (Exception e) {         try {           transactionContext.abort();         } catch (TransactionFailureException e1) {           throw new IOException("Could not rollback transaction", e1);         }       }       return null;     }     public Result[] getByIndex(byte[] value) throws IOException {       try {         transactionContext.start();         Scan scan = new Scan(value, Bytes.add(value, new byte[0]));         scan.addColumn(secondaryIndexFamily, secondaryIndexQualifier);         ResultScanner indexScanner = secondaryIndexTable.getScanner(scan);         ArrayList<Get> gets = new ArrayList<Get>();         for (Result result : indexScanner) {           for (Cell cell : result.listCells()) {             gets.add(new Get(cell.getValue()));           }         }         Result[] results = transactionAwareHTable.get(gets);         transactionContext.finish();         return results;       } catch (Exception e) {         try {           transactionContext.abort();         } catch (TransactionFailureException e1) {           throw new IOException("Could not rollback transaction", e1);         }       }       return null;     }     public void put(Put put) throws IOException {       put(Collections.singletonList(put));     }     public void put(List<Put> puts) throws IOException {       try {         transactionContext.start();         ArrayList<Put> secondaryIndexPuts = new ArrayList<Put>();         for (Put put : puts) {           List<Put> indexPuts = new ArrayList<Put>();           Set<Map.Entry<byte[], List<KeyValue>>> familyMap = put.getFamilyMap().entrySet();           for (Map.Entry<byte [], List<KeyValue>> family : familyMap) {             for (KeyValue value : family.getValue()) {               if (value.getQualifier().equals(secondaryIndex)) {                 byte[] secondaryRow = Bytes.add(value.getQualifier(),                                                 DELIMITER,                                                 Bytes.add(value.getValue(),                                                 DELIMITER,                                                 value.getRow()));                 Put indexPut = new Put(secondaryRow);                 indexPut.add(secondaryIndexFamily, secondaryIndexQualifier, put.getRow());                 indexPuts.add(indexPut);               }             }           }           secondaryIndexPuts.addAll(indexPuts);         }         transactionAwareHTable.put(puts);         secondaryIndexTable.put(secondaryIndexPuts);         transactionContext.finish();       } catch (Exception e) {         try {           transactionContext.abort();         } catch (TransactionFailureException e1) {           throw new IOException("Could not rollback transaction", e1);         }       }     }   } 标签:Tephra
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值