Apache Omid TSO 组件源码实现原理解析

 更新时间:2024年07月22日 10:41:16   作者:FlyingZCC  
这篇文章主要介绍了Apache Omid TSO 组件源码实现原理解析,本文给大家介绍的非常详细,感兴趣的朋友一起看看吧

Apache Omid TSO 组件实现原理

作用

独立进程,处理全局事务之间的并发冲突。

流程

TSOChannelHandler#channelRead -> AbstractRequestProcessor -> PersistenceProcessorHandler

总体流程

thread1
    TSOChannelHandler#channelRead
        AbstractRequestProcessor#timestampRequest 接收 client 请求,创建 RequestEvent 并 publish
thread2
    AbstractRequestProcessor#onEvent 处理 RequestEvent 请求
        AbstractRequestProcessor#handleRequest
            PersistenceProcessorImpl#addTimestampToBatch 创建 PersistEvent,当 batch 满了发送事件
thread3
    PersistenceProcessorHandler#onEvent 持久化事件处理

TSOChannelHandler

继承自 Netty 的 ChannelInboundHandlerAdapter,用于处理 TSO 的入站请求

channelRead

委托 requestProcessor 创建 timestampRequest 和 commitRequest 请求事件。

AbstractRequestProcessor

处理 timestamp 和 commit 事件。

onEvent

处理 RequestEvent 事件,按照事件类型派发给 handleTimestamp 和 handleCommit 方法进行处理。

handleTimestamp

1.通过 timestampOracle 获取下一个时间戳;
2.PersistenceProcessorImpl#addBatch 事件添加到 batch,但是后续对 timestamp 请求不会额外处理。

handleCommit

主要通过 hasConflictsWithCommittedTransactions 判断 writeSet 和 CommitHashMap 里是否有事务写冲突,如果没有则可以提交事务,分配 commitTimestamp。

private void handleCommit(RequestEvent event) throws Exception {
    long startTimestamp = event.getStartTimestamp(); // startTimestamp
    Iterable<Long> writeSet = event.writeSet(); // 写入集,存储的是 cellIds
    Collection<Long> tableIdSet = event.getTableIdSet();
    boolean isCommitRetry = event.isCommitRetry();
    boolean nonEmptyWriteSet = writeSet.iterator().hasNext(); // 检查写集合是否为空,即事务是否有写操作
    if (startTimestamp > lowWatermark &&
        !hasConflictsWithFences(startTimestamp, tableIdSet) &&
        !hasConflictsWithCommittedTransactions(startTimestamp, writeSet)) { // 检查事务是否满足提交条件,通过 hasConflictsWithCommittedTransactions 判断是否有事务写冲突
        // 可以进行事务提交
        long commitTimestamp = timestampOracle.next(); // 获取提交时间戳
        Optional<Long> forwardNewWaterMark = Optional.absent();
        if (nonEmptyWriteSet) { // 写集合非空
            long newLowWatermark = lowWatermark;
            for (long r : writeSet) { // 遍历写集合中的每个元素,更新其最新的写入时间戳,并计算新的低水位线
                long removed = hashmap.putLatestWriteForCell(r, commitTimestamp); // 更新 cellId 对应的 commitTimestamp, 返回之前的 oldest commitTimestamp
                newLowWatermark = Math.max(removed, newLowWatermark); // 更新低水位线
            }
            if (newLowWatermark != lowWatermark) { // 更新低水位线
                lowWatermark = newLowWatermark;
                forwardNewWaterMark = Optional.of(lowWatermark);
            }
        }
        forwardCommit(startTimestamp, commitTimestamp, c, event.getMonCtx(), forwardNewWaterMark);  // 持久化 commit 请求
    } else { // 事务不满足提交条件
        if (isCommitRetry) { // Re-check if it was already committed but the client retried due to a lag replying
            forwardCommitRetry(startTimestamp, c, event.getMonCtx());  // 若是提交重试,再次检查是否已提交以避免因响应延迟导致的重复提交
        } else {
            forwardAbort(startTimestamp, c, event.getMonCtx()); // 否则,中止事务
        }
    }
}

CommitHashMap

通过 LongCache 缓存 cellId -> lastCommittedTimestamp 的映射。

getLatestWriteForCell 方法:
根据 cellId 获取 lastCommittedTimestamp。

putLatestWriteForCell 方法:
更新 cellId 对应的 lastCommittedTimestamp。

LongCache

缓存 cellId -> lastCommittedTimestamp 的映射。

get 和 set 操作都是先将原始 cellId 进行 hash 操作找到位置,所以可能存在冲突。

set

更新 cellId 对应的 lastCommittedTimestamp。

