解析Apache Kafka中的事务机制
这篇博客文章并不是关于使用事务细节的教程,我们也不会深入讨论设计细节。相反,我们将在适当的地方链接到JavaDocs或设计文档,以供希望深入研究的读者使用。
为什么交易?
我们在Kafka中设计的事务主要用于那些显示“读-进程-写”模式的应用程序,其中的读和写来自于异步数据流,比如Kafka主题。这种应用程序通常称为流处理应用程序。
第一代流处理应用程序可以容忍不准确的处理。例如,使用web页面印象流并生成每个web页面的视图聚合计数的应用程序可以容忍计数中的一些错误。
然而,随着这些应用程序的流行,对具有更强语义的流处理应用程序的需求也在增长。例如,一些金融机构使用流处理应用程序来处理用户帐户上的借方和贷方。在这些情况下,不能容忍处理过程中的错误:我们需要准确地一次处理所有消息,没有例外。
更正式地说,如果流处理应用程序使用消息a并生成消息B,使得B = F(a),那么仅一次处理就意味着如果且仅当成功生成B时才使用a,反之亦然。
使用配置为至少一次传递语义的普通Kafka生产者和消费者,流处理应用程序可能会在以下方面失去一次处理语义:
- 由于内部重试,生产者.send()可能导致消息B的重复写入。这是由幂等生产者解决的,并不是本文其余部分的重点。
- 我们可能会重新处理输入消息A,导致将重复的B消息写入输出,这违反了一次处理语义。如果流处理应用程序在写入B之后但在将A标记为已使用之前崩溃,则可能发生重新处理。因此,当它恢复时,它将再次消耗A并再次写入B,从而导致重复。
- 最后,在分布式环境中,应用程序会崩溃,甚至更糟!-暂时失去与系统其余部分的连接。通常,会自动启动新实例来替换那些被认为丢失的实例。通过这个过程,我们可能会有多个实例处理相同的输入主题,并写入相同的输出主题,从而导致输出重复,并违反一次处理语义。我们称之为“僵尸实例”问题。
我们在Kafka中设计了事务api来解决第二个和第三个问题。事务通过使这些周期成为原子性的,并通过促进僵死的隔离,从而在读写周期中实现精确的一次处理。
事务性语义
原子多分区写道
事务允许对多个Kafka主题和分区进行原子写入。事务中包含的所有消息都将被成功写入,或者一个也不写入。例如,处理过程中的错误可能导致事务中止,在这种情况下,来自事务的任何消息都不会被使用者读取。现在我们来看看它是如何实现原子读写周期的。
首先,让我们考虑原子读写周期的含义。简而言之,这意味着如果一个应用程序使用一个消息的抵消X topic-partition tp0,和写消息B topic-partition tp1在消息上做一些处理,B = F (a),然后read-process-write周期是a和B原子只有在消息被认为成功地消耗和发表在一起,要么一无所有。
现在,只有当消息A的偏移量X标记为已使用时,才会认为它是从主题分区tp0使用的。将偏移量标记为已使用的偏移量称为提交偏移量。在Kafka中,我们通过写入内部Kafka主题offsets主题来记录偏移量提交。仅当消息的偏移量提交到偏移量主题时,才认为该消息已被消耗。
因此从一个偏移量提交只是另一个写一个卡夫卡的话题,因为消息被认为是只有当其抵消消费承诺,原子还写跨多个主题和分区使原子read-process-write周期:提交的抵消X的补偿主题写的消息B tp1将单个事务的一部分,因此原子。
僵尸击剑(Zombie fencing)
我们通过要求为每个事务生产者分配一个称为transaction .id的惟一标识符来解决zombie实例的问题。这用于跨流程重新启动标识相同的生产者实例。
API要求事务生产者的第一个操作应该是显式注册其事务。使用Kafka集群的id。当它这样做时,Kafka代理使用给定的事务检查打开的事务。id并完成它们。它还增加与transaction .id关联的epoch。epoch是存储在每个transaction .id中的内部元数据。
一旦epoch被碰撞,任何具有相同事务的生产者。身份证和旧时代被认为是僵尸,被隔离。来自这些生产者的未来事务写将被拒绝。
读事务消息
现在,让我们将注意力转向在读取作为事务的一部分写入的消息时提供的保证。
Kafka使用者只会在事务被提交时才会向应用程序提交事务消息。换句话说,使用者不会交付作为开放事务一部分的事务性消息,也不会交付作为中止事务一部分的消息。
值得注意的是,上面的保证没有达到原子读取。特别是,当使用Kafka使用者来消费来自主题的消息时,应用程序将不知道这些消息是否作为事务的一部分写入,因此它们不知道事务何时开始或结束。进一步说,一个给定的消费者不保证订阅所有分区事务的一部分,它没有发现这个方法,这就很难保证所有的信息是一个事务的一部分最终会被一个消费者。
简而言之:Kafka保证使用者最终只交付非事务性消息或提交的事务性消息。它将从打开的事务中保留消息,并从中止的事务中过滤出消息。
Java中的事务API
事务特性主要是一个服务器端和协议级特性,任何支持它的客户端库都可以使用它。用Java编写的“读-处理-写”应用程序,使用Kafka的事务API,看起来应该是这样的:
第1-5行通过指定事务设置生产者。配置id并将其注册到initTransactions API。inittransactions()返回后,由具有相同事务的生产者的另一个实例启动的任何事务。id会被关闭和隔离。
第7-10行指定KafkaConsumer应该只读取非事务性消息,或者从它的输入主题中提交事务性消息。流处理应用程序通常在多个读写阶段处理其数据,每个阶段使用前一阶段的输出作为其输入。通过指定read_committed模式,我们可以在所有阶段只执行一次处理。
第14-21行演示了读写循环的核心:我们使用一些记录,启动一个事务,处理使用的记录,将处理过的记录写入输出主题,将使用的偏移量发送到偏移量主题,最后提交事务。根据上面提到的保证,我们知道偏移量和输出记录将作为一个原子单元提交。
事务是如何工作的
在本节中,我们将简要概述上述事务api引入的新组件和新数据流。为了更详尽地讨论这个主题,您可以阅读原始设计文档,或者观看介绍事务的Kafka峰会演讲。
下面内容的目标是在调试使用事务的应用程序时,或者在尝试调优事务以获得更好的性能时,提供一个心智模型。

