【杂谈】分布式事务——高大上的无用知识?

news/2025/3/14 20:37:49/文章来源:https://www.cnblogs.com/longfurcat/p/18772784

如何保证多个数据库操作的原子性?

很简单,用事务。具体到代码,在方法上加一个@Transactional,这样方法内部的entity操作都能在一个数据事务内。
abc三个entity,要么全部修改,要么一个不改。
@Transactional
public void someOperation() {aRepo.save(a);bRepo.save(b);cRepo.save(c);
}

那如果有个entity的修改是在另外一个服务上呢?

假设 b 这个数据属于另一个微服务,我们通过 FeignClient 远程调用它:

@Transactional
public void someOperation() {aRepo.save(a);bFeignClient.save(b);cRepo.save(c);
}

那么@Transactional就只能保证a,c两个修改的原子性,b的修改不受控制。

分布式事务的解决方案

分布式事务通常使用两阶段提交(2PC, Two-Pahse Commit)进行处理。

1.第一阶段(Prepare,准备阶段)
  • 事务协调器(TC,Transaction Coordinator)通知所有参与者(分支事务)执行事务操作,但不提交
  • 每个参与者执行本地事务并记录undo log(或锁定资源),然后向TC报告准备成功或失败
    • 使用行级锁(SELECT ... FOR UPDATE)锁定一行数据,无法被其他线程修改。
2.第二阶段(Commit/Rollback,提交或回滚阶段)
  • 如果所有参与者都准备成功,TC通知所有分支事务提交
  • 如果有任何一个失败,TC通知所有分支事务回滚。

就代码来讲,假设不使用XA命令。
一个分支事务会依次执行这5句SQL,一般执行完④成功后,就停住了,不再发新SQL给数据库。
分支事务会等待协调器TC的命令,如果可以执行,就继续执行COMMIT,否则就执行ROLLBACK。

-- 1. 开启事务
START TRANSACTION;

-- 2. 先锁定目标数据,确保后续不会有其他事务并发修改
SELECT * FROM table_b WHERE id = '12345' FOR UPDATE;

-- 3. 记录旧数据到 `undo_log`
INSERT INTO undo_log (table_name, record_id, old_value)
SELECT 'table_b', id, name FROM table_b WHERE id = '12345';

-- 4. 执行更新操作
UPDATE table_b SET name='www' WHERE id= '12345';

-- 5. 等待 TC 指令:
-- ✅ 如果 TC 说“可以提交”,则执行:
COMMIT;

-- ❌ 如果 TC 说“回滚”,则执行:
ROLLBACK;

如果分支事务COMMIT后,其他分支事务失败,则可以通过undo_log表来回滚数据。

分布式事务框架——Seata

高性能的AT模式
  • 在第一阶段,Seata直接修改数据库(和2PC不同,它不会锁定资源)
    • Seata会拦截SQL并记录undo log(修改前的数据),用于回滚
  • 在第二阶段:
    • 提交时:直接提交,无额外操作
    • 回滚时:用undo log恢复数据
相比于传统的2PC,它避免了长时间锁定资源,提高了性能。
案例代码:
@GlobalTransactional
public void someOperation() {aRepo.save(a);bFeignClient.save(b);cRepo.save(c);
}

有了Seata这种高性能框架,分布式事务为何还是不常见?

1. 业务通常不需要强一致性,仅需最终一致性

大多数业务场景对数据的一致性要求没有那么严格,只要能在一段时间内完成最终一致性,就足够了。

案例:用户余额充值与优惠券发放

假设你在一个电商平台充值 100 元,并且平台规定:首次充值 100 元以上,会赠送 10 元优惠券

假设充值和优惠卷发放是在两个独立的服务上,完全可以在充值完成后,写入MQ,然后优惠卷服务再处理消息。

只要最终结果一致就行。

2. 事务本地化

拆分微服务时,事务操作通常划归到一个服务内,不会跨服务。