public long set(long key, long value) {
    final int index = index(key); // cellId 取模返回下标,可能会冲突
    int oldestIndex = 0;
    long oldestValue = Long.MAX_VALUE;
    for (int i = 0; i < associativity; ++i) {
        int currIndex = 2 * (index + i); // 计算 key 下标
        if (cache[currIndex] == key) { // 相同事务 cellId, 替换场景
            oldestValue = 0;
            oldestIndex = currIndex;
            break;
        }
        if (cache[currIndex + 1] <= oldestValue) { // 没找到相同的key.通过和 oldestValue 比较会将最小的 timestamp 剔除
            oldestValue = cache[currIndex + 1];
            oldestIndex = currIndex;
        }
    }
    // 替换最旧的键值对,将其更新为新的键值对
    cache[oldestIndex] = key;
    cache[oldestIndex + 1] = value;
    return oldestValue;
}

get

获取 cellId 对应的 lastCommittedTimestamp,找不到则返回 0.

public long get(long key) {
    final int index = index(key);
    for (int i = 0; i < associativity; ++i) { // associativity 里存储的元素key应该是相同的
        int currIndex = 2 * (index + i); // 计算 key 的下标
        if (cache[currIndex] == key) { // 找到 cache key
            return cache[currIndex + 1]; // 返回对应的 value
        }
    }
    return 0;
}

PersistenceProcessorImpl

将 startTimestamp 和 commitTimestamp 放入 batch.

addCommitToBatch

创建 event,添加到 current batch
如果 current batch is full
    triggerCurrentBatchFlush

triggerCurrentBatchFlush

创建 PersistBatchEvent 并发送事件

PersistenceProcessorHandler

处理上面 PersistenceProcessorImpl 发送过来的事件,进行持久化处理。

onEvent

实际上只处理 commit 事件,会创建 put 对象将事务信息持久化到 hbase 的 commitTable (OMID_COMMIT_TABLE).

HBaseCommitTable

构造方法: 根据 HBaseCommitTableConfig 配置初始化

到此这篇关于Apache Omid TSO 组件源码实现原理的文章就介绍到这了,更多相关Apache Omid TSO 组件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Linux文件搜索神器之Find命令的使用完全指南

    Linux文件搜索神器之Find命令的使用完全指南

    find命令是Linux系统中最强大的文件搜索工具,可以按名称、大小、时间等多种维度筛选文件,它支持基础和高级用法,下面小编就和大家详细介绍一下吧
    2026-04-04
  • linux中的软连接和硬连接详解

    linux中的软连接和硬连接详解

    大家好,本篇文章主要讲的是linux中的软连接和硬连接详解,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • linux设置服务开机自启动的三种方式

    linux设置服务开机自启动的三种方式

    这篇文章主要介绍了linux设置服务开机自启动的三种方式,需要的朋友可以参考下
    2015-11-11
  • Apache访问机制配置小结

    Apache访问机制配置小结

    本文主要介绍了Apache访问机制配置小结,包括如何设置访问控制、认证和授权,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-07-07
  • Service Temporarily Unavailable的503错误是怎么回事?

    Service Temporarily Unavailable的503错误是怎么回事?

    一般来说,出现Service Temporarily Unavailable错误多半是因为网站访问量大,造成了流量超限或者并发数大引起的资源超限出现的错误
    2013-05-05
  • Polysh命令实现多日志查询的方法示例

    Polysh命令实现多日志查询的方法示例

    大家应该都知道Polysh是一个交互式命令,可以在一台服务器上批量的对一批服务器进行处理,运行交互式命令。下面这篇文章主要给大家介绍了关于利用Polysh命令实现多日志查询的相关资料,需要的朋友可以参考,下面来一起看看吧。
    2017-12-12
  • 详解Linux 安装 JDK、Tomcat 和 MySQL(图文并茂)

    详解Linux 安装 JDK、Tomcat 和 MySQL(图文并茂)

    本文会详细介绍 Windows 上安装虚拟机之后,如何在 Linux 中安装 JDK、Tomcat 和 MySQL 的过程,有兴趣的可以了解一下
    2017-08-08
  • linux系列之常用运维命令整理笔录(小结)

    linux系列之常用运维命令整理笔录(小结)

    这篇文章主要介绍了linux系列之常用运维命令整理笔录(小结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • Linux下配置jdk环境的方法

    Linux下配置jdk环境的方法

    这篇文章主要介绍了Linux下配置jdk环境的方法,非常不错,具有一定的参考借鉴价值 ,需要的朋友可以参考下
    2019-05-05
  • Centos7 firewall和docker冲突问题及解决过程

    Centos7 firewall和docker冲突问题及解决过程

    本文描述了一个在CentOS 7上使用firewalld和Docker容器的问题,当firewalld启动或重启时,会从iptables中移除Docker的规则,导致Docker容器中的服务无法访问,最终,作者通过先启动或重启防火墙,然后重启Docker服务,并删除有问题的容器,重新创建容器来解决了问题
    2025-12-12

最新评论