事务协调器和事务日志
Kafka 0.11.0中的transactions API引入的组件是事务协调器和上图右侧的事务日志。
事务协调器是在每个Kafka代理中运行的模块。事务日志是一个内部kafka主题。每个协调器在事务日志中拥有一些分区子集。其代理为其领导的分区。
每一个事务。id通过一个简单的哈希函数映射到事务日志的特定分区。这意味着只有一个协调器拥有给定的transaction .id。
通过这种方式,我们利用Kafka的rock solid复制协议和leader选择过程来确保事务协调器总是可用的,并且所有事务状态都被持久地存储。
值得注意的是,事务日志只存储事务的最新状态,而不是事务中的实际消息。消息仅存储在实际的主题分区中。事务可以处于“进行中”、“准备提交”和“完成”等不同状态。存储在事务日志中的就是这种状态和相关的元数据。
数据流
在较高的层次上,数据流可以分为四种不同的类型。
A:生产者和事务协调者的交互
执行事务时,生产者向事务协调器发出以下请求:
initTransactions API注册一个事务。id与协调器。此时,协调器将使用该事务关闭任何挂起的事务。id和碰撞的时代,以栅栏出僵尸。每个生产者会话只发生一次。
当生产者在事务中第一次将数据发送到一个分区时,该分区首先向协调器注册。
当应用程序调用commitTransaction或abortTransaction时,将向协调器发送一个请求,以开始两阶段提交协议。
B:协调器和事务日志的交互
随着事务的进展,生产者发送上述请求来更新协调器上事务的状态。事务协调器将其拥有的每个事务的状态保存在内存中,并将该状态写入事务日志(以三种方式复制,因此是持久的)。
事务协调器是从事务日志中读写的惟一组件。如果给定的代理失败,则将选出一个新的协调器作为死代理拥有的事务日志分区的leader,它将从传入分区读取消息,以便为这些分区中的事务重建其内存状态。
C:生产者写数据到目标主题分区
在向协调器注册了事务中的新分区之后,生产者将数据正常地发送到实际的分区。这是同一个生产者。发送流,但是要进行一些额外的验证以确保生产者不受保护。
D:主题分区交互的协调器
在生产者发起提交(或中止)之后,协调器开始两阶段提交协议。
在第一阶段,协调器将其内部状态更新为“prepare_commit”,并在事务日志中更新此状态。一旦完成了这一步,就可以保证在任何情况下提交事务。
然后协调器开始第2阶段,将事务提交标记写入作为事务一部分的主题分区。
这些事务标记不公开给应用程序,而是由处于read_committed模式的使用者使用,以过滤掉中止的事务中的消息,并且不返回作为打开事务一部分的消息(即,在日志中但没有与之关联的事务标记的。
一旦写入了标记,事务协调器将事务标记为“完成”,并且生产者可以启动下一个事务。
实践中处理交易
既然我们已经理解了事务的语义以及它们是如何工作的,那么我们就将注意力转向编写利用事务的应用程序的实践方面。
如何选择一个transaction .id
事务。id在保护僵尸方面起着重要作用。但是保持一个标识符在不同的生产者会话之间是一致的,并且适当地隔离僵尸是有点棘手的。
正确隔离“僵尸”的关键是确保对于给定的transaction .id,读写周期中的输入主题和分区总是相同的。如果这不是真的,那么一些消息可能会通过事务提供的围栏泄漏。
例如,在一个分布式流处理应用程序中,假设主题分区tp0最初是由transactional处理的。T0 id。如果在以后的某个时候,它可以映射到另一个具有transactional的生产者。id T1,在T0和T1之间没有栅栏。因此,可以对来自tp0的消息进行重新处理,这违反了一次处理的保证。
实际上,必须存储输入分区和事务之间的映射。外部存储中的id,或者对其进行一些静态编码。Kafka Streams选择后一种方法来解决这个问题。
事务如何执行,以及如何调优它们
事务生产者的性能
让我们将注意力转向事务如何执行。
首先,事务只导致适度的写放大。增加的写是由于:
- 对于每个事务,我们都有额外的rpc向协调器注册分区。这些是成批的,因此我们的rpc比事务中的分区要少。
- 在完成事务时,必须将一个事务标记写入参与事务的每个分区。同样,事务协调器在单个RPC中批量处理为同一代理绑定的所有标记,因此我们在那里保存RPC开销。但是我们不能避免对事务中的每个分区进行一次额外的写操作。
- 最后,我们将状态更改写入事务日志。这包括对添加到事务中的每批分区的写操作、“prepare_commit”状态和“complete_commit”状态。
我们可以看到,开销与作为事务一部分写入的消息的数量无关。因此,提高吞吐量的关键是在每个事务中包含更多的消息。
实际上,对于在最大吞吐量下生成1KB记录的生产者,每100ms提交一条消息只会导致吞吐量降低3%。较小的消息或较短的事务提交间隔将导致更严重的降级。
增加事务持续时间的主要代价是增加了端到端延迟。请记住,读取事务性消息的使用者不会交付作为开放事务一部分的消息。因此,提交间隔的时间越长,应用程序的等待时间就越长,从而增加了端到端延迟。
事务消费者的性能
事务性消费者比生产者简单得多,因为它所需要做的就是:
- 筛选属于中止的事务的消息。
- 不返回作为开放事务一部分的事务消息。
因此,当以read_committed模式读取事务消息时,事务使用者的吞吐量没有下降。这样做的主要原因是,我们在读取事务性消息时保持零副本读取。
而且,使用者不需要任何缓冲来等待事务完成。相反,代理不允许它提前进行补偿,其中包括打开的事务。
因此,消费者是极其轻量级和高效的。有兴趣的读者可以在本文档中了解消费者设计的细节。
进一步的阅读
我们刚刚触及了Apache Kafka中事务的皮毛。幸运的是,几乎所有的设计细节都记录在网上。有关文件如下:
- 最初的Kafka KIP:它提供了关于数据流的详细信息和公共接口的概述,特别是随事务而来的配置选项。
- 原始设计文档:不适合胆小的人,这是权威的地方——源代码外!-了解如何处理每个事务RPC,如何维护事务日志,如何清除事务数据,等等。
- KafkaProducer javadocs:这是一个学习如何使用新api的好地方。页面开头的示例以及send方法的文档都是很好的起点。
结论
在这篇文章中,我们了解了Apache Kafka中事务API的关键设计目标,理解了事务API的语义,并对API的实际工作方式有了更深入的了解。
如果我们考虑一个读-进程-写循环,这篇文章主要讨论了读和写路径,处理本身就是一个黑盒。事实上,在处理阶段可以做很多事情,这使得仅使用事务api无法保证一次处理。例如,如果处理对其他存储系统有副作用,这里介绍的api不足以保证只进行一次处理。
Kafka Streams框架使用这里描述的事务api向上移动价值链,并为各种流处理应用程序提供一次处理,甚至包括那些在处理期间更新某些额外状态存储的应用程序。
将来的一篇博客文章将讨论Kafka流如何提供一次处理语义,以及如何编写利用它的应用程序。
最后,对于那些渴望了解上述api实现细节的人,我们将在另一篇后续博客文章中介绍一些更有趣的解决方案。
相关文章:

解析Apache Kafka中的事务机制
这篇博客文章并不是关于使用事务细节的教程,我们也不会深入讨论设计细节。相反,我们将在适当的地方链接到JavaDocs或设计文档,以供希望深入研究的读者使用。 为什么交易? 我们在Kafka中设计的事务主要用于那些显示“读-进程-写”模式的应用…...

Vue虚拟节点和渲染函数
1.虚拟节点 虚拟节点(dom)本质上就是一个普通的JS对象,用于描述视图的界面结构 2.渲染函数render():接收一个 createElement()函数创建的VNode Vue.component("board", {render: function(createElement) {return cr…...

后台交互-首页->与后台数据进行交互,wsx的使用
与后台数据进行交互wsx的使用 1.与后台数据进行交互 // index.js // 获取应用实例 const app getApp() const apirequire("../../config/app.js") const utilrequire("../../utils/util.js") Page({data: {imgSrcs:[{"img": "https://cd…...

【微服务保护】Sentinel 流控规则 —— 深入探索 Sentinel 的流控模式、流控效果以及对热点参数进行限流
文章目录 前言一、快速掌握 Sentinel 的使用1.1 什么是簇点链路1.2 Sentinel 的简单使用示例 二、Sentinel 流控模式2.1 直接模式2.2 关联模式2.3 链路模式 三、流控效果3.1 快速失败3.2 预热模式3.3 排队等待 四、对热点参数的流控4.1 热点规则4.2 热点规则演示 前言 微服务架…...

ZXing.Net 的Core平台生成二维码
一、引用 二、代码 帮助类 /// <summary>/// ZXing.NET 二维码帮助类/// </summary>public class ZXingHelper{/// <summary>/// 站点二维码的目录/// </summary>private static string QRCodeDirectory "QRCode";/// <summary>/// 使…...
【C++】假设给类分配的是栈的空间,那么计算机是如何访问栈中不同位置的对象的数据的呢?
2023年10月22日,周日上午 当在栈上创建一个对象时,计算机会为该对象分配一块连续的内存空间。该内存空间的位置在栈帧中,栈帧是用来存储函数调用信息和局部变量的一块内存区域。 栈帧中包含一个指针,称为栈指针(stack…...
iOS使用CoreML运用小型深度神经网络架构对图像进行解析
查找一个图片选择器 我用的是ImagePicker 项目有点老了,需要做一些改造,下面是新的仓库 platform :ios, 16.0use_frameworks!target learnings dosource https://github.com/CocoaPods/Specs.gitpod ImagePicker, :git > https://github.com/KevinS…...

使用Python打造微信高效自动化操作教程
引言 在如今数字化时代,人们对于效率的追求越来越强烈,尤其是在工作和学习中。自动化操作成为了提高生产力的有效途径之一,而PyAutoGUI和Pyperclip作为Python中的两个强大库,为我们实现自动化操作提供了便利。本文将向大家介绍如…...

怎么在爬虫中使用ip代理服务器,爬虫代理IP的好处有哪些?
随着互联网的快速发展,网络爬虫已经成为数据采集、分析和整理的重要工具。然而,随着网络技术的不断发展,许多网站都会采取反爬虫措施,以避免数据被恶意获取。在这种情况下,代理IP服务器就成为了爬虫们的必本备文工将具…...

Typora的相关配置(Typora主题、字体、快捷键、习惯)
Typora的相关配置(Typora主题、字体、快捷键、习惯) 文章目录 Typora的相关配置(Typora主题、字体、快捷键、习惯)[toc]一、主题配置二、字体配置查看字体名称是否可以被识别:如果未能正确识别: 三、习惯配置四、快捷键配置更改提供的功能的快捷键&#…...

守护进程深度分析
思考 代码中创建的会话,如何关联控制终端? 新会话关联控制终端的方法 会话首进程成功打开终端设备 (设备打开前处于空闲状态) 1、关闭标准输入输出和标准错误输出2、将 stdin 关联到终端设备:STDIN_FILENO > 03、将 stdout 关联到终端设…...

SpringAMQP
SpringAMQT RabbitMQ安装与部署RabbitMQ结构简单队列模型 SpringAMQP依赖引入配置RabbitMQ连接信息基本模型简单队列模型WorkQueue模型 发布订阅模型FanoutExchangeDirectExchangeTopicExchange 消息转换器 消息队列是实现异步通讯的一种方式,我们将从RabbitMQ为例开…...

深入探索Sharding JDBC:分库分表的利器
随着互联网应用的不断发展和用户量的不断增加,传统的数据库在应对高并发和大数据量的场景下面临着巨大的挑战。为了解决这一问题,分库分表成为了一个非常流行的方案。分库分表主流的技术包括MyCat和Sharding JDBC。我们来通过一张图来了解这两者有什么区…...

Java后端模拟面试 题集④
1.你先作个自我介绍吧 面试官您好,我叫张睿超,来自湖南长沙,大学毕业于湖南农业大学,是一名智能科学与技术专业的统招一本本科生。今天主要过来面试贵公司的Java后端开发工程师岗位。 大学里面主修的课程是Java、Python、数字图…...
中国5G产业全景图谱报告2022_挚物AIoT产业研究院
中国5G产业全景图谱报告2022_挚物AIoT产业研究院 产业结构 5G 产业结构主要包括接入网、传输网、核心网、电信运营商、网络配套服务商、5G 应用生态及产业服务 7 个主要板块。根据各版块中主要市场参与者提供的产品和服务,又下分子版块。 (1ÿ…...

设计链表复习
设计链表 class ListNode {int val;ListNode next;public ListNode() {}public ListNode(int val) {this.val val;}public ListNode(int val, ListNode next) {this.val val;this.next next;}}class MyLinkedList {//size存储链表元素的个数int size;//虚拟头节点ListNode…...

在 Visual Studio Code (VS Code) 中设置
在 Visual Studio Code (VS Code) 中设置代理服务器的详细教程如下: 打开 Visual Studio Code。 在顶部菜单栏中,点击 "File"(文件) > "Preferences"(首选项) > "Settings…...

2023年拼多多双11百亿补贴新增单件立减玩法介绍
2023年拼多多双11百亿补贴新增单件立减玩法介绍 拼多多启动了11.11大促活动,主题为“天天11.11,天天真低价”。消费者享受多重优惠,包括满减、百亿补贴和单件立减等。百亿补贴新增玩法,有超过20000款品牌商品参与单件立减活动。 …...
面试题 01.06. 字符串压缩
题目来源: leetcode题目,网址:面试题 01.06. 字符串压缩 - 力扣(LeetCode) 解题思路: 计算压缩后的字符串长度,如果该长度小于原字符串长度,返回压缩后的字符串,否则…...

那些你面试必须知道的webpack知识点
目录 1、webpack介绍和简单使用1.1 什么是webpack?1.2 安装webpack1.3 简单使用一下webpack 2、webpack的入口与输出2.1 入口(entry)2.2 输出(output) 3、入口多种配置方法3.1 多文件打包成一个文件3.2 多文件打包成多文件 4、loader的概念5、压缩打包HTML5.1 使用步…...

工作邮箱收到钓鱼邮件,点了链接进去无法访问,会有什么问题吗?
没事的,很可能是被安全网关拦截了。最近做勒索实验,有感而发,不要乱点击邮箱中的附件。 最初我们采用钓鱼邮件投递恶意载荷,发现邮件网关把我们的 exe/bat 程序直接拦截了,换成压缩包也一样拦截了,载荷始终…...
GO 基础语法和数据类型面试题及参考答案(上)
目录 Go 中变量定义方式有哪些?各有什么适用场景? 使用 : 定义变量的限制是什么? 全局变量可以使用 : 声明吗?为什么? Go 中如何声明一个多变量赋值?有哪些注意事项? 常量能否通过表达式赋值…...

AI架构师修炼之道
1 AI时代的架构革命 与传统软件开发和软件架构师相比,AI架构师面临着三重范式转换: 1.1 技术维度,需处理异构算力调度与模型生命周期管理的复杂性; 1.2 系统维度,需平衡实时性与资源约束的矛盾; 1.3 价…...
atc abc409E
原题链接:E - Pair Annihilation 题目背景: n 个点 n - 1 条边的有权无向图,每个点都有一个值,两个连通的点的值可以互相抵消,既将u 的 -1 传给 v 时可以抵消掉 v 的 1 并花费边权值;求最小花费。 考察算…...
大数据+智能零售:数字化变革下的“智慧新零售”密码
大数据+智能零售:数字化变革下的“智慧新零售”密码 大家好,今天咱们聊聊一个火到不行的话题:大数据在智能零售中的应用。这个领域,不仅是技术的“硬核战场”,更是商业创新的风口浪尖。谁能玩转数据,谁就能掌控消费者心智,实现销售爆发。 咱们不搞枯燥学术,而是用最“…...

layer norm和 rms norm 对比
Layer norm # Layer Norm 公式 mean x.mean(dim-1, keepdimTrue) var x.var(dim-1, keepdimTrue) output (x - mean) / sqrt(var eps) * gamma beta特点: 减去均值(去中心化)除以标准差(标准化)包含可学习参数 …...

FPGA定点和浮点数学运算-实例对比
在创建 RTL 示例时,经常使用 VHDL 2008 附带的 VHDL 包。它提供了出色的功能,可以高效地处理定点数,当然,它们也是可综合的。该包的一些优点包括: 有符号和无符号(后缀和后缀)定点向量。轻松将定…...
限流算法java实现
参考教程:2小时吃透4种分布式限流算法 1.计数器限流 public class CounterLimiter {// 开始时间private static long startTime System.currentTimeMillis();// 时间间隔,单位为msprivate long interval 1000L;// 限制访问次数private int limitCount…...
数据可视化大屏案例落地实战指南:捷码平台7天交付方法论
分享大纲: 1、落地前置:数据可视化必备的规划要素 2、数据可视化双路径开发 3、验证案例:数据可视化落地成效 在当下数字化转型浪潮中,数据可视化建设已成为关键环节。数据可视化大屏的落地,成为企业数据可视化建设的难…...
生物发酵展同期举办2025中国合成生物学与生物制造创新发展论坛
一、会议介绍 2025中国合成生物学与生物制造创新发展论坛暨上海国际合成生物学与生物制造展览会于2025年8月7-9日在上海新国际博览中心(浦东新区龙阳路2345号)召开,本次论坛汇聚了国内外顶尖学者、行业领袖及政策制定者,将围绕“…...