同事操作两个数据源保持事务一致_终于有人把分布式事务说清楚了

前言

这篇文章将给大家介绍一下对分布式事务的一些见解,并讲解分布式事务处理框架 TX-LCN 的执行原理,错误之处望各位不吝指正。

149f2a4053b92c6e2ddfcb0af70ec87e.png

1. 什么情况下需要使用分布式事务?

使用的场景很多,先举一个常见的:在微服务系统中,如果一个业务需要使用到不同的微服务,并且不同的微服务对应不同的数据库。

打个比方:电商平台有一个客户下订单的业务逻辑,这个业务逻辑涉及到两个微服务,一个是库存服务(库存减一),另一个是订单服务(订单数加一),示意图如下:

85e95c733d5bf00b5bb6995f37d5b951.png

如果在执行这个业务逻辑时没有使用分布式事务,当库存与订单其中一个出现故障时,就很可能出现这样的情况:库存数据库的值减少了 1,但是订单数据库没有变化;或是库存没变化,多了一个订单,也就是出现了数据不一致现象。

所以在类似的场合下我们要使用分布式事务,保证数据的一致性。

2. 分布式事务的解决思路

2.1引入:MySQL 中的两阶段提交策略

在谈分布式事务的解决思路之前,我们先来看看单一数据源是如何做事务处理的,我们可以从中获取一些启发。

我们以 MySQL 的 InnoDB 引擎为例,由于 MySQL 中有两套日志机制,一套是存储层的 redo log,另一套是 server 层的 binlog,每次更新数据都要对两个日志进行更新。为了防止写日志时只写了其中一个而没有写另外一个,MySQL 使用了一个叫两阶段提交的方式保证事务的一致性。具体是这样的:

假设创建一个这样的数据库:mysql> create table T(ID int primary key, c int);, 然后执行一条这样的更新语句:mysql> update T set c=c+1 where ID=2;

这条更新语句的执行流程是这样子的:

  1. 首先执行器会找引擎取 ID=2 这一行数据
  2. 拿到数据后会把数据进行+1 操作,然后调用引擎接口把新数据写入
  3. 引擎将数据更新到内存中,并将操作记录到 redo log 里,此时 redo log 处于 prepare 状态。但它不会提交事务,只是通知执行器已经完成任务,可以随时提交。
  4. 执行器生成这个操作的 binlog,并把 binlog 写入磁盘
  5. 最后执行器调用引擎的事务接口,把 redo log 改为提交状态,更新完成。

在上述过程中,redo log 写完后没有直接提交,而是处于 prepare 状态,等通知执行器并把 binlog 写完后,redo log 再进行提交。这个过程就是两阶段提交,这是一个精妙的设计。

可能你会问为什么要有两阶段提交?如果不采用两阶段提交的话,也就是使用一阶段提交,那就相当于按顺序执行写 redo log 和 binlog,如果写完 redo log 后系统出现了故障,那么就会只有 redo log 记录了操作,binlog 没有记录,造成数据不一致;使用两阶段提交的话,假设写完 redo log 后系统出现了故障,由于事务还没有提交,所以可以顺利回滚。

两阶段提交的设计还有什么好处?首先要奠定一个概念:一个操作执行的时间越长,这个操作就越有可能失败。打个比方,你吃饭要用 20 分钟,上厕所要用 1 分钟,在吃饭的过程中收到微信消息的概率肯定比去上厕所的过程中收到微信消息的概率大。由于在数据库中更新操作的时间要远大于提交事务的时间,所以先把更新操作做完,等所有耗时操作都做完最后再提交事务,能够最大程度保证事务执行成功。

2.2分布式事务的两阶段提交策略

根据上述的两阶段提交策略,分布式事务也可以采取类似的办法完成事务。

在第一阶段,我们要新增一个事务管理者的角色,通过它来协调各个数据源。还是拿开头的订单案例讲解,在执行下订单的逻辑时,先让各个数据库去执行各自的事务,比如从库存中减 1,在订单库中加 1,但是完成后不提交,只是通知事务管理者已经完成了任务。

cf6ce2c700dcc4810a191feeccb1fba6.png

到了第二阶段,由于在阶段一我们已经收到了各个数据源是否就绪的信息,只要有一个数据源没有就绪,在第二阶段就通知所有数据源回滚;如果全部数据源都已经就绪,就通知所有数据源提交事务。

