当前位置:首页 > 问答 > 正文

Kafka事务那些事儿,从入门到实操边聊边学,别太严肃

(来源:知乎专栏《Kafka实战笔记》) 好嘞,咱今天就唠唠Kafka事务这个事儿,别一听到“事务”俩字就头大,觉得又是数据库里那种复杂玩意儿,其实吧,你完全可以把它想象成一种“打包”机制,平时你往Kafka里发消息,是不是一条一条发的?事务呢,就是允许你把好几条消息,甚至是从Kafka读一些消息再发一些消息这些操作,打包成一个“原子操作”,这个包里的所有操作,要么全部成功,要么全部失败回滚,不会出现成功一半失败一半的尴尬局面。

(来源:Kafka官方文档概览) 那为啥需要这个“打包”功能呢?我给你举个特别常见的例子,咱就叫他“银行转账”吧,假设你要从A账户转100块钱到B账户,这个操作在系统里可能对应两条消息:一条是“A账户扣减100元”,另一条是“B账户增加100元”,你要是分开两条消息发,万一“扣钱”的消息发出去了,但“加钱”的消息因为网络抖动啥的没发成功,那不就坏菜了?A的钱没了,B的钱没到账,这锅谁背?有了事务,你就可以把这两条消息放到一个事务里,保证它们同生共死,要么都成功,要么都当没发生过,这样账就对得上了。

(来源:个人博客《Kafka事务的通俗理解》) 要实现这个,Kafka玩了几个小花招,它得有个“事务协调器”(Transaction Coordinator),你可以把它当成是打包操作的“包工头”,你的应用程序(生产者)在开始干活前,得先跟这个“包工头”打个招呼,说“老大,我要开始一个事务了”(这步叫initTransactions),包工头”给你个身份牌,你就开始正常发送消息,但这时候发的消息都还只是“预提交”状态,相当于把要打包的东西先整理好,但还没封箱,等你所有消息都发送妥当了,你再跟“包工头”说“我搞定了,可以提交了”(调用commitTransaction)。“包工头”就会在Kafka内部一个特殊的主题里写一条记录,说“某某事务成功提交啦!”,反之,如果你中途发现不对劲,可以喊“回滚”(abortTransaction),那“包工头”就会标记这个事务失败,之前“预提交”的那些消息就会被清理掉,消费者也看不到它们。

Kafka事务那些事儿,从入门到实操边聊边学,别太严肃

(来源:技术社区讨论帖《Kafka事务ID的重要性》) 这里有个关键点,你得给你的生产者设置一个唯一的transactional.id,这个ID有啥用呢?主要是为了防止“僵尸实例”捣乱,比如你的应用程序突然崩溃了,然后重启了,一个新的生产者实例用同一个ID上线了,这时候,“包工头”就能通过这个ID识别出:“哦,之前那个家伙可能挂了,现在你是新来的,我来帮你把之前那个没完成的事务清理掉,避免它占着茅坑不拉屎,然后你重新开始吧。”这保证了即使程序出问题,也不会留下脏数据。

(来源:实战项目经验总结) 光生产者这边保证还不够,消费者那边也得配合,消费者得设置一个叫isolation.level的参数为read_committed,这意思是告诉消费者:“老弟,你只去读那些已经明确被‘包工头’标记为提交成功的事务消息,那些还在进行中或者失败了的‘预提交’消息,你就当没看见。”这样就确保了消费者读到的数据始终是保持一致性的“干净”数据。

Kafka事务那些事儿,从入门到实操边聊边学,别太严肃

(来源:个人学习笔记《第一次配置Kafka事务踩坑记》) 说起来简单,上手配置的时候可能还是会遇到点小麻烦,你的Kafka集群得先配置好transaction.state.log.replication.factortransaction.state.log.min.isr这些参数(别怕,这大概是唯一需要接触的几个专业点儿的词了,就是保证事务日志本身高可用的),不然事务功能可能启动不了,然后在代码里,大概的步骤是这样的:

  1. 给生产者配置上transactional.id,并开启幂等性(enable.idempotence=true,这个能防止消息重复,是事务的基础)。
  2. 调用initTransactions()方法初始化事务。
  3. 开启事务beginTransaction()
  4. 像平常一样用send()方法发消息,但这会儿消息都在缓存里等着。
  5. 干得差不多了,就commitTransaction()提交。 万一中间出错,就抓一下异常,然后调用abortTransaction()回滚。

(来源:与同事的技术讨论) 最后咱得唠点实在的,不是所有场景都需要上事务这把“牛刀”,事务毕竟会带来一些性能开销,因为多了和“包工头”的通信,还有写那些内部记录的操作,如果你只是单条发消息,或者允许短暂的不一致,那可能用个幂等生产者(保证不重复不丢失)就足够了,但当你确实需要维护多个操作之间的原子性,比如咱们刚才说的转账,或者处理订单时同时更新库存和生成物流单,这种跨消息的强一致性场景,那事务就是你的好帮手了。

Kafka事务就是个高级版的“打包快递”服务,让你能安心地处理一组关联操作,核心思想就是“All or Nothing”,刚开始用可能会觉得步骤有点多,但摸清楚“包工头”(事务协调器)的脾气,配置好参数,多试几次也就熟了,希望这么聊下来,你能觉得它没那么神秘和严肃了。