比如一个系统,它的支付相关的操作,都在一个支付服务内。

3.Seata 仍然存在额外开销

比如额外的SQL解析;undo log表的维护,额外的数据库写入;额外的网络通信。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/898852.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu 22.04 LTS 基于 Docker 部署 WordPress

Ubuntu 22.04 LTS 基于 Docker 部署 WordPress 1. 引言 WordPress 是全球最受欢迎的内容管理系统 (CMS),使用 Docker 可以简化其部署过程。本教程将介绍如何在 Ubuntu 22.04 LTS 上使用 Docker 部署 WordPress。2. WordPress 简介 2.1 WordPress 是什么? WordPress 是全球最流…

7.接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 示例1:输入:height = [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示…

K近邻算法等

1. KNN算法和KD - tree总结 1.1 KNN算法 模型 K近邻(K - Nearest Neighbors,KNN)算法是一种基本的分类与回归方法。它的模型实际上是对特征空间的划分,给定一个训练数据集,对于新的输入实例,在训练数据集中找到与该实例最邻近的 \(K\) 个实例,然后根据这 \(K\) 个实例的…

正则表达式--java进阶day06

1.正则表达式2.正则表达式的规则、使用3.字符类讲解如图,单独一个a满足正则表达式的规则,所以返回true当删去[]后,正则表达式中的规则就会变为必须是abc,否则不满足条件,即使有一个a该规则是指a-d或者m-p,可以写成[a-dm-p]4.预定义字符类注意事项 正则表达式中存在数量问…

探秘Transformer系列之(13)--- FFN

从零开始解析Transformer,目标是:(1) 解析Transformer如何运作,以及为何如此运作,让新同学可以入门;(2) 力争融入一些比较新的或者有特色的论文或者理念,让老鸟也可以有所收获。探秘Transformer系列之(13)--- FFN 目录探秘Transformer系列之(13)--- FFN0x00 概述0x01…

EXCEL-时间函数

💖简介 在Excel中,时间函数用于处理和操作日期和时间数据; 以下是Excel中常用的时间函数及其常见应用场景的总结.📖函数 ⭐时间函数基础 🌟TIME语法:TIME(hour, minute, second) 功能:将小时、分钟、秒转换为时间序列号(0到0.99999999之间的数值)。 示例:TIME(9,30…

day29linux三剑客----sed

day29linux三剑客----sed单个正则字符还认识组合到一起就晕了,怎么办?本质还是对单个字符没理解.认识*认识.*组合到就一起就蒙了,为什么?还是没想明白.的意义,*的意义正则表达式,从左向右,逐步理解单个字符的意义怎么做? 1.思维脑图写没写? 2.每一个正则表达式的符号,…

3.14 学习记录

基于Android Studio 完成了简单的石家庄地铁购票APP

Android配置

将grade-wrapper.properties中地址改为 https://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-bin.zip 等待下载。一般要几个小时。

sqlserver 的视图创建

首先,什么是视图?视图是一种数据库对象,是从一个或者多个数据表或视图中导出的虚表,视图的结构和数据是对数据表进行查询的结果,只存放视图的定义,不存放视图对应的数据; 其结构和和数据是建立在对表的查询基础上,故表中的数据发生变化,从视图中查询出的数据也随之改变…

day:21 python——判断语句

一.if语句 (1)单分支: 格式: if 判断条件: 执行语句块 else: 执行语句块2 备注:判断条件 if中可以使用比较运算符,<,!=,==,>=,<=在学习自动化中也可以用if语句断言, 案例1: a=10 if a != 10: print("你中奖了") else: print("谢谢惠顾"…

clickhouse 开启认证

配置文件说明 默认路径:/etc/clickhouse-server/users.xml 密码存储类型 明文密码(不推荐) <password>qwerty</password> <!-- 直接明文存储 --> SHA256 哈希 <password_sha256_hex>5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d15…