b51805f8928ddcf3b818d84050b6bf8f.png

总结一下这个两阶段提交的过程就是:首先事务管理器通知各个数据源进行操作,并返回是否准备好的信息。等所有数据源都准备好后,再统一发送事务提交(回滚)的通知让各个数据源提交事务。由于最后的提交操作耗时极短,所以操作失败的可能性会很低。

那么这个两阶段提交协议可能存在什么缺点呢?很可能存在被阻塞的问题,假如其中一个数据源出现了某些问题阻塞了,既不能返回成功信息,也不能返回失败信息,那么整个事务将被阻塞。对应的策略是添加一些倒计时的操作,或者是重新发送消息。

3. 分布式事务框架 TX-LCN

讲了这么多理论的知识,下面讲解一款真正应用在生产中的分布式事务框架 TX-LCN 的运行原理。(典型的分布式事务框架不止 TX-LCN,比如还有阿里的 GTS,不过 GTS 是收费的,TX-LCN 是开源的)

我们先看一下官方文档中给出的运行原理示意图:

fad855d7adb3f28691b10b7c17120ccd.png

思路和我们上面讲的两阶段分布式事务处理流程差不多(有小不同),核心步骤分为 3 步:

  1. 创建事务组:在事务发起方开始执行业务代码之前先调用 TxManager 创建事务组对象,然后拿到事务表示 GroupId 的过程。简单来说就是对这次下订单的操作在事务管理中心里创建一个对象,拿到一个 id。
  2. 加入事务组:参与方在执行完业务方法后,将该模块的事务信息通知给 TxManager 的操作。也就是指各个数据源(各个服务)完成操作后,和事务管理中心说一声,注册一下自己。
  3. 通知事务组:发起方执行业务代码后,将发起方执行结果状态通知给 TxManager,TxManager 将根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方。和客户打交道的下订单服务会收到减库存和加订单是否成功消息,它会把这两个消息通知给事务管理者,事务管理者根据情况通知两个库存服务提交事务或回滚事务。

目前网上有不错的 TX-LCN 执行源码分析文章:

文章中跟着源码走一遍会发现和上面的流程图差不多,落实到代码中有一些精彩的地方,比如:

public Object runTransaction(DTXInfo dtxInfo, BusinessCallback business) throws Throwable {        if (Objects.isNull(DTXLocalContext.cur())) {            DTXLocalContext.getOrNew();        } else {            return business.call();        }        log.debug("");        DTXLocalContext dtxLocalContext = DTXLocalContext.getOrNew();        TxContext txContext;        // ---------- 保证每个模块在一个DTX下只会有一个TxContext ---------- //        if (globalContext.hasTxContext()) {            // 有事务上下文的获取父上下文            txContext = globalContext.txContext();            dtxLocalContext.setInGroup(true);            log.debug("Unit[{}] used parent's TxContext[{}].", dtxInfo.getUnitId(), txContext.getGroupId());        } else {            // 没有的开启本地事务上下文            txContext = globalContext.startTx();        }        //......} 

这段代码保证了每个模块下只会有一个 TxContext,换个说法就是假设一个业务逻辑不是操作不同的数据源,而是对同一个数据源执行多次相同的操作,那么该数据源对应的模块在 DTX 下会只有一个 TxContext

9158541341e857d81ef0805511793c86.png

LCN 的事务协调机制

LCN 的口号是:LCN 并不生产事务,LCN 只是本地事务的协调工。大家肯定会有个疑问,它不生产事务,那么它是怎么控制各个模块在完成事务的逻辑操作之后不马上提交,而是等到 TxManager 最后一起通知各模块提交的呢?

因为每个模块都是一个 TxClient,每个 TxClient 下都有一个连接池,是框架自定义的连接池,对 Connection 使用静态代理的方式进行包装。

public class LcnConnectionProxy implements Connection {    private Connection connection;    public LcnConnectionProxy(Connection connection) {        this.connection = connection;    }    /**     * notify connection     *     * @param state transactionState     * @return RpcResponseState RpcResponseState     */    public RpcResponseState notify(int state) {        try {            if (state == 1) {                log.debug("commit transaction type[lcn] proxy connection:{}.", this);                connection.commit();            } else {                log.debug("rollback transaction type[lcn] proxy connection:{}.", this);                connection.rollback();            }            connection.close();            log.debug("transaction type[lcn] proxy connection:{} closed.", this);            return RpcResponseState.success;        } catch (Exception e) {            log.error(e.getLocalizedMessage(), e);            return RpcResponseState.fail;        }    }    @Override    public void setAutoCommit(boolean autoCommit) throws SQLException {        connection.setAutoCommit(false);    }    //......} 

连接池在没有接收到通知事务之前会一直占有着这次分布式事务的连接资源。等到最后 TxManager 通知 TxClient 时,TxClient 才会去执行相应的提交或回滚。所以 LCN 的事务协调机制相当于是拦截了一下连接池,控制了连接的事务提交。

fb082a5e1d44e153b60a8502c7477bac.png

image

LCN 的事务补偿机制

由于我们不能保证事务每次都正常执行,如果在执行某个业务方法时,本应该执行成功的操作却因为服务器挂机或网络抖动等问题导致事务没有正常提交,这种场景就需要通过补偿来完成事务。

在这种情况下 TxManager 会做一个标示;然后返回给发起方。告诉他本次事务有存在没有通知到的情况,然后 TxClient 再次执行该次请求事务。

最后

欢迎大家关注我的公众号【程序员追风】,文章都会在里面更新,整理的资料也会放在里面。


作者:程序员追风
链接:https://www.jianshu.com/p/2973fbd8c8af
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

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

相关文章

数据安全引担忧?get它,让你吃一颗“定心丸”

网络购物、在线外卖、远程教育、共享单车……如今,这些数字化的消费场景在个人生活中早已司空见惯。同时,在数字化浪潮下,越来越多的企业意识到大数据资产的价值,并试图推动其数字化转型。数据经济飞速发展,带来便捷和…

rpm 安装RabbitMQ 3.7.24版本-User can only log in via localhost

登录遇到问题&#xff1a;User can only log in via localhost 解决问题&#xff1a; 找到这个文件rabbit.app /usr/lib/rabbitmq/lib/rabbitmq_server-3.7.7/ebin/rabbit.app将&#xff1a;{loopback_users, [<<”guest”>>]}&#xff0c; 改为&#xff1a;{loo…

TableStore轻松实现轨迹管理与地理围栏

一、方案背景 轨迹管理系统日常生活中使用非常普遍&#xff0c;如外卖派送轨迹、快递物流流转、车辆定位轨迹等。该场景与地理位置管理类似&#xff0c;核心点与瓶颈都在数据库的存储性能与查询能力&#xff0c;同时需要时间字段正序排列&#xff0c;保证轨迹点顺序&#xff1…

i 智慧 | 为云而生、多快好省,这就是星星海的style!

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 晶少责编 | 阿秃出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09;不久之前&#xff0c;腾讯云刚刚对外上新了一款服务器&#xff0c;号称深度自研且独一无二&#xff0c;其名字也十二分的卡哇伊&#xff0…

看图了解RocksDB

它是一个高性能的Key-Value数据库。设计了完善的持久化机制&#xff0c;同时保证性能和安全性。能够良好的支持范围查询&#xff0c;因为K-V记录就是按照Key来排序的。 下图为写入的流程&#xff1a; 可以看到主要的三个组成部分&#xff0c;内存结构memtable&#xff0c;类似…

当Kubernetes应用遇到阿里分批发布模式

对于熟悉Kubernetes的用户来说&#xff0c;应该知道当你的应用程序一旦部署到Kubernetes以后&#xff0c;Kubernetes能够自动帮你管理应用程序&#xff0c;当Pod发生故障后可以自动调度重建&#xff0c;确保服务的持续可用。但Kubernetes的原生发布策略难以满足生产级别的发布要…

一分钟搭建、运行、测试SSM项目

pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 …

1 io口 stm32_从STM32分享各种硬件以及总线之GPIO简介

今天有网友私信我&#xff0c;问我怎么没有更新文章&#xff0c;我有点惭愧没有能够及时更新文章。因为都没有太多时间准备&#xff0c;所以更新的有点慢&#xff0c;但是我还是决定每天给大家分享一点知识。之前我们讲了STM32的时钟&#xff0c;讲完时钟之后我开始为大家讲一些…

亚太CDN年度盛典:阿里云姚伟斌发表《场景化的CDN技术》主题演讲

在11月14日的2018亚太CDN年度盛典上&#xff0c;阿里云资深技术专家姚伟斌&#xff08;文景&#xff09;发表了《场景化的CDN技术》主题演讲&#xff0c;他认为&#xff0c;当下的CDN&#xff0c;需要根据过去CDN的发展历程&#xff0c;更清晰地分析客户不同场景下的不同需求&a…

IBM在中国发布Cloud Paks,牵手神州数码,助力企业云转型步入“第二篇章”

2019年11月5日&#xff0c;北京盘古大观IBM大中华区总部&#xff0c;2501会议室。不到80平的会议室里挤了50多人&#xff0c;包括记者、分析师&#xff0c;以及来自IBM中国和神州数码的高管和专家—— 参会记者&#xff1a;通常是开会的地方越小&#xff0c;事儿越大…… 神州…

Vue 快速集成ElementUI

App.vue作为Vue项目的主窗口<router-view></router-view>让路由生效 main.js负责加载插件组件等 views&#xff1a;放页面 components&#xff1a;放组件 router&#xff1a;路由配置&#xff1a;相当于springmvc的视图解析器将url和组件进行关联Element UI 后台管…

双11个性化推荐背后,阿里云“舜天”如何应对百亿次挑战?

2018天猫双11在技术世界&#xff0c;创下不少新记录&#xff0c;其中有一个记录是11日当天阿里全平台共为用户做个性化推荐453亿次&#xff0c;这些推荐的图片长度加起来可以绕地球70圈。 当你在天猫/手淘上买买买的时&#xff0c;图片会以不同格式或分辨率来转码呈现&#xf…

重磅发布!阿里云推PostgreSQL 10 高可用版

2015年&#xff0c;阿里云宣布正式推出RDS for PostgreSQL服务,届时&#xff0c;阿里云成为国内首家同时支持MySQL、SQL Server 和PostgreSQL关系型数据库的云计算服务商。 用户在云上就能享受PostgreSQL引擎带来的对SQL标准和NoSQL的高度兼容、强大的处理复杂查询能力、以及丰…

axios专栏

axios 基于promise用于浏览器和node.js的http客户端 支持浏览器和node.js 支持promise 能拦截请求和响应 自动转换JSON数据 能转换请求和响应 数据 axios基础用法 get和 delete请求传递参数 通过传统的url 以 ? 的形式传递参数 restful形式传递参数 通过params 形式传递参数…

【双11】阿里云边缘节点ENS助力淘宝构建音视频通信网络

前言 淘宝在2016年推出直播平台&#xff0c;和娱乐直播性质不同&#xff0c;电商直播的主角多为网红店铺及网红达人&#xff0c;以直播带动产品售卖。在淘宝的双11流量加持之下&#xff0c;淘宝直播平台关注度持续攀升&#xff0c;通常的网红店主一场直播带来的收益不亚于一场…

护航Lazada双11购物节 阿里云CDN全球化火力全开

2018年11月12日零点——东南亚最大的电子商务公司Lazada 2018双11购物节正式收官。这是一场超过2000万的消费者在Lazada网站上及APP上浏览和疯狂抢购的盛会。 Lazada是东南亚最大B2C平台&#xff0c;业务范围覆盖印度尼西亚、马来西亚、菲律宾、新加坡、泰国和越南六个东南亚国…

AI行业真实现状:做芯片没工作,做视觉、语音血赚

最近&#xff0c;深圳前瞻产品研究院发布了《2019年人工智能行业现状和发展趋势报告》&#xff0c;笔者看过之后&#xff0c;感觉真是用了心&#xff0c;这不仅是一份数据报告&#xff0c;更像一本“扫盲手册”。众人皆知&#xff0c;AI血赚&#xff0c;当我们谈人工智能&#…

JSON数据从OSS迁移到MaxCompute最佳实践

本文为您介绍如何利用DataWorks数据集成将JSON数据从OSS迁移到MaxCompute&#xff0c;并使用MaxCompute内置字符串函数GET_JSON_OBJECT提取JSON信息。 数据上传OSS 将您的JSON文件重命名后缀为TXT文件&#xff0c;并上传到OSS。本文中使用的JSON文件示例如下。 {"store&…

在MaxCompute上分析IP来源的方法

淘宝IP库 淘宝IP地址库官网地址http://ip.taobao.com/&#xff0c;其查询接口[http://ip.taobao.com/service/getIpInfo.php?ip[ip地址字串]](http://ip.taobao.com/service/getIpInfo.php?ip[ip地址字串])&#xff0c;如下所示&#xff1a; 但是在MaxCompute中禁止使用http…