关于分布式事务:一天吃透分布式事务面试八股文

本文曾经收录到Github仓库,该仓库蕴含计算机根底、Java根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等外围知识点,欢送star~ Github地址:https://github.com/Tyson0314/Java-learning 简介事务事务是应用程序中一系列紧密的操作,所有操作必须胜利实现,否则在每个操作中所作的所有更改都会被吊销。也就是事务具备原子性,一个事务中的一系列的操作要么全副胜利,要么一个都不做。事务应该具备 4 个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为 ACID 个性。 分布式事务分布式事务是指事务的参与者,反对事务的服务器,资源服务器以及事务管理器别离位于分布式系统的不同节点之上。通常一个分布式事务中会波及对多个数据源或业务零碎的操作。分布式事务也能够被定义为一种嵌套型的事务,同时也就具备了ACID事务的个性。 强一致性、弱一致性、最终一致性强一致性 任何一次读都能读到某个数据的最近一次写的数据。零碎中的所有过程,看到的操作程序,都和全局时钟下的程序统一。简言之,在任意时刻,所有节点中的数据是一样的。 弱一致性 数据更新后,如果能容忍后续的拜访只能拜访到局部或者全副拜访不到,则是弱一致性。 最终一致性 不保障在任意时刻任意节点上的同一份数据都是雷同的,然而随着工夫的迁徙,不同节点上的同一份数据总是在向趋同的方向变动。简略说,就是在一段时间后,节点间的数据会最终达到统一状态。 因为分布式事务计划,无奈做到齐全的ACID的保障,没有一种完满的计划,可能解决掉所有业务问题。因而在理论利用中,会依据业务的不同个性,抉择最适宜的分布式事务计划。 分布式事务的根底CAP实践Consistency(一致性):数据统一更新,所有数据变动都是同步的(强一致性)。 Availability(可用性):好的响应性能。 Partition tolerance(分区容错性) :可靠性。 定理:任何分布式系统只可同时满足二点,没法三者兼顾。 CA零碎(放弃P):指将所有数据(或者仅仅是那些与事务相干的数据)都放在一个分布式节点上,就不会存在网络分区。所以强一致性以及可用性失去满足。 CP零碎(放弃A):如果要求数据在各个服务器上是强统一的,然而网络分区会导致同步工夫有限缩短,那么如此一来可用性就得不到保障了。保持事务ACID(原子性、一致性、隔离性和持久性)的传统数据库以及对后果一致性十分敏感的利用通常会做出这样的抉择。 AP零碎(放弃C):这里所说的放弃一致性,并不是齐全放弃数据一致性,而是放弃数据的强一致性,而保留数据的最终一致性。如果即要求零碎高可用又要求分区容错,那么就要放弃一致性了。因为一旦产生网络分区,节点之间将无奈通信,为了满足高可用,每个节点只能用本地数据提供服务,这样就会导致数据不统一。一些恪守BASE准则数据库,(如:Cassandra、CouchDB等)往往会放宽对一致性的要求(满足最终一致性即可),一次来获取根本的可用性。 BASE实践BASE 是 Basically Available(根本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写。是对CAP中AP的一个扩大。 根本可用:分布式系统在呈现故障时,容许损失局部可用性能,保障外围性能可用。软状态:容许零碎中存在中间状态,这个状态不影响零碎可用性,这里指的是CAP中的不统一。最终统一:最终统一是指通过一段时间后,所有节点数据都将会达到统一。BASE解决了CAP中实践没有网络提早,在BASE中用软状态和最终统一,保障了提早后的一致性。BASE和 ACID 是相同的,它齐全不同于ACID的强一致性模型,而是通过就义强一致性来取得可用性,并容许数据在一段时间内是不统一的,但最终达到统一状态。 分布式事务解决方案分布式事务的实现次要有以下 6 种计划: 2PC 计划TCC 计划本地音讯表MQ事务Saga事务最大致力告诉计划2PC计划2PC计划分为两阶段: 第一阶段:事务管理器要求每个波及到事务的数据库预提交(precommit)此操作,并反映是否能够提交. 第二阶段:事务协调器要求每个数据库提交数据,或者回滚数据。 长处: 尽量保障了数据的强统一,实现老本较低,在各大支流数据库都有本人实现,对于MySQL是从5.5开始反对。 毛病: 单点问题:事务管理器在整个流程中表演的角色很要害,如果其宕机,比方在第一阶段曾经实现,在第二阶段正筹备提交的时候事务管理器宕机,资源管理器就会始终阻塞,导致数据库无奈应用。同步阻塞:在准备就绪之后,资源管理器中的资源始终处于阻塞,直到提交实现,开释资源。数据不统一:两阶段提交协定尽管为分布式数据强一致性所设计,但依然存在数据不一致性的可能,比方在第二阶段中,假如协调者收回了事务commit的告诉,然而因为网络问题该告诉仅被一部分参与者所收到并执行了commit操作,其余的参与者则因为没有收到告诉始终处于阻塞状态,这时候就产生了数据的不一致性。总的来说,2PC计划比较简单,老本较低,然而其单点问题,以及不能反对高并发(因为同步阻塞)仍然是其最大的弱点。 TCCTCC 的全称是:Try、Confirm、Cancel。 Try 阶段:这个阶段说的是对各个服务的资源做检测以及对资源进行 锁定或者预留。Confirm 阶段:这个阶段说的是在各个服务中执行理论的操作。Cancel 阶段:如果任何一个服务的业务办法执行出错,那么这里就须要 进行弥补,就是执行曾经执行胜利的业务逻辑的回滚操作。(把那些执行胜利的回滚)举个简略的例子如果你用100元买了一瓶水, Try阶段:你须要向你的钱包查看是否够100元并锁住这100元,水也是一样的。 如果有一个失败,则进行cancel(开释这100元和这一瓶水),如果cancel失败不论什么失败都进行重试cancel,所以须要放弃幂等。 如果都胜利,则进行confirm,确认这100元扣,和这一瓶水被卖,如果confirm失败无论什么失败则重试(会依附流动日志进行重试)。 这种计划说实话简直很少人应用,然而也有应用的场景。因为这个事务回滚实际上是重大依赖于你本人写代码来回滚和弥补了,会造成弥补代码微小。 本地音讯表本地音讯表的外围是将须要分布式解决的工作通过消息日志的形式来异步执行。消息日志能够存储到本地文本、数据库或音讯队列,再通过业务规定主动或人工发动重试。人工重试更多的是利用于领取场景,通过对账系统对预先问题的解决。 对于本地音讯队列来说外围是把大事务转变为小事务。还是举下面用100元去买一瓶水的例子。 1.当你扣钱的时候,你须要在你扣钱的服务器上新减少一个本地音讯表,你须要把你扣钱和写入减去水的库存到本地音讯表放入同一个事务(依附数据库本地事务保障一致性。 2.这个时候有个定时工作去轮询这个本地事务表,把没有发送的音讯,扔给商品库存服务器,叫他减去水的库存,达到商品服务器之后这个时候得先写入这个服务器的事务表,而后进行扣减,扣减胜利后,更新事务表中的状态。 3.商品服务器通过定时工作扫描音讯表或者间接告诉扣钱服务器,扣钱服务器本地音讯表进行状态更新。 4.针对一些异常情况,定时扫描未胜利解决的音讯,进行从新发送,在商品服务器接到音讯之后,首先判断是否是反复的,如果曾经接管,在判断是否执行,如果执行在马上又进行告诉事务,如果未执行,须要从新执行须要由业务保障幂等,也就是不会多扣一瓶水。 本地音讯队列是BASE实践,是最终统一模型,实用于对一致性要求不高的。实现这个模型时须要留神重试的幂等。 MQ事务基于 MQ 的分布式事务计划其实是对本地音讯表的封装,将本地音讯表基于 MQ 外部,其余方面的协定根本与本地音讯表统一。 ...

March 15, 2023 · 1 min · jiezi

关于分布式事务:一种基于柔性事务的分布式事务解决方案设计探究

1 背景市面上常见的有,2pc/3pc、tcc、saga等常见的分布式事务解决方案,然而理论施行起来框架比拟重,设计开发比拟繁琐,不易于疾速开发上手。本文提供一种基于柔性事务设计的简略易上手的分布式事务设计方案,用于解决常见的分布式事务常见场景。 2 常见分布式事务场景2.1 同步场景常见的场景,办法内依赖内部微服务多个同步接口,等同步接口返回再开展后续逻辑,如下图1形容。 图1 分布式事务同步场景 存在的问题:B/C失败后,A/B不能回滚,造成数据不统一? 2.2 异步场景办法内依赖内部微服务多个同步接口同时,本地事务提交并收回异步MQ,如下图2形容。 图2 分布式事务异步场景 存在的问题:询价零碎无奈保障本地事务和mq音讯的发送同时胜利或失败,会造成数据不统一。 3 解决方案3.1 数据模型设计事务表:记录每次同步办法执行的状态,包含:1-进行中(同步办法执行开始)、2-已实现(同步办法执行胜利)、3-失败(同步办法执行失败)、4-已回滚(回滚办法执行胜利); 办法调用表:记录一个残缺的事务内所有办法的执行前入参、同步办法接口、回滚接口、回滚入参、办法执行程序,如下图3形容: 图3 事务服务数据模型 3.2 设计原理原理:一个残缺事务内,1.首先每个办法提供回滚接口,其次,事务内每次同步办法执行时,优先保护入参数据到事务表,不便后续做回滚弥补;2.整个事务内某一个办法执行失败时,完结整个事务,并更新事务表状态=失败;3.事务表通过轮询状态status=3(失败)事务,调用回滚接口,利用回滚入参进行接口弥补;4.回滚逻辑:找到事务表中失败的执行办法的程序值,只调用小于失败程序值的所有回滚接口、入参,留神并不回滚失败值的接口,并依据程序倒序进行接口回滚弥补。 图4 回滚原理图 3.3 执行时序 图5 回滚执行时序图 3.4 回滚失败解决计划:事务服务的高可用保障:柔性事务前提是保障事务服务高可用性,重点保障;回滚服务重试机制:回滚接口失败重试机制,保证数据一致性;为了防止架构复杂度,做日志记录、报警、人工解决。4 留神问题回滚服务的幂等性:回滚做好业务防重和零碎防重,避免因为回滚带来的业务数据不统一;脏数据:整个事务中某一办法执行失败,后面调用办法的数据作为脏数据应用。简略的解决方案:依赖事务表整个事务执行状态来决定是否应用脏数据。但毛病就是这样会耦合业务逻辑;中心化:整个事务的保护齐全依赖事务服务实现,须要保障事务服务的高可用性;实时性:事务保护本计划通过定时工作保护,如果业务场景有实时性要求,形式能够改为:在整个事务中某一办法执行失败时,catch异样,catch内更新工作状态胜利时,间接进行回滚逻辑调用。5 总结除了通过惯例本地大事务保障事务完整性计划,本次计划提供了一套基于柔性事务回滚弥补的形式来保障分布式事务,通过保护事务服务和事务服务中心对应数据表,从而保障整个分布式事务的完整性。实现形式简略、轻量、易于操作,不便地解决了常见分布式事务场景。 作者:郑朋辉

October 24, 2022 · 1 min · jiezi

关于分布式事务:Seataphp-半年规划

文| 赵新(花名:于雨 ) :蚂蚁团体 Seata 我的项目开源负责人、凋谢原子开源基金会代码奉献之星 郭成(花名:星北 ) :Seata-php 我的项目独特发起人、蚂蚁团体技术专家 刘岳健:Seata-php 我的项目独特发起人、Hyperf 开发组成员、广东快客电子商务有限公司高级后端工程师 本文 5894 字 浏览 12 分钟 导语艰深地讲,seata-php 是 Seata 的 PHP 语言实现,它实现了 Java 和 PHP 之间的互通,让 PHPer 也能应用 seata-php 来实现分布式事务。 Seata 是一个十分成熟的分布式事务框架,在 Java 畛域是事实上的分布式事务技术标准平台。Seata 目前正在构建其多语言体系[1],整个体系蕴含了目前罕用的五大类语言:Java、Go、Python、JS 和 PHP。目前的态势是后四种语言都根据 Seata Java 版本构建起对应语言的实现。 除了谋求 Seata 多语言体系过程中因为开源价值要求构建 Seata 的 PHP 版本这个起因外,作为构建起 Web 1.0 时代技术根底 LAMP 架构中的要角,PHP 语言在电商和金融交易场景下仍然被宽泛应用。而这些场景对数据一致性要求十分强烈,这是构建 seata-php 最大的诱因,也是其技术价值所在。 PART. 1--Seata 架构与多语言体系 图片来自 Seata 官网Seata 总体架构由如下角色形成: - 事务协调器 Transaction Coordinator ...

August 17, 2022 · 4 min · jiezi

关于分布式事务:分布式事务一致性业界方案及实践经验总结

综述:日常需要常常须要用到分布式事务一致性保障,踩坑摸索中积攒了一些教训,联合各种内外部材料,对立做一次梳理,并在最初附上一些实践经验 举例一个业务场景:银行账户A 转账给银行账户 B 100 元引入问题和要求: A-100 和 B+100 都胜利,或都失败转账前 A+B 总额 == 转账后 A+B 总额同一段时间多笔转账间尽量可能并行且不相互影响转账实现后数据永恒保留不失落本地(单机)事务场景如果咱们的业务零碎不简单,能够在一个数据库、一个服务内对数据进行批改,实现转账,那么,咱们能够利用数据库事务,保障转账业务的正确实现。现有的关系型数据库在这方面曾经十分欠缺了 在单机数据库场景下,事务保障 ACID 要求 摘自维基百科: 原子性(Atomicity):一个事务(transaction)中的所有操作,或者全副实现,或者全副不实现,不会完结在两头某个环节。事务在执行过程中产生谬误,会被回滚(Rollback)到事务开始前的状态,就像这个事务素来没有执行过一样。即,事务不可分割、不可约简。[1]一致性(Consistency):在事务开始之前和事务完结当前,数据库的完整性没有被毁坏。这示意写入的材料必须完全符合所有的预设束缚、触发器、级联回滚等。[1]事务隔离(Isolation):数据库容许多个并发事务同时对其数据进行读写和批改的能力,隔离性能够避免多个事务并发执行时因为穿插执行而导致数据的不统一。事务隔离分为不同级别,包含未提交读(Read uncommitted)、提交读(read committed)、可反复读(repeatable read)和串行化(Serializable)。[1]持久性(Durability):事务处理完结后,对数据的批改就是永恒的,即使系统故障也不会失落。[1]在常见互联网业务场景下,都是读多写少,所以比拟多都是用 MVCC 技术,用版本号和快照的计划实现高并发(repeatable read)下的一致性 分布式事务现实情况下,绝大多数的互联网业务都是分布式系统,服务、资源、数据库等都不是单机部署,甚至可能横跨多家公司、运营商。所以在这种场景下,原有的本地事务保障曾经无奈满足需要 数据库外部分布式事务一些分布式数据库原生外部反对事务个性,能够大大减少业务方操作事务的复杂性,根本由数据库组件来解决问题目前常见的反对分布式事务的数据库举例: TDSQL(腾讯)TBase(腾讯)TiDB(PingCAP)Spanner(Google)OceanBase(阿里巴巴)这种计划比拟适宜数据位于同一个数据库组件,只须要可能保障并发操作不会造成数据不统一的业务 更宽泛场景下的的异构分布式事务更多时候,咱们的业务流程都比较复杂,比方上网买个女朋友。外围的流程至多有: 下单领取扣库存发货每个步骤可能都是由不同服务操作,相干的数据存储在不同数据库中。这种状况下,难以有对立的版本号,MVCC 伎俩无奈应用目前为止还没有反对跨数据库的严格一致性计划 绝对应于本地事务的强 ACID 要求,分布式事务场景下,为了面向高可用、可扩大等要求,个别会进行取舍,升高局部一致性和隔离性的要求,遵循 BASE 实践: 根本业务可用(Basic Availability)软状态(Soft state)最终统一(Eventual consistency)分布式事务中的 ACID 状况: 原子性:严格遵循,采纳相似 UNDO 的形式实现一致性:实现后的一致性严格遵循;事务中的一致性可适当放宽隔离性:大量事务能够并行持久性:严格遵循目前业界常见的分布式事务解决方案有 XA(两阶段提交、三阶段提交)TCCSAGA本地音讯表事务音讯最大致力告诉AT 事务模式XAXA 是由 X/Open 组织提出的分布式事务标准,这个标准次要定义了(全局)事务管理器(TM) 和(部分)资源管理器(RM)之间的接口。本地数据库如 MySQL 对应的是这里的 RM 角色XA 由一个或多个资源管理器(RM),一个事务管理器(TM)和一个应用程序(ApplicationProgram)组成。这三个角色概念 RM、TM、AP 是经典的角色划分,须要见名知意 目前支流的数据库根本都反对 XA 事务 两阶段提交顾名思义就是须要分两步提交事务:第一阶段(prepare):事务管理器向所有本地资源管理器发动申请,询问是否是 ready 状态,所有参与者都将本事务是否胜利的信息反馈发给协调者第二阶段(commit/rollback):事务管理器依据所有本地资源管理器的反馈,告诉所有本地资源管理器,各自为政地在所有分支上提交或者回滚 长处: 简略容易了解,开发较容易毛病: 同步阻塞问题:prepare 阶段锁住了资源,其余参与者须要等前一个参与者开释,并发度低单点问题:事务管理器呈现故障,整个零碎不可用不统一的可能:commit/rollback 阶段,如果事务管理器只发送了局部 commit 音讯,此时如果事务管理宕机(极其状况某些参与者也一起宕机),则难以保障所有参与者一致性失常不统一的可能,能够引入超时机制和互询机制来很大水平解决:对于协调者来说如果在指定工夫内没有收到所有参与者的应答,则能够主动退出 WAIT 状态,并向所有参与者发送 rollback 告诉。对于参与者来说如果位于 READY 状态,然而在指定工夫内没有收到协调者的第二阶段告诉,则不能果断地执行 rollback 操作,因为协调者可能发送的是 commit 告诉,这个时候执行 rollback 就会导致数据不统一。此时,咱们能够染指互询机制,让参与者 A 去询问其余参与者 B 的执行状况。如果 B 执行了 rollback 或 commit 操作,则 A 能够大胆的与 B 执行雷同的操作;如果 B 此时还没有达到 READY 状态,则能够推断出协调者收回的必定是 rollback 告诉;如果 B 同样位于 READY 状态,则 A 能够持续询问另外的参与者。只有当所有的参与者都位于 READY 状态时,此时两阶段提交协定无奈解决,将陷入长时间的阻塞状态。 ...

June 5, 2022 · 2 min · jiezi

关于分布式事务:分布式事务理论及最终一致性解决方案

分布式事务分布式事务是指会波及到操作多个数据库的事务,其实就是将对同一库事务的概念扩充到了对多个库的事务。目标是为了保障分布式系统中的数据一致性。 分布式事务处理的要害是: 须要记录事务在任何节点所做的所有动作;事务进行的所有操作要么全副提交,要么全副回滚。1.CAP实践 分布式系统的三个指标: 一致性 Consistency:对于客户端的每次读操作,要么读到的是最新的数据,要么读取失败。可用性 Availability:任何客户端的申请都能失去响应数据,不会呈现响应谬误。分区容错性 Partition tolerance:因为分布式系统通过网络进行通信,网络是不牢靠的。当任意数量的音讯失落或提早达到时,零碎仍会持续提供服务,不会挂掉。一个分布式系统,不可能同时做到这三点; 对于一个分布式系统而言,P是前提,必须保障,因为只有有网络交互就肯定会有提早和数据失落,这种情况咱们必须承受,必须保证系统不能挂掉。所以只剩下C、A能够抉择。要么保证数据一致性(保证数据相对正确),要么保障可用性(保证系统不出错)。 当抉择了C(一致性)时,如果因为网络分区而无奈保障特定信息是最新的,则零碎将返回谬误或超时。 当抉择了A(可用性)时,零碎将始终解决客户端的查问并尝试返回最新的可用的信息版本,即便因为网络分区而无奈保障其是最新的。 CP架构 因为网络问题,节点A和节点B之前不能相互通信。当有客户端(上图Actor)向节点A进行写入申请时(筹备写入Message 2),节点A会不接管写入操作,导致写入失败,这样就保障了节点A和节点B的数据一致性,即保障了Consisteny(一致性)。 而后,如果有另一个客户端(上图另一个Actor)向B节点进行读申请的时候,B申请返回的是网络故障之前所保留的信息(Message 1),并且这个信息是与节点A统一的,是整个零碎最初一次胜利写入的信息,是能失常提供服务的,即保障了Partition tolerance(分区容错性)。 AP架构 因为网络问题,节点A和节点B之前不能相互通信。当有客户端(上图Actor)向节点A进行写入申请时(筹备写入Message 2),节点A容许写入,申请操作胜利。但此时,因为A和B节点之前无奈通信,所以B节点的数据还是旧的(Message 1)。当有客户端向B节点发动读申请时候,读到的数据是旧数据,与在A节点读到的数据不统一。但因为零碎能照常提供服务,所以满足了Availability(可用性)要求。 注意事项对于开发者而言,构建服务的时候须要依据业务个性作出衡量思考,哪些点是以后零碎能够取舍的,哪些是应该重点保障的。 如在某个电商零碎中,属于用户模块的数据(账密、钱包余额等)对一致性的要求很高,就能够采纳CP架构。 而对于一些商品信息方面的数据对一致性要求没那么高,但为了关照用户体验,所以对可用性要求更高一些,那么这个模块的数据就能够采纳AP架构。 注:只能抉择CP和AP,无奈抉择CA,这句话成立的前提条件是在零碎产生了网络故障的状况下。在网络失常状况下,CA是能够实现的,咱们也须要去保障在绝大多数工夫下的CA架构。 2.Base实践核心思想:既是无奈做到强一致性(Strong consistency),但每个利用都能够依据本身的业务特点,采纳适当的形式来使零碎达到最终一致性(Eventual consistency)。Basically Available 根本可用假如零碎,呈现了不可预知的故障,但还是能用,相比拟失常的零碎而言会有响应工夫和性能上的损失: 响应工夫上的损失:失常状况下的搜索引擎0.5秒即返回给用户后果,而根本可用的搜索引擎能够在2秒作用返回后果。性能上的损失:在一个电商网站上,失常状况下,用户能够顺利完成每一笔订单。然而到了大促期间,为了爱护购物零碎的稳定性,局部消费者可能会被疏导到一个降级页面。Soft state(软状态)绝对于原子性而言,要求多个节点的数据正本都是统一的,这是一种“硬状态”。 软状态指的是:容许零碎中的数据存在中间状态,并认为该状态不影响零碎的整体可用性,即容许零碎在多个不同节点的数据正本存在数据延时。 如先把订单状态改成已领取胜利,而后通知用户曾经胜利了;剩下在异步发送mq音讯告诉库存服务减库存,即便生产失败,MQ音讯也会从新发送(重试)。 注:不可能始终是软状态,必须有个工夫期限。在期限过后,该当保障所有正本保持数据一致性,从而达到数据的最终一致性。这个工夫期限取决于网络延时、零碎负载、数据复制方案设计等等因素。 Eventually consistent(最终一致性)强一致性读操作要么处于阻塞状态,要么读到的是最新的数据最终一致性通常是异步实现的,读到的数据刚开始可能是旧数据,然而过一段时间后读到的就是最新的数据3.最终一致性解决方案留神可查问操作:业务方须要提供可查问接口,来查问数据信息和状态,供其余服务晓得数据状态。幂等操作:同样的参数执行同一个办法,返回的后果都一样。在分布式环境,难免会呈现数据的不统一,很多时候为了保证数据的统一,咱们都会进行重试。如果不保障幂等,即便重试胜利了,也无奈保证数据的一致性。咱们能够通过业务自身实现实现幂等,比方数据库的惟一索引来束缚;也能够缓存(记录)申请和操作后果,当检测到一样的申请时,返回之前的后果。弥补操作:某些数据存在不失常的状态,须要通过额定的形式使数据达到最终一致性的操作。XA标准XA标准是由 X/Open组织(即当初的 Open Group )定义的分布式事务处理模型。\XA 一共分为两阶段: 第一阶段(prepare):即所有的参与者 RM 筹备执行事务并锁住须要的资源。参与者 ready 时,向 TM 报告已准备就绪。 第二阶段 (commit/rollback):当事务管理者(TM)确认所有参与者(RM)都 ready 后,向所有参与者发送 commit 命令。 目前支流的数据库根本都反对 XA 事务,包含 mysql、oracle、sqlserver、postgre XA标准的组成应用程序( AP )事务管理器( TM ):交易中间件等资源管理器( RM ):关系型数据库等通信资源管理器( CRM ):消息中间件等XA标准定义XA标准定义了交易中间件与数据库之间的接口标准(即接口函数),交易中间件用它来告诉数据库事务的开始、完结以及提交、回滚等。而XA接口函数由数据库厂商提供。 ...

May 8, 2022 · 2 min · jiezi

关于分布式事务:MySQL-分布式事务的路与坑

1 数据库事务1.1 一般本地事务分布式事务也是事务,事务的 ACID 根本个性仍旧必须合乎: A:Atomic,原子性,事务内所有 SQL 作为原子工作单元执行,要么全副胜利,要么全副失败; C:Consistent,一致性,事务实现后,所有数据的状态都是统一的。如事务内A给B转100,只有A减去了100,B账户则必然加上了100; I:Isolation,隔离性,如果有多个事务并发执行,每个事务作出的批改必须与其余事务隔离; D:Duration,持久性,即事务实现后,对数据库数据的批改被长久化存储。 一般的非分布式事务,在一个过程外部,基于锁依赖于快照读和以后读,比拟好实现 ACID 来保障事务的可靠性。但分布式事务参与方通常在不同机器的不同实例上,原来的部分事务的锁不能保障分布式事务的ACID个性,须要引入新的事务框架,MySQL的分布式事务是基于2PC(二阶段提交)实现,上面具体介绍下2pc分布式事务。 1.2 基于2pc的分布式事务分布式事务有多种实现形式,如2PC(二阶段提交)、3PC(三阶段提交)、TCC(弥补事务)等,MySQL是基于 2PC 实现的分布式事务,上面介绍 2PC 分布式事务实现形式。 两阶段提交:Two-Phase Commit , 简称2PC,为了使基于分布式系统架构下的所有节点在进行事务提交时放弃一致性而设计的一种算法。 2PC的算法思路能够概括为,参与者将操作成败告诉协调者,再由协调者依据所有参与者的反馈情报,决定各参与者是否要提交操作还是停止操作。这里的参与者能够了解为 Resource Manager (RM),协调者能够了解为 Transaction Manager(TM)。 下图阐明了RM和TM在分布式事务中的运作过程: 第一阶段提交:TM 会发送 Prepare 到所有RM询问是否能够提交操作,RM 接管到申请,实现本身事务提交前的筹备工作并返回后果。 第二阶段提交:依据RM返回的后果,所有RM都返回能够提交,则 TM 给 RM 发送 commit 的命令,每个 RM 实现本人的提交,同时开释锁和资源,而后 RM 反馈提交胜利,TM 实现整个分布式事务;如果任何一个 RM 返回不能提交,则波及分布式事务的所有 RM 都须要回滚。 2 MySQL 分布式事务XAMySQL分布式事务XA是基于下面的2pc框架实现,上面具体介绍MySQL XA相干内容。 2.1 XA事务规范 X/Open 这个组织定义的一套分布式XA事务的规范,定义了标准和API接口,而后由厂商进行具体的实现。 XA标准中分布式事务由AP,RM,TM组成: 如上图,应用程序AP定义事务边界(定义事务开始和完结),并拜访事务边界内的资源。资源管理器RM治理共享的资源,也就是数据库实例。事务管理器TM负责管理全局事务,调配事务惟一标识,监控事务的执行进度,并负责事务的提交、回滚、失败复原等。MySQL实现了XA规范语法,提供了下面的RMs能力,能够让下层利用基于它疾速反对分布式事务。 2.2 MySQL XA语法XA START xid:开启一个分布式事务xid。 XA END xid: 将分布式事务xid置于 IDLE 状态,示意事务内的SQL操作实现。 ...

March 17, 2022 · 2 min · jiezi

关于分布式事务:分布式事务实践-解决数据一致性

分布式事务实际 解决数据一致性Socket 是什么以及创建过程一个数据包经由应用程序产生,进入到协定栈中进行各种报文头的包装,而后操作系统调用网卡驱动程序指挥硬件,把数据发送到对端主机。整个过程的大抵的图示如下。 download 咱们大家知道,协定栈其实是位于操作系统中的一些协定的重叠,这些协定包含 TCP、UDP、ARP、ICMP、IP等。通常某个协定的设计都是为理解决某些问题,比如 TCP 的设计就负责安全可靠的传输数据,UDP 设计就是报文小,传输效率高,ARP 的设计是能够通过 IP 地址查问物理(Mac)地址,ICMP 的设计目标是返回谬误报文给主机,IP 设计的目标是为了实现大范畴主机的互联互通。 应用程序比如阅读器、电子邮件、文件传输服务器等产生的数据,会通过传输层协定进行传输,而应用程序是不会和传输层间接建立联系的,而是有一个能够连接应用层和传输层之间的套件,这个套件就是 Socket。 在下面这幅图中,应用程序蕴含 Socket 和解析器,解析器的作用就是向 DNS 服务器发动查问,查问目标 IP 地址。 应用程序的上面就是操作系统外部,操作系统外部包含协定栈,协定栈是一系列协定的重叠。操作系统上面就是网卡驱动程序,网卡驱动程序负责管制网卡硬件,驱动程序驱动网卡硬件实现收发工作。 在操作系统外部有一块用于存放管制信息的存储空间,这块存储空间记录了用于管制通信的管制信息。其实这些管制信息就是 Socket 的实体,或者说存放管制信息的内存空间就是套接字的实体。 这里大家有可能不太明显所以然,所以我用了一下 netstat 命令来给大伙看一下套接字是啥玩意。 咱们在 Windows 的命令提示符中输出 netstat -ano netstat 用于浮现套接字内容 , -ano 是可选选项a 不只浮现正在通信的套接字,还浮现包含尚未开始通信等状态的所有套接字n 浮现 IP 地址和端口号o 浮现套接字的程序 PID我的计算机会出现上面后果。 图中的每一行都相当于一个套接字,每一列也被称为一个元组,所以一个套接字就是五元组(协定、本地地址、内部地址、状态、PID)。有的时候也被叫做四元组,四元组不包含协定。 比如图中的第一行,它的协定就是 TCP,本地地址和近程地址都是 0.0.0.0,这示意通信还没有开始,IP 地址临时还未必定,而本地端口已知是 135,然而近程端口还未知,此时的状态是 LISTENING,LISTENING 示意应用程序已经打开,正在等待与近程主机建立连接(对于各种状态之间的转换,大家可能浏览笔者的这篇文章 TCP ,丫的终于来了!!)最初一个元组是 PID,即过程标识符,PID 就像咱们的身份证号码,能够精确定位唯一的过程。 现在你可能对 Socket 有了一个基本的意识,现在喝口水,劳动一下,让咱们持续探究 Socket。 现在我有个问题,Socket 是如何创建的呢?Socket 是和应用程序一起创建的。应用程序中有一个 socket 组件,在应用程序启动时,会调用 socket 申请创建套接字,协定栈会根据应用程序的申请创建套接字:首先调配一个套接字所需的内存空间,这一步相当于是为管制信息筹备一个容器,但只有容器并没有实际作用,所以你还需要向容器中放入管制信息;如果你不申请创建套接字所需要的内存空间,你创建的管制信息也没有地方存放,所以分配内存空间,放入管制信息缺一不可。至此套接字的创建就已经实现了。 ...

January 7, 2022 · 1 min · jiezi

关于分布式事务:关于分布式事务的思考

景象互联网的世界与十几年前相比,曾经大不相同。以往的单体服务就能够撑持起大多数的用户需要。然而随着手机等电子产品的遍及,用户想要的服务曾经是越来越简单,各种需要互相关联。而这也给软件开发带来了更多的挑战。为了应酬随时会变动的代码世界,现有的开发趋势都在逐步的化整为零。其中最具代表性的就是微服务的风行。 在软件设计时,咱们常常会说高内聚,低耦合。这其实是对性能职责的确定。而在微服务的设计里,就是要将这些职责明确后拆分进来,再联结起来提供残缺的零碎性能。这其实跟一家企业的运行是一样的,企业须要依据本身的状况去组建各个部门,让业余的人干专门的事,以实现独特的指标。 问题在对于微服务的设计上,曾经有很多成熟的领导计划。比方基于畛域模型的,基于事件驱动的。然而,当各个服务各自为战后,在数据的一致性上,却未能有欠缺的解决方案。或是受性能效率限度,或是实现流程简单。总之,咱们须要采取很多额定的伎俩去解决分布式服务的数据一致性问题。 首先,何为数据一致性?集体了解,它能够是业务上的一致性,即数据库里常常提及的完整性束缚。就像银行的转账流程,其后果导向是一方的账户余额会缩小,另一方的增多。这是咱们在一开始就定义好的数据流程转换,必须要正确;另一种常见的数据一致性是零碎外部为了解决某些瓶颈而不得不思考面对的问题,比方主从复制、数据分片、集群选举等,对于这一块咱们后续有空再钻研。先来看看分布式服务里常常须要保障的业务一致性。 在软件开发行业里,自身就有对业务一致性的解决方案,即事务。对于事务的应用到当初都不会过期,只不过须要在同一个数据源(比方同一个数据库里),他人能力给你保障。然而,在以拆分为核心思想的微服务架构下,数据的变更是会在不同中央产生的,怎么去协调这些变更,以实现定义好的业务后果,是很艰难的。因为不足一个对立的协调管理者来染指,而一旦引入这个概念,又会挑战微服务的核心思想:去中心化,各个模块有可能会从新耦合在一起。 这就是艰难点所在了,咱们心愿各个服务能独立运行,但服务所产生的行为数据又要能配合协调,甚至相互依赖。这就有点纠缠不清的感觉了。 剖析只管分布式服务的数据一致性很简单,但作为软件开发的客人,咱们总得去厘清这外面的逻辑关系,确定边界,而后按对立的领导准则去设计。在这方面,曾经有一个规范的分布式统一解决模型,它是由国内开发规范组织 Open Group 定制的,其外围就像后面提及到的,有一个全局的协调者:事务管理器,外加事务的参与者:资源处理器以及其余辅助组件。咱们常常看到的 2PC ,两阶段提交就是基于此实现的。 两阶段提交对于两阶段提交,它的设计是在数据档次上的一个对立协调者,它对于服务提供方来讲侵入性较少,其治理的指标是将所有参与者波及到的数据进行对立的提交与回滚。 咱们晓得,在以前的本地事务里,当一系列的业务操作执行完后,就能够进行事务的提交/回滚这个确认动作了。而在两阶段提交里,这个确认动作要被提早。之所以要提早,是因为须要事务管理器这个协调者去查看其余参与者是否也将本人的业务流程执行完,只有当所有参与者都反馈执行完,能力进行最初的确认动作。当然,这个确认动作也是会告诉到所有参与者的。 实际上,事务管理器作为全局事务的管理者,它在一开始就染指整个流程了。具体过程如下: 【1】准备阶段(Prepare phase): 事务管理器向所有事务参与者发送事务执行申请,当参与者承受到该音讯后,进行本地事务的执行,记录事务日志,但并不提交事务。在执行完后会将执行后果反馈给协调者,等到后续告诉。 【2】提交阶段(Commit phase):事务管理器依据所有参与者的执行反馈后果决定此次的操作是提交或回滚。若参与者执行失败或者超时,则会告诉所有参与者进行回滚;否则,告诉提交。 能够看到,咱们将本来的事务流程拆分成了多个阶段,再由事务管理器去对立协调这些阶段解决。这个解决形式看起来简略,但外面要思考的因素有很多,例如: 阻塞:对于参与者来讲,因为本地事务的处理结果尚未确定,所以必须要阻塞期待协调者的后续执行指令。单点问题:事务管理器这个协调者很重要,一旦发送故障,那么将无奈进行后续的决策,即事务参与者将会有限的阻塞,直到协调者从新上线。除此之外,两阶段提交在性能效率上也须要掂量思考。此时的全局事务实现工夫曾经不再是简略的 1+1 的计算形式了,而是参与者与协调者的合作实现工夫。 TCC两阶段提交是属于强一致性的解决方案,它在资源管理器上的实现通常是由各个参加数据库来提供对立操作:筹备、提交、回滚。而这一套规范操作也能够由业务来实现,以提供更细的业务粒度以及更好的并发能力,相当于服务间接的参加了全局事务的协调流程,这即所谓的 TCC:Try-Confirm-Cancel 分布式事务。 TCC 分布式事务模型也将整体流程划分了两个阶段,在第一个阶段里,会由事务管理器这个对立协调者去进行所有参与者的业务检查和资源预留,在此阶段并不会真正的执行预期业务动作,只是先 check 前置条件,占有资源。在接下来的第二阶段里,会依据各个参与者的反馈后果,去决定是进行业务的确认操作还是勾销操作。 在 TCC 的设计里,会认为只有所有参与者都 Try 胜利,那么接下来的 Confirm 或 Cancel 操作是必定能胜利,如果失败,那么将须要一直从新或者人工染指。因而,TCC 还得在业务上有幂等保障。 TCC 相当于让业务自定义了筹备、提交、回滚操作,这样能够让开发者决定资源的占有机会,升高锁抵触,进步解决能力。就是对业务的侵入很大,原有的流程须要强行定义出 Try-Confirm-Cancel 的业务操作。 音讯最终统一在理论的开发过程中,咱们会发现有一些简略的分布式事务处理场景,比方签到后积分减少这种。它们可能仅仅是本地事务处理完,而后告诉另外个服务进行资源更新以实现整条链路需要。对于这种简略的分布式事务处理,用户对其不统一的容忍度比拟高,不要求立马失效,只有后果正确。针对这种即时性要求不高的需要,咱们能够应用上面这种最终统一的计划:状态管制+音讯发送+定时查看。 状态管制:对于本地的业务需要要有个状态记录,用于探知以后的解决阶段,以便重试或辅助前面的定时查看。音讯发送:当波及其余服务的资源更新操作时,通过音讯进行解耦,驱动下一个事务流程。定时查看:不再有协调者去兼顾全局事务,各个参与者只负责本人的事务实现状况,定时测验是否由异样状态或后果,触发对应解决流程。如报警告诉、人工兜底。下面的这个最终统一模型,实用于简略流程的分布式服务,或者跟事务想要的成果都搭不上边,更多的是靠音讯告诉,预先解决这种简略伎俩来保障业务的最终统一。如果业务链比较复杂,那就会定义出各种各样的音讯类型,这种反而会让整个零碎难以了解,也不易保护。 总结此次,咱们钻研了数据档次的分布式事务;业务档次的分布式事务;以及最终统一的伪分布式事务。对于这些模型的实现,市面上曾经有些成熟的框架了,比方 Seata,ByteTCC 等。大家能够站在伟人的肩膀上,更深刻的理解其实现流程,验证本人的所思所想。 感兴趣的敌人能够搜一搜公众号「 阅新技术 」,关注更多的推送文章。 能够的话,就顺便点个赞、留个言、分享下,感激各位反对! 阅新技术,浏览更多的新常识。

December 13, 2021 · 1 min · jiezi

关于分布式事务:分布式事务及其解决方案

为什么须要分布式事务分布式的微服务通常各自有本人的数据源,单机的事务由本机的数据源实现;当一个业务波及多个微服务的多个数据源时,很难保障多个数据源同时胜利、同时失败。 比方:支付宝 转账到 余额宝 1000元 支付宝: 账户A(id, user_id, amount)领取服务pay,转出:update A set amount=amount-1000 where user_id=1;余额宝: 账户B(id, user_id, amount)余额宝服务balance,转入:update B set amount=amount+1000 where user_id=1;领取服务pay和余额宝服务balance,只有同时执行胜利,才算本次转账胜利;只有有一方执行失败,则本次转账失败。 分布式事务的解决方案--2PC2PC(Two Phase Commit protocol)两阶段提交,是强一致性的分布式事务实现形式。 2PC波及的角色: 协调者:coordinator,协调多个参与者进行投票、提交或回滚;参与者:participants,本地事务的执行者; 2PC的解决部署: 投票阶段 协调者告诉参与者执行本地事务,而后进入表决过程;参与者执行本地事务,但不提交,将执行后果回复协调者;提交阶段 协调者收到参与者的执行后果,若均执行胜利,则向所有参与者发送commit;否则,向所有参与者发送rollback;2PC的毛病: 协调者是个单点,存在单点故障;事务提交之前,资源被预留锁定,因为波及多节点的网络交互,导致锁工夫较长,影响并发;分布式事务的解决方案--TCCTCC(Try Confirm Cancel),是业务层面的分布式事务,TCC要求所有的事务参与者都要实现3个接口: Try: 预处理;Confirm: 确认;Cancel: 勾销;TCC的执行过程: Client向所有事务参与者发送Try操作;若所有事务参与者的Try均胜利,则Client向所有事务参与者发送Confirm,否则发送Cancel;TCC要求事务参与方,都要当时实现Try/Confirm/Cancel接口,对业务代码的侵入性较强。 TCC存在的问题: 空回滚 第一阶段的Try因为音讯失落而产生网络超时,触发第二阶段的Cancel;事务参与方在没有收到Try的状况下,收到了Cancel音讯,称为“空回滚”;“空回滚”的存在,要求事务参与方在实现Cancel接口时,思考未收到Try的状况;防悬挂 第一阶段的Try因为音讯失落而产生网络超时,触发第二阶段的Cancel;事务参与方在没有收到Try的状况下,收到了Cancel音讯,执行“空回滚”;此时,第一阶段的Try音讯又达到,该场景称为“防悬挂”;解决办法: 执行“空回滚”的时候,插入1条记录,状态为已回滚;当Try又回来时,先查问记录,若已存在且已回滚,则不再执行该Try;分布式事务的解决方案--最终一致性基本思路是,将事务音讯进行长久化(Transaction outbox),通过binlog推送给上游,上游生产胜利后,调用callback到上游,通知上游事务处理完毕。 没有事务的回退流程,通过重试,尽最大致力交付,实质上是最终一致性的解决方案。 事务音讯长久化:Transaction outbox支付宝pay服务执行本地事务,进行A用户扣款,同时记录音讯数据msg,该msg表与pay业务数据在同一个DB中。 支付宝pay服务的本地事务保障,只有实现扣款,msg肯定能保留下来。 begin transaction update A set amount = amount - 1000 where user_id=1; insert into msg(user_id, amount, status) values(1, 1000, 1)end transactioncommit通过binlog推送给上游:Transaction log tailing支付宝的msg表被Canal订阅,而后发送到kafka。通过Canal异步订阅的形式,将两边解耦。 ...

December 8, 2021 · 1 min · jiezi

关于分布式事务:不就是分布式事务这下彻底清楚了

大家好,我是老三,上次发文的时候还是上次发文的时候,这篇文章分享分布式事务,看完要是你们不懂,那肯定是不明确。 从本地事务到分布式事务事务大家应该都晓得,事务将一组操作纳入到一个不可分割的执行单元,这个执行单元里的操作都胜利时能力提交胜利。 简略地说,事务提供一种要么不做,要么全做机制。 ACID咱们先简略理解一下事务的四大个性: A 原子性(Atomicity)一个事务(transaction)中的所有操作,要么全副实现,要么全副不实现,不会呈现局部胜利局部失败的状况。。 C 一致性(Consistency)事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务胜利地实现,那么零碎中所有变动将正确地利用,零碎处于无效状态。如果在事务中呈现谬误,那么零碎中的所有变动将主动地回滚,零碎返回到原始状态。 I 隔离性(Isolation)指的是在并发环境中,当不同的事务同时操纵雷同的数据时,每个事务都有各自的残缺数据空间。由并发事务所做的批改必须与任何其余并发事务所做的批改隔离。事务查看数据更新时,数据所处的状态要么是另一事务批改它之前的状态,要么是另一事务批改它之后的状态,事务不会查看到中间状态的数据。 D 持久性(Durability)指的是只有事务胜利完结,它对数据库所做的更新就必须永恒保留下来。即便产生零碎解体,重新启动数据库系统后,数据库还能复原到事务胜利完结时的状态。 单体事务在单体架构时代,所有的业务只用一个数据库。 单体架构时代事务的是实现很简略,咱们操作的是同一个数据库,利用数据库自身提供的事务机制反对就能够了。 例如咱们比拟相熟的MySQL数据库: 事务的隔离性是通过数据库锁的机制实现的。事务的一致性由undo log来保障:undo log是逻辑日志,记录了事务的insert、update、deltete操作,回滚的时候做相同的delete、update、insert操作来复原数据。事务的原子性和一持久性由redo log来保障:redolog被称作重做日志,是物理日志,事务提交的时候,必须先将事务的所有日志写入redo log长久化,到事务的提交操作才算实现。 具体理解倡议浏览《MySQL技术底细 InnoDB存储引擎》7.2节。分布式事务随着业务倒退,单体架构顶不住了,缓缓进入分布式时代——SOA或者粒度更细的微服务。 当然随同而来的就是分库分表。 咱们可能会依据业务服务拆分的形式,对应地垂直拆分大库,例如原始大库拆分成订单库、商品库、领取库。同时因为业务数据可能会高速减少,很快就成了亿级,咱们不得不又程度分库,来加重单个数据库的压力。 不论是怎么分库的,最初的后果就是咱们一个操作可能要横跨多个数据库。 数据库自身的事务机制只能保障它本人这个库的事务,然而没法保障到其它的库。咱们要保障跨多个库的操作还具备事务的个性,就不得不上分布式事务了。 在后面 分布式必备实践根底:CAP和BASE 里,讲了分布式的实践根底——CAP和BASE,这里就不再多讲。 咱们只须要晓得,BASE实践是对CAP中AP的一个延申,在没法保障强一致性的前提下,尽可能达到最终的一致性。 咱们的分布式事务通常也做不到本地事务那么强的一致性,个别都是对一致性(Consistency)适当做了一些放宽,只须要达到最终的一致性。 分布式事务解决方案XA /2PC两阶段提交XAXA是一个分布式事务协定,由Tuxedo提出。 在这个协定里,有三个角色: AP(Application):利用零碎(服务)TM(Transaction Manager):事务管理器(全局事务管理)RM(Resource Manager):资源管理器(数据库)XA标准次要定义了 事务管理器(Transaction Manager)和资源管理器(Resource Manager)之间的接口。 XA协定采纳两阶段提交形式来治理分布式事务。XA接口提供资源管理器与事务管理器之间进行通信的标准接口。 2PC 两阶段提交两阶段提交的思路能够概括为: 参与者将操作成败告诉协调者,再由协调者依据所有参与者的反馈状况决定各参与者是否要提交操作还是停止操作。 两阶段提交的两个阶段:第一阶段:筹备阶段,第二阶段:提交阶段 ![两阶段-参考[2]](https://gitee.com/sanfene/pic...) 筹备阶段 Prepares 协调者向所有参与者询问是否能够执行提交操作,所有参与者执行事务,将后果返回给协调者。 提交阶段 commit 如果第一阶段汇中所有参与者都返回yes响应,协调者向所有参与者收回提交申请,所有参与者提交事务如果第一阶段中有一个或者多个参与者返回no响应,协调者向所有参与者收回回滚申请,所有参与者进行回滚操作 两阶段提交长处:尽量保障了数据的强统一,但不是100%统一 两阶段提交同样有一些毛病: 单点故障 因为协调者的重要性,一旦协调者产生故障,参与者会始终阻塞,尤其时在第二阶段,协调者产生故障,那么所有的参与者都处于锁定事务资源的状态中,而无奈持续实现事务操作。 同步阻塞 它是一个强一致性的同步阻塞协定,也就是所谓刚性事务,事务执⾏过程中须要将所需资源全副锁定,会比拟影响性能。 数据不统一 在第二阶段中,当协调者想参与者发送提交事务申请之后,因为网络抖动,如果第二阶段只有局部参与者收到提交申请,那么就会导致数据不统一。 3PC 三阶段提交三阶段提交(3PC)是二阶段提交(2PC)的一种改良版本 ,为解决两阶段提交协定的单点故障和同步阻塞问题。上边提到两阶段提交,当协调者解体时,参与者不能做出最初的抉择,就会始终放弃阻塞锁定资源。 2PC 中只有协调者有超时机制,3PC 在协调者和参与者中都引入了超时机制,协调者呈现故障后,参与者就不会始终阻塞。而且在第一阶段和第二阶段中又插入了一个预提交阶段,保障了在最初提交阶段之前各参加节点的状态是统一的。 ...

September 18, 2021 · 2 min · jiezi

关于分布式事务:五分钟带你体验一把分布式事务so-easy

@[toc]网上对于分布式事务讲实践的多,讲实战的少,明天我想通过一个案例,来让小伙伴们感触一把分布式事务,咱们明天尽量少谈点实践。咱们明天的配角是 Seata! 分布式事务波及到很多实践,如 CAP,BASE 等,很多小伙伴刚看到这些实践就被劝退了,所以咱们明天不讲实践,咱们就看个 Demo,通过代码疾速体验一把什么是分布式事务。 1. 什么是 Seata?Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简略易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。 Seata 反对的事务模式有四种别离是: Seata AT 模式Seata TCC 模式Seata Saga 模式Seata XA 模式Seata 中有三个外围概念: TC (Transaction Coordinator) - 事务协调者:保护全局和分支事务的状态,驱动全局事务提交或回滚。TM (Transaction Manager) - 事务管理器:定义全局事务的范畴,开始全局事务、提交或回滚全局事务。RM ( Resource Manager ) - 资源管理器:治理分支事务处理的资源( Resource ),与 TC 交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。其中,TC 为独自部署的 Server 服务端,TM 和 RM 为嵌入到利用中的 Client 客户端。 这些概念小伙伴们作为一个理解即可,不理解也能用 Seata,理解了更能了解 Seata 的工作原理。 2. 搭建 Seata 服务端咱们先来把 Seata 服务端搭建起来。 Seata 下载地址: https://github.com/seata/seata/releases目前最新版本是 1.4.2,咱们就应用最新版本来做。 这个工具在 Windows 或者 Linux 上部署差异不大,所以我这里就间接部署在 Windows 上了,不便一些。 ...

August 16, 2021 · 4 min · jiezi

关于分布式事务:分布式事务分析以及seata的AT模式解读

前言家喻户晓,分布式事务是个简单的问题,有很多种不同的思路和办法。 目前,只有是微服务架构做的分布式系统,就绕不开分布式事务这个话题。当然,并不是应用了分布式事务解决方案服务就稳固,高大上,不应用分布式事务服务也并不一定不好。目前咱们公司就没有应用分布式事务,一样撑起了稳固,灵便的零碎。分布式事务的抉择与否没有对错,在于实在我的项目中权衡利弊后的抉择。分布式事务倒退起源最开始咱们的利用是单体服务,在应用切当的状况下,spring的事务能够很好的帮咱们解决相干事务问题。简略剖析spring的事务:当某个代理办法应用 @Transactiona 并被AOP切面切到当前。spring会给aop切面中应用ThreadLocal治理同一个DBConnection。由这一个connection替咱们治理事务:即整个办法执行完结(活异样)则一起提交或者回滚事务。此时咱们的利用如下图: 或者这样 然而随着我的项目流量的增长,同一个DB连贯必定承接不住各类服务数据,因而,在理论的我的项目中,咱们大多数的利用与DB关系是这样的:咱们晓得,spring事务是在一个jvm下通过同一个db连贯管制的事务,然而当有多台服务器的时候,不同的服务器有本人的jvm,即便近程调用,db-connection必定不是同一个。由此,咱们须要一个第三者去协调每个服务的数据连贯。便产生了咱们常说的分布式事务问题。 筹备知识点ACID 原子性(Atomic)一致性(Consistency)隔离性(Isolation)持久性(Durability)隔离级别 Read Uncommitted(读未提交)Read Committed(读已提交)Repeated Read(反复读)Serialization(串行化)CAP定律 一致性(C) 在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点拜访同一份最新的数据正本)可用性(A) 在集群中一部分节点故障后,集群整体是否还能响应客户端的读写申请。(对数据更新具备高可用性)分区容错性(P) 以实际效果而言,分区相当于对通信的时限要求。零碎如果不能在时限内达成数据一致性,就意味着产生了分区的状况,必须就以后操作在C和A之间做出抉择。BASE实践BASE是Basically Available(根本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的缩写。BASE实践是对CAP中一致性和可用性衡量的后果,其来源于对大规模互联网零碎分布式实际的总结, 是基于CAP定理逐渐演变而来的。BASE实践的核心思想是:即便无奈做到强一致性,但每个利用都能够依据本身业务特点,采纳适当的形式来使零碎达到最终一致性。根本可用 根本可用是指分布式系统在呈现不可预知故障的时候,容许损失局部可用性—-留神,这绝不等价于零碎不可用。比方:(1)响应工夫上的损失。失常状况下,一个在线搜索引擎须要在0.5秒之内返回给用户相应的查问后果,但因为呈现故障,查问后果的响应工夫减少了1~2秒(2)零碎性能上的损失:失常状况下,在一个电子商务网站上进行购物的时候,消费者简直可能顺利完成每一笔订单,然而在一些节日大促购物顶峰的时候,因为消费者的购物行为激增,为了爱护购物零碎的稳定性,局部消费者可能会被疏导到一个降级页面软状态 软状态指容许零碎中的数据存在中间状态,并认为该中间状态的存在不会影响零碎的整体可用性,即容许零碎在不同节点的数据正本之间进行数据同步的过程存在延时最终一致性 最终一致性强调的是所有的数据正本,在通过一段时间的同步之后,最终都可能达到一个统一的状态。因而,最终一致性的实质是须要零碎保障最终数据可能达到统一,而不须要实时保证系统数据的强一致性。开源的分布式事务框架目前世面上用于开源的分布式事务问题的框架还不少,比方seata,lcn,应用事务音讯rocketmq等等。而每种框架上面反对的模式又有很多种(能够想想为什么每种框架都反对好几种模式)。明天咱们具体解读一下seata的无代码侵入式的AT模式。 首先通过一个简略的sata例子认识一下 ============user模块=============/** * @author liuliang * @date 2021/7/9 2:31 下午 */@Slf4j@Servicepublic class UserService extends ServiceImpl<UserMapper, UserInfo> { @Resource private OrderFeign orderFeign; @GlobalTransactional public void buyProduct() { UserInfo userInfo = this.getById(1L); userInfo.setAmount(userInfo.getAmount() - 1); //扣减本人的余额 this.updateById(userInfo); //创立订单 orderFeign.test(); log.info("购买完结"); }}user模块调用order模块 /** * @author liuliang * @date 2021/7/9 2:58 下午 */@FeignClient(name = "mm-order")public interface OrderFeign { @GetMapping("/order/test") void test();}order模块 ...

July 16, 2021 · 1 min · jiezi

关于分布式事务:Springboot微服务整合Seata分布式事务

Seata Server服务端搭建一、官网地址Seata 文档 Seata github Seata releases 二、Seata Server 下载这里地址为 1.3.0版本 seata-server-1.3.0 Linuxwget https://github.com/seata/seata/releases/download/v1.3.0/seata-server-1.3.0.zip三、批改配置文件配置文件次要有:registry.conf file.conf registry.conf : Seata 服务注册核心地址,配置核心地址file.conf : 当配置核心应用 file时,此配置文件失效。配置store存储配置 这里咱们应用eureka做为注册核心,Apollo做为配置核心 registry.confregistry { # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa type = "eureka" # 配置eureka地址 eureka { serviceUrl = "http://10.0.17.92:9001/eureka" application = "seata-server" weight = "1" } ... file { name = "file.conf" }}config { # file、nacos 、apollo、zk、consul、etcd3 #type = "file" type = "apollo" # 配置核心配置apollo地址和namespace apollo { appId = "seata-server" apolloMeta = "http://10.0.17.92:9083" namespace = "application" } file { name = "file.conf" }}file.conf## transaction log store, only used in seata-serverstore { ## store mode: file、db、redis mode = "db" ## file store property file { ## store location dir dir = "sessionStore" # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions maxBranchSessionSize = 16384 # globe session size , if exceeded throws exceptions maxGlobalSessionSize = 512 # file buffer size , if exceeded allocate new buffer fileWriteBufferCacheSize = 16384 # when recover batch read size sessionReloadReadSize = 100 # async, sync flushDiskMode = async } ## database store property db { ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc. datasource = "druid" ## mysql/oracle/postgresql/h2/oceanbase etc. dbType = "mysql" driverClassName = "com.mysql.cj.jdbc.Driver" url = "jdbc:mysql://10.0.17.122:3306/seata" user = "root" password = "Pdhn^456" minConn = 5 maxConn = 30 globalTable = "global_table" branchTable = "branch_table" lockTable = "lock_table" queryLimit = 100 maxWait = 5000 } ## redis store property redis { host = "127.0.0.1" port = "6379" password = "" database = "0" minConn = 1 maxConn = 10 queryLimit = 100 }}Apollo配置Server配置次要批改:数据库地址 用户名 明码等 ...

June 10, 2021 · 4 min · jiezi

关于分布式事务:分布式锁分析使用Redis实现分布式事务中的锁机制

分布式协调服务Zookeeper是分布式协调服务框架分布式协调技术: 次要用来解决分布式环境当中多个过程之间的同步控制,让过程有序的去拜访某种临界资源,避免造成"脏数据"的结果分布式协调技术的外围就是实现分布式锁 分布式锁分布式锁: 为了避免分布式系统中的多个过程之间互相烦扰,须要分布式协调技术对过程进行调度,这个分布式协调技术的外围就是实现分布式锁 分布式锁条件在分布式系统环境下,一个办法在同一时间只能被一个机器的一个线程执行高可用的获取锁与开释锁高性能的获取锁与开释锁具备可重入个性具备锁生效机制,避免死锁具备非阻塞锁个性 分布式锁的实现ZookeeperRedisMemcachedChubby Redis分布式锁的实现分布式锁实现的三个外围因素:加锁,解锁,锁超时Redis是单线程的 加锁应用setnx命令key是锁的惟一标识,按业务来决定命名value能够设置成任意值当一个线程执行setnx返回1,阐明key本来不存在,该线程胜利失去锁.当一个线程执行setnx返回0,阐明key曾经存在,该线程抢锁失败 解锁当失去锁的线程执行完工作,须要开释锁,以便其它线程能够进入,应用del指令开释锁之后,其它线程就能够继续执行setnx命令取得锁 锁超时一个失去所得线程在执行工作的过程中呈现问题,不能显式开释锁,这些资源将永远被锁住,造成死锁的状态setnx的key要设置一个超时工夫,保障锁没有被显式开释时,会在肯定工夫后主动开释.setnx不反对超时参数,须要额定的指令expireRedis分布式锁问题: 非原子性操作: 解决方案: 通过应用set命令set(key,value,expire) 设置为原子操作误删锁: 在设置锁超时的状况下,操作没有实现,当操作实现时,del命令删除的是其它过程的锁解决方案: 判断是否为本过程的锁.带着key和value=threadID线程ID判断是否为本过程的锁在设置锁超时的状况下,操作没有实现 解决方案: 开释锁时判断操作是否实现, 减少守护线程:为锁超时加时,提早开释

May 17, 2021 · 1 min · jiezi

关于分布式事务:分布式事务四Seata-AT模式Spring-Cloud微服务案例

我的项目源码: https://gitee.com/benwang6/se... 一、订单业务案例1.1 创立 Empty Project:seata-at先新建文件夹 seata-samples,前面测试的 Seata AT 和 Seata TCC 模式都放在该目录下。 接着创立 seata-at 我的项目: 抉择 Empty Project: 填写我的项目名 seata-at 和寄存目录,寄存在你新建的 seata-samples 目录下: 1.2 数据库初始化工具订单案例波及四个数据库: 为了后续测试不便咱们编写一个工具,用来重置所有数据库表,能够不便地把数据重置到初始状态。 1.3 新建Module:db-init新建 Module,抉择 Spring Initializr 填写 Group 和 Artifact,其余选项默认即可: 增加 JDBC 和 MySQL Driver 依赖: 实现后,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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.tedu</groupId> <artifactId>db-init</artifactId> <version>0.0.1-SNAPSHOT</version> <name>db-init</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>1.4 application.yml 配置我的项目的 application.properties 文件改名成 application.yml,而后增加 mysql 连贯配置: ...

March 5, 2021 · 12 min · jiezi

关于分布式事务:Seata-TCC-分布式事务

1. 前言本文先通过分布式事务中tcc计划,衍生出seata的tcc模式,次要还是会通过代码示例来做介绍。github代码地址可提前下载,该我的项目中包含数据库、seata配置,以及所有分布式服务的全副代码。大家如果想练练手,能够先拉取该我的项目代码,再联合本文学习。外围配置环境如下: 环境类型版本号jdk1.8.0_251mysql8.0.22seata server1.4.11.1. tcc咱们后面有几篇文章都有介绍过分布式事务的计划,目前常见的分布式事务计划有:2pc、tcc和异步确保型。之前讲过用jta atomikos实现多数据源的 2pc,用 异步确保型 计划实现领取业务的事务等等,就是没专门讲过 tcc 的利用。 因为tcc计划的操作难度还是比拟大的。不能单打独斗,最好须要依靠一个成熟的框架来实现。常见的tcc开源框架有tcc-transaction、Hmily和ByteTCC等,不过他们不像seata背靠大厂,无奈提供继续的保护,因而我更举荐seata的tcc计划。 1.2. seata先说说seata吧,分布式事务的解决方案必定不局限于下面说的三种,实际上形形色色。因为它确实很让人头疼,各位大神都想研发出最好用的框架。本文的配角 - seata ,就是阿里的一个开源我的项目。 seata提供了AT、TCC、SAGA 和 XA,一共4种事务模式。像AT模式就很受欢迎,咱们在实现多数据源的事务一致性时,通常会选用 2PC的计划,期待所有数据源的事务执行胜利,最初再一起提交事务。这个期待所有数据源事务执行的过程就比拟耗时,即影响性能,也不平安。 而seata AT模式的做法就很灵便,它学习数据库的 undo log,每个事务执行时立刻提交事务,但会把 undo 的回退sql记录下来。如果所有事务执行胜利,革除记录 undo sql的行记录,如果某个事务失败,则执行对应 undo sql 回滚数据。在保障事务的同时,并发量也大了起来。 但咱们明天要讲的是 seata TCC 模式,如果你对 Seata的其余模式感兴趣,能够上官网理解。 2. 业务先讲一下示例的业务吧,咱们还是拿比拟经典的电商领取场景举例。假如领取胜利后,波及到三个零碎事务: 订单零碎(order):创立领取订单。库存零碎(storage):对应商品扣除库存。账户零碎(account):用户账户扣除响应金额。2.1. tcc业务依照tcc(try-confirm-cancel)的思路,这三个事务能够别离分解成上面的过程。 订单零碎 ordertry: 创立订单,然而订单状态设置一个长期状态(如:status=0)。confirm: try胜利,提交事务,将订单状态更新为齐全状态(如:status=1)。cancel: 回滚事务,删除该订单记录。库存零碎 storagetry: 将须要缩小的库存量解冻起来。confirm: try胜利,提交事务,应用解冻的库存扣除,实现业务数据处理。cancel: 回滚事务,解冻的库存冻结,复原以前的库存量。账户零碎 accounttry: 将须要扣除的钱解冻起来。confirm: try胜利,提交事务,应用解冻的钱扣除,实现业务数据处理。cancel: 回滚事务,解冻的钱冻结,复原以前的账户余额。2.2. 数据库为了模仿分布式事务,上述的不同零碎业务,咱们通过在不同数据库中创立表构造来模仿。当然tcc的分布式事务不局限于数据库层面,还包含http接口调用和rpc调用等,然而殊途同归,能够作为示例参考。 上面先列出三张业务表的表构造,具体的sql可见最初附件。 表:order列名类型备注idint主键order_novarchar订单号user_idint用户idproduct_idint产品idamountint数量moneydecimal金额statusint订单状态:0:创立中;1:已完结表:storage列名类型备注idint主键product_idint产品idresidueint残余库存frozenintTCC事务锁定的库存表:account列名类型备注idint主键user_idint用户idresidueint残余可用额度frozenintTCC事务锁定的金额3. seata server3.1. 下载seata server 的安装包可间接从官网github下载,下载压缩包后,解压到本地或服务器上。 3.2. 配置Seata Server 的配置文件有两个: seata/conf/registry.confseata/conf/file.confregistry.confSeata Server 要向注册核心进行注册,这样,其余服务就能够通过注册核心去发现 Seata Server,与 Seata Server 进行通信。Seata 反对多款注册核心服务:nacos 、eureka、redis、zk、consul、etcd3、sofa。咱们我的项目中要应用 eureka 注册核心,eureka服务的连贯地址、注册的服务名,这须要在 registry.conf 文件中对 registry 进行配置。 ...

February 15, 2021 · 4 min · jiezi

关于分布式事务:分布式事务系统之底层原理揭秘

博客原文分布式事务/零碎之底层原理揭秘 导言分布式事务是分布式系统必不可少的组成部分,基本上只有实现一个分布式系统就逃不开对分布式事务的反对。本文从分布式事务这个概念切入,尝试对分布式事务最外围的底层原理逐个进行分析,内容包含但不限于 BASE 准则、两阶段原子提交协定、三阶段原子提交协定、Paxos/Multi-Paxos 分布式共识算法的原理与证实、Raft 分布式共识算法和分布式事务的并发管制等内容。 事务事务是拜访并可能更新各种数据项的一个程序执行单元(unit)。事务由一个或多个步骤组成,个别应用形如 begin transaction 和 end transaction 语句或者函数调用作为事务界线,事务内的所有步骤必须作为一个繁多的、不可分割的单元去执行,因而事务的后果只有两种:1. 全副步骤都执行实现,2. 任一步骤执行失败则整个事务回滚。 事务最早由数据库管理系统(database management system,DBMS)引入并实现,数据库事务是数据库管理系统执行过程中的一个逻辑单位,由一个无限的数据库操作序列形成。数据库事务严格遵循 ACID 准则,属于刚性事务,一开始数据库事务仅限于对繁多数据库资源对象的访问控制,这一类事务称之为本地事务 (Local Transaction),起初随着分布式系统的呈现,数据的存储也不可避免地走向了分布式,分布式事务(Distributed Transaction)便应运而生。 刚性事务 刚性事务(如繁多数据库事务)齐全遵循 ACID 标准,即数据库事务的四大根本个性: Atomicity(原子性):一个事务(transaction)中的所有操作,或者全副实现,或者全副不实现,不会完结在两头某个环节。事务在执行过程中产生谬误,会被回滚(Rollback)到事务开始前的状态,就像这个事务素来没有执行过一样。即,事务不可分割、不可约简。Consistency(一致性):在事务开始之前和事务完结当前,数据库的完整性没有被毁坏。这示意写入的材料必须完全符合所有的预设束缚、触发器、级联回滚等。Isolation(隔离性):数据库容许多个并发事务同时对其数据进行读写和批改的能力,隔离性能够避免多个事务并发执行时因为穿插执行而导致数据的不统一。事务隔离分为不同级别,包含未提交读(Read uncommitted)、提交读(read committed)、可反复读(repeatable read)和串行化(Serializable)。Durability(持久性):事务处理完结后,对数据的批改就是永恒的,即使系统故障也不会失落。刚性事务也可能以分布式 CAP 实践中的 CP 事务来作为定义。 柔性事务 在电商畛域等互联网场景下,传统的事务在数据库性能和解决能力上都遇到了瓶颈。因而,柔性事务被提了进去,柔性事务基于分布式 CAP 实践以及延长进去的 BASE 实践,相较于数据库事务这一类齐全遵循 ACID 的刚性事务来说,柔性事务保障的是 “根本可用,最终统一”,CAP 原理置信大家都很相熟了,这里咱们讲一下 BASE 准则: 根本可用(Basically Available):零碎可能根本运行、始终提供服务。软状态(Soft-state):零碎不要求始终放弃强统一状态。最终一致性(Eventual consistency):零碎须要在某一时刻后达到一致性要求。柔性事务(如分布式事务)为了满足可用性、性能与降级服务的须要,升高一致性(Consistency)与隔离性(Isolation)的要求,遵循 BASE 实践,传统的 ACID 事务对隔离性的要求十分高,在事务执行过程中,必须将所有的资源对象锁定,因而对并发事务的执行极度不敌对,柔性事务(比方分布式事务)的理念则是将锁资源对象操作从本地资源对象层面上移至业务逻辑层面,再通过放宽对强一致性要求,以换取零碎吞吐量的晋升。 此外,尽管柔性事务遵循的是 BASE 实践,然而还须要遵循局部 ACID 标准: 原子性:严格遵循。一致性:事务实现后的一致性严格遵循;事务中的一致性可适当放宽。隔离性:并行事务间不可影响;事务两头后果可见性容许平安放宽。持久性:严格遵循。本地事务本地事务(Local Transaction)指的是仅仅对繁多节点/数据库资源对象进行拜访/更新的事务,在这种事务模式下,BASE 实践派不上用场,事务齐全遵循 ACID 标准,确保事务为刚性事务。 分布式事务在分布式架构成为支流的当下,系统对资源对象的访问不能还局限于单节点,多服务器、多节点的资源对象拜访成为刚需,因而,本地事务无奈满足分布式架构的零碎的要求,分布式事务应运而生。 拜访/更新由多个服务器治理的资源对象的立体事务或者嵌套事务称之为分布式事务(Distributed Transaction),分布式事务是绝对于本地事务来说的。 ...

January 3, 2021 · 9 min · jiezi

关于分布式事务:分布式事务二Seata-框架-AT-事务

Seata介绍Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简略易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。 2019 年 1 月,阿里巴巴中间件团队发动了开源我的项目 Fescar(Fast & EaSy Commit And Rollback),和社区一起共建开源分布式事务解决方案。Fescar 的愿景是让分布式事务的应用像本地事务的应用一样,简略和高效,并逐渐解决开发者们遇到的分布式事务方面的所有难题。 Fescar 开源后,蚂蚁金服退出 Fescar 社区参加共建,并在 Fescar 0.4.0 版本中奉献了 TCC 模式。 为了打造更中立、更凋谢、生态更加丰盛的分布式事务开源社区,通过社区核心成员的投票,大家决定对 Fescar 进行品牌降级,并更名为 Seata,意为:Simple Extensible Autonomous Transaction Architecture,是一套一站式分布式事务解决方案。 Seata 交融了阿里巴巴和蚂蚁金服在分布式事务技术上的积攒,并积淀了新批发、云计算和新金融等场景下丰盛的实践经验,但要实现实用于所有的分布式事务场景的愿景,仍有很长的路要走。 Seata AT事务计划Seata 的 AT 模式(Automatic Transaction)是一种无侵入的分布式事务解决方案。上面联合具体业务场景来剖析其执行的原理。 业务场景订单零碎 当用户下订单时,执行以下三步流程: 订单零碎保留订单订单零碎调用库存服务,缩小商品库存订单零碎调用账户服务,扣减用户金额这三步要作为一个整体事务进行治理,要么整体胜利,要么整体失败。 Seata AT基本原理Seata AT 事务分两个阶段来治理全局事务:第一阶段: 执行各分支事务第二阶段: 管制全局事务最终提交或回滚 第一阶段:执行各分支事务微服务零碎中,各服务之间无奈互相感知事务是否执行胜利,这时就须要一个专门的服务,来协调各个服务的运行状态。这个服务称为 TC(Transaction Coordinator),事务协调器。 订单零碎开始执行保留订单之前,首先启动 TM(Transaction Manager,事务管理器),由 TM 向 TC 申请开启一个全局事务: 这时TC会产生一个全局事务ID,称为 XID,并将 XID 传回 TM: ...

December 14, 2020 · 7 min · jiezi

关于分布式事务:分布式事务一数据库事务分布式事务

数据库事务数据库事务由一组sql语句组成。 所有sql语句执行胜利则事务整体胜利;任一条sql语句失败则事务整体失败,数据恢复到事务之前的状态。 上面以转账为例进一步阐明。A 账户向 B 账户转账,须要更新两个账户的记录: - A 账户减金额update user set money=money-100 where id='A'- B 账户加金额update user set money=money+100 where id='B' 两条sql语句都胜利则转账胜利。任意一条sql语句失败,复原以前的状态。数据操作的最小单元是事务,而不是一条sql语句! Mysql 事务操作开始事务start transaction;- 或begin; 事务开始后,对数据的增删改操作不间接批改数据表,而是被记录在日志文件中。 提交事务commit; 将日志中记录的操作,永恒保留到数据表,并清空日志文件。 回滚事务rollback;间接清空日志文件 事务个性 ACIDA - 原子性 Atomic一个事务是一个不可分割的工作单元,事务中包含的操作要么都做,要么都不做。 数据操作的最小单元是事务,而不是SQL语句 。 C - 一致性 Consistency事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。 例如: 转账前 a+b = 100转帐后 a+b = 100I - 隔离性 Isolation一个事务的执行不能被其余事务烦扰。 即一个事务外部的操作及应用的数据对并发的其余事务是隔离的,并发执行的各个事务之间不能相互烦扰。 D - 持久性 Durancy一个事务一旦提交,它对数据库中数据的扭转就应该是永久性的。接下来的其余操作或故障不应该对其有任何影响。 数据库并发拜访抵触问题脏读读取到其余事务未提交的数据。 不可反复读反复读取同一数据时,与之前读取的数据不统一。一个事务提交的数据,能够被另一个事务立刻读取。幻读读取到曾经被删除的数据。读取不到新插入的数据。Mysql 的四种事务隔离级别事务之间为了防止相互烦扰,执行时要进行隔离。也就是A执行时B要期待。但严格的隔离会造成性能的降落。 数据库为了兼顾数据安全和性能,能够在肯定水平上容许多个事务并行执行。 Mysql 提供了四种隔离级别从低到高: READ-UNCOMMITTEDREAD-COMMITTEDREPEATABLE-READSERIALIZABLE隔离级别越高数据越平安;越低性能越好,但会造成数据拜访的问题: 可能引发的问题READ-UNCOMMITTEDREAD-COMMITTEDREPEATABLE-READSERIALIZABLE幻读√√√×不可反复读√√××脏读√×××Mysql 设置隔离级别set tx_isolation='read-uncommitted';set tx_isolation='read-committed';# repeatable-read 是Mysql默认的隔离级别set tx_isolation='repeatable-read';set tx_isolation='serializable';oracle mysql 8 应用 transaction_isolation 零碎变量: ...

December 14, 2020 · 1 min · jiezi

关于分布式事务:浅谈Saga分布式事务

SagaSaga和TCC一样,也是最终一致性事务、柔性事务。Saga的实质就是把一个长事务分隔成一个个小的事务,每个事务都蕴含一个执行模块和弥补模块。Saga的模型如下: 每个Saga由一系列sub-transaction Ti 组成。每个Ti 都有对应的弥补动作Ci,弥补动作用于撤销Ti造成的后果。Saga的执行程序: T1, T2, T3, ..., TnT1, T2, ..., Tj, Cj,..., C2, C1,其中0 < j < nSaga的复原形式: 向后复原:撤销Ti造成的后果。向前复原:始终重试Tj前面的事务,直至胜利,这个时候须要保障肯定胜利。然而其实很难保障的,比方扣减库存,曾经库存为0了,你让他怎么减。所以失常用向后复原。同样用TCC的例子来阐明一下。失常状况下,订单服务把状态改为已出库,提交事务后,调用库存服务,更改库存值。如果库存服务出现异常,向后复原的话,就是把已出库的状态改为未出库。向前复原的话,就是始终调用库存服务,直至库存扣减胜利。 和TCC比照Saga没有try,间接提交事务,所以也没有预留资源。比方库存,TCC会解冻掉,Saga是间接扣除。从通信上来说,Saga通信次数少,等于子事务的个数,而TCC是子事务的2倍(try后再commit一次)。从业务上来说,TCC的侵入性更大,每个业务流程都要Try、Commit、Cancel。Saga只有提供一个中央对事务弥补就好了。

November 13, 2020 · 1 min · jiezi

关于分布式事务:浅谈TCC分布式事务

TCC分布式事务,分为三个阶段,Try(尝试)-Confirm(确认)-Cancel(勾销),他的实质就是预留资源,比方对状态的变更,对金额的解冻,上面通过一个出库的例子来阐明。仓库人员清单完货品,确认出库,此时零碎须要两个操作,一个是库存订单状态从未出库更改为已出库,另外一个是库存的扣减。 trytry的时候就是对资源的预留,比方订单,就要把状态从未出库改为待出库,库存比方是100,此时要加一个解冻库存字段,解冻值为此次的出库数量,假如10,理论库存变成了100-10=90。try的时候间接在数据库做变更,当然咱们并不是间接的变更值,而是先做判断。比方状态是待出库或者已出库,则此时就不能更改。比方库存此时有余10,扣减失败。 Confirm如果合乎出库和库存扣减条件,则确认业务,把待出库的状态改已出库,把库存改为90,解冻为0。 Cancel如果某个条件不满足或者都不满足,则勾销业务,把待出库的状态改为未出库,把库存改为100,解冻为0。也就是说把资源复原到之前的状态。 和2PC区别TCC对业务的侵入性很大,每个子业务都要写Try-Confirm-Cancel,。2PC是在数据库层面上管制的,对开发者是无感知的。2PC是强一致性事务,TCC是最终一致性事务(当然有可能订单服务是try执行胜利,库存服务的try执行失败,这个时候要保障多个零碎的可用性)。TCC每个阶段都是本地提交事务,而2PC会始终锁住资源直至整个过程完结,所以TCC的性能更高,有更高的吞吐量。

November 12, 2020 · 1 min · jiezi

关于分布式事务:分布式事务

Distributed Transaction Processing: Reference Model Distributed Transaction Processing: The XA Specification 1、事务简介        事务(Transaction)是拜访并可能更新数据库中各种数据项的一个程序执行单元(unit)。在关系数据库中,一个事务由一组SQL语句组成。事务应该具备4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID个性。     原子性(atomicity):个事务是一个不可分割的工作单位,事务中包含的诸操作要么都做,要么都不做。     一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态,事务的中间状态不能被察看到的。     隔离性(isolation):一个事务的执行不能被其余事务烦扰。即一个事务外部的操作及应用的数据对并发的其余事务是隔离的,并发执行的各个事务之间不能相互烦扰。隔离性又分为四个级别:读未提交(read uncommitted)、读已提交(read committed,解决脏读)、可反复读(repeatable read,解决虚读)、串行化(serializable,解决幻读)。     持久性(durability):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的扭转就应该是永久性的。接下来的其余操作或故障不应该对其有任何影响。     任何事务机制在实现时,都应该思考事务的ACID个性,包含:本地事务、分布式事务。 2 本地事务        大多数场景下,咱们的利用都只须要操作繁多的数据库,这种状况下的事务称之为本地事务(Local Transaction)。本地事务的ACID个性是数据库间接提供反对。本地事务利用架构如下所示:     在JDBC编程中,咱们通过java.sql.Connection对象来开启、敞开或者提交事务。代码如下所示: 代码块 Plain Text Connection conn = ... //获取数据库连贯 conn.setAutoCommit(false); //开启事务 try{ //...执行增删改查sql conn.commit(); //提交事务 }catch (Exception e) { conn.rollback();//事务回滚 }finally{ conn.close();//敞开链接 }    此外,很多java利用都整合了spring,并应用其申明式事务管理性能来实现事务性能。个别应用的步骤如下:     1、配置事务管理器。spring提供了一个PlatformTransactionManager接口,其有2个重要的实现类:         DataSourceTransactionManager:用于反对本地事务,事实上,其外部也是通过操作java.sql.Connection来开启、提交和回滚事务。 ...

September 27, 2020 · 1 min · jiezi

基于-Seata-Saga-设计更有弹性的金融应用

Seata 意为:Simple Extensible Autonomous Transaction Architecture,是一套一站式分布式事务解决方案,提供了 AT、TCC、Saga 和 XA 事务模式,本文详解其中的 Saga 模式。项目地址:https://github.com/seata/seata 本文作者:屹远(陈龙),蚂蚁金服分布式事务核心研发 。 金融分布式应用开发的痛点分布式系统有一个比较明显的问题就是,一个业务流程需要组合一组服务。这样的事情在微服务下就更为明显了,因为这需要业务上的一致性的保证。也就是说,如果一个步骤失败了,那么要么回滚到以前的服务调用,要么不断重试保证所有的步骤都成功。---《左耳听风-弹力设计之“补偿事务”》 而在金融领域微服务架构下的业务流程往往会更复杂,流程很长,比如一个互联网微贷业务流程调十几个服务很正常,再加上异常处理的流程那就更复杂了,做过金融业务开发的同学会很有体感。 所以在金融分布式应用开发过程中我们面临一些痛点: 业务一致性难以保障我们接触到的大多数业务(比如在渠道层、产品层、集成层的系统),为了保障业务最终一致性,往往会采用“补偿”的方式来做,如果没有一个协调器来支持,开发难度是比较大的,每一步都要在 catch 里去处理前面所有的“回滚”操作,这将会形成“箭头形”的代码,可读性及维护性差。或者重试异常的操作,如果重试不成功可能要转异步重试,甚至最后转人工处理。这些都给开发人员带来极大的负担,开发效率低,且容易出错。 业务状态难以管理业务实体很多、实体的状态也很多,往往做完一个业务活动后就将实体的状态更新到了数据库里,没有一个状态机来管理整个状态的变迁过程,不直观,容易出错,造成业务进入一个不正确的状态。 幂等性难以保障服务的幂等性是分布式环境下的基本要求,为了保证服务的幂等性往往需要服务开发者逐个去设计,有用数据库唯一键实现的,有用分布式缓存实现的,没有一个统一的方案,开发人员负担大,也容易遗漏,从而造成资损。 业务监控运维难,缺乏统一的差错守护能力业务的执行情况监控一般通过打印日志,再基于日志监控平台查看,大多数情况是没有问题的,但是如果业务出错,这些监控缺乏当时的业务上下文,对排查问题不友好,往往需要再去数据库里查。同时日志的打印也依赖于开发,容易遗漏。对于补偿事务往往需要有“差错守护触发补偿”、“工人触发补偿”操作,没有统一的差错守护和处理规范,这些都要开发者逐个开发,负担沉重。 理论基础一些场景下,我们对数据有强一致性的需求时,会采用在业务层上需要使用“两阶段提交”这样的分布式事务方案。而在另外一些场景下,我们并不需要这么强的一致性,那就只需要保证最终一致性就可以了。 例如蚂蚁金服目前在金融核心系统使用的就是 TCC 模式,金融核心系统的特点是一致性要求高(业务上的隔离性)、短流程、并发高。 而在很多金融核心以上的业务(比如在渠道层、产品层、集成层的系统),这些系统的特点是最终一致即可、流程多、流程长、还可能要调用其它公司的服务(如金融网络)。这是如果每个服务都开发 Try、Confirm、Cancel 三个方法成本高。如果事务中有其它公司的服务,也无法要求其它公司的服务也遵循 TCC 这种开发模式。同时流程长,事务边界太长会影响性能。 对于事务我们都知道 ACID,也很熟悉 CAP 理论最多只能满足其中两个,所以,为了提高性能,出现了 ACID 的一个变种 BASE。ACID 强调的是一致性(CAP 中的 C),而 BASE 强调的是可用性(CAP 中的 A)。我们知道,在很多情况下,我们是无法做到强一致性的 ACID 的。特别是我们需要跨多个系统的时候,而且这些系统还不是由一个公司所提供的。BASE 的系统倾向于设计出更加有弹力的系统,在短时间内,就算是有数据不同步的风险,我们也应该允许新的交易可以发生,而后面我们在业务上将可能出现问题的事务通过补偿的方式处理掉,以保证最终的一致性。 所以我们在实际开发中会进行取舍,对于更多的金融核心以上的业务系统可以采用补偿事务,补偿事务处理方面在30年前就提出了 Saga 理论,随着微服务的发展,近些年才逐步受到大家的关注。目前业界比较也公认 Saga 是作为长事务的解决方案。 https://github.com/aphyr/dist-sagas/blob/master/sagas.pdfhttp://microservices.io/patterns/data/saga.html社区和业界的方案Apache Camel SagaCamel 是实现 EIP(Enterprise Integration Patterns)企业集成模式的一款开源产品,它基于事件驱动的架构,有着良好的性能和吞吐量,它在2.21版本新增加了 Saga EIP。 Saga EIP 提供了一种方式可以通过 camel route 定义一系列有关联关系的 Action,这些 Action 要么都执行成功,要么都回滚,Saga 可以协调任何通讯协议的分布式服务或本地服务,并达到全局的最终一致性。Saga 不要求整个处理在短时间内完成,因为它不占用任何数据库锁,它可以支持需要长时间处理的请求,从几秒到几天,Camel 的 Saga EIP 是基于 Microprofile 的 LRA(Long Running Action),同样也是支持协调任何通讯协议任何语言实现的分布式服务。 ...

November 5, 2019 · 2 min · jiezi

阿里开源分布式事务组件-seata-AT-模式的分支事务处理

AT 模式与 TCC 模式seata 的事务处理模式主要分为两种,分别是 AT 模式和 TCC 模式,其实在早期的 seata 版本里(还叫 fescar 的时候),它把模式分为:AT 模式,MT 模式和混合模式。那时还未深入了解 seata 时,对这样的分类模式感到有点奇怪,咱也不知道,更是不敢问。不过现在这种只分为 AT 模式 和 TCC 模式的分类方法,我觉得是更合理的。在王者荣耀这款游戏里,我听说过“万物皆可打野”,“万物皆可百穿”。在算法的世界里,我听说过“万物皆可动态规划”。在分布式事务的处理里,我觉得“万物皆可 TCC”。seata 的 AT 模式主要是针对全局事务只涉及到数据库数据的场景,实际上 seata 的 AT 模式也是一种 TCC 模式,只不过,CC 阶段是程序自动完成的,不需要业务开发人员去关心如何去 Confirm,如何去 Cancel。在 AT 模式中,Try 阶段实际上已经完成本地事务的提交,而 Confirm 阶段则是删除 Undo log,Cancel 阶段则是利用 undo log,做数据的回放。使用 seata 的朋友,如果 seata 给你一种分布式事务的接入很简单的感觉,你还是要记得你是在使用 TCC 模式。 AT 模式原理seata 的 AT 模式,采用的是大量运用在数据库软件的 Write Ahead Log 思想,即把事务的信息以事务日志的方式记录下来。这种处理方式,实际上是对传统两阶段提交的一种改进和优化,在《浅析阿里分布式事务组件 fescar/seata 2pc 的设计思想》 这篇文章我也简单的分析了一下。主要有几个关键点: 传统两阶段提交协议是阻塞协议,性能差传统两阶段提交协议高可用性不好传统两阶段提交协议的全局事务隔离机制不支持根据八二原则,80% 的涉及到全局事务的业务是能正常完成并提交的。因此,在 AT 模式下,seata 采取的做法是,一个事务分支的数据库操作执行完后,马上进行本地事务的提交,从而释放相关的数据库资源。 ...

October 14, 2019 · 12 min · jiezi

阿里开源分布式事务组件-seata-demo-环境搭建以及运行流程简析

案例设计seata 官方给出了一系列 demo 样例,不过我在用的过程中发现总有这个那个的问题,所以自己维护了一份基于 dubbo 的 demo 在 github 上,适配的 seata 版本是 0.8.0。案例的设计直接参考官方 quick start给出的案例: 整个案例分为三个服务,分别是存储服务、订单服务和账户服务,这些服务通过 dubbo 进行发布和调用,内部调用逻辑如上面图所示。整个 demo 的工程样例如下所示: undo_log 表这个案例除了在数据库需要建立业务表以外,还要额外建立一张 undo_log 表,这个表的主要作用是记录事务的前置镜像和后置镜像。全局事务进行到提交阶段,则删除该表对应的记录,全局事务如果需要回滚,则会利用这个表里记录的镜像数据,恢复数据。undo_log 表里的数据实际上是“朝生夕死”的,数据不需要在表里存活太久。表结构如下所示: CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, `ext` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;服务逻辑每个服务都对应了一个 starter 类,这个类主要用来在 spring 环境下,将该服务启动,并通过 dubbo 发布出去,以账户服务为例: ...

October 9, 2019 · 4 min · jiezi

阿里开源分布式事务组件-seata-配置机制简析

seata 的客户端代码和服务端代码逻辑里,读取配置时统一采用的以下这种 API ConfigurationFactory.getInstance().getString()seata 目前(0.8.0)支持以下几种配置方式 本地文件方式zookeepernacosapolloconsuletcd在分析 seata 的配置解析细节之前,先看看 seata 对于配置解析机制的设计具体来说,seata 的配置项的命名风格都是类似于 computer.apple.macbookpro 这种的文本扁平化风格。它本质上还是一个结构形的配置方式,即每个具体配置项都有父节点,举个例子: computer.apple { macbookpro = 12000 macbookair = 8000}这种配置方式与普通的 xml 配置文件在结构上没有什么大的区别,但与 xml 相比 还是有不少的好处和优点。 简洁明了,xml 本质上还是个标记型语言,引入了许多不必要的复杂标记,间接增加了解析成本解析阶段,xml 定位到某个配置项的逻辑更加繁琐配置更新时如果需要更新文件,xml 的文件的更新动作不够轻量级,如果依赖一些第三方实现,还会造成代码入侵,可扩展性差。相比之下,目前主流的配置中心例如 zookeeper 或者 apollo,这些软件设计之初对数据结构的选型就与 computer.apple.macbookpro 这样的配置形态很相称。例如,zookeeper 的数据结构是类似于文件系统的树状风格,所以 zookeeper 之前是很适合拿来做公共配置中心的,直到后面更优秀的配置中心出现,zookeeper 才慢慢淡出配置界,毕竟 zookeeper 它更擅长做的事情是分布式一致性的协调。正是 seata 采用了这种配置风格,所以 seata 在配置中心的支持这一块,就很方便地与当前主流的配置中心做集成。 试想一下,如果要把一个 xml 配置文件存到 zookeeper 上做全局管理。大概有这么两种方式吧: 把整个 xml 文本存到一个 znode 上把 xml 的结构解析称树状结构,再一个一个对应地到 zookeeper 上创建节点第一种方式,显然很 low 哦。干脆存到数据库算了。第二种方式,会带来额外的解析成本,并且不容易做配置变更这样的逻辑,因为一个配置项的变更,意味着要重新在内存里生成整个 xml 文本,然后再写进本地配置文件里面。 下面说一说 seata 的配置机制,因为 seata 支持上述这么多主流的配置中心,但是实际上使用的时候,必须也只能用一个。因此,一个 seata 相关的进程启动的时候,必然要从本地某个地方直到要用什么配置中心。而实际上,seata 是先读取本地的一个配置文件 registry.conf,再决定要用什么样的配置方式的。这个逻辑在我们上面提到的 API ConfigurationFactory.getInstance() 的源码里可以看到具体的细节。 ...

September 19, 2019 · 3 min · jiezi

浅析分布式事务中的2PC和3PC

分布式事务大型的分布式系统架构都会涉及到事务的分布式处理的问题,基本来说,分布式事务和普通事务都有一个共同原则: A(Atomic) 原子性,事务要么一起完成,要么一起回滚C(Consistent) 一致性,提交数据前后数据库的完整性不变I(Isolation) 隔离性,不同的事务相互独立,不互相影响D(Duration)持久性,数据状态是永久的另外一个设计原则,分布式系统要符合以下原则: C(Consistent)一致性,分布式系统中存在的多个副本,在数据和内容上要一模一样A(Availability)高可用性,在有限时间内保证系统响应P(Partition tolerance)分区容错性,一个副本或一个分区出现宕机或其他错误时,不影响系统整体运行由于在设计分布式系统时,难以保证CAP全部符合,最多保证其中两个,例如在一个分布式系统中:要保证系统数据库的强一致性,需要跨表跨库占用数据库资源,在复杂度比较高的情况下,耗时难以保证,就会出现可用性无法保证的情况。 故而又出现了BASE理论 Basic Available基本可用,分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用Soft state柔性状态,允许系统存在中间状态,这个状态不影响系统可用性Eventually Consistent 最终一致性 指经过一段时间之后,所有数据状态保持一致的结果实际上BASE理论是基于AP的一个扩展,一个分布式事务不需要强一致性,只需要达到一个最终一致性就可以了。 分布式事务就是处理分布式中各个节点的数据操作,使之能够在节点相互隔离的情况下完成多个节点数据的一致性操作。 分步式事务 - 2PC2PC即二阶段提交,在这里需要了解两个概念 参与者:参与者即各个实际参与事务的节点,这些节点相互隔离,彼此不知道对方是否提交事务。协调者:收集和管理参与者的事务信息,负责统一管理节点信息,也就是分布式事务中的第三方。准备阶段,也称投票阶段准备阶段中,协调者向参与者发送precommit的消息,参与者在本地执行事务逻辑,如果没有超时以及运行无误,那么会记录redo和undo日志,将ack信息发送给协调者,当协调者收到所有节点参与者发送的ack信息时,准备进入下一阶段,否则会发送回滚信息给各个节点,各个节点根据之前记录好的undo信息回滚,本次事务提交失败。 提交阶段提交阶段中,协调者已经收到所有节点的应答信息,接下来发送commit消息给各个节点,通知各个节点参与者可以提交事务,各个参与者提交完毕后一一发送完成信息给协调者,并释放本地占用的资源,协调者收到所有完成消息后,完成事务。 二阶段提交的图示如下: 二阶段提交的问题二阶段提交虽然能够解决大多数的分布式事务的问题,且发生数据错误的概率极小,但仍然有以下几个问题: 单点故障:如果协调者宕机,那么参与者会一直阻塞下去,如果在参与者收到提交消息之前协调者宕机,那么参与者一直保持未成功提交的状态,会一直占用资源,导致同时间其他事务可能要一直等待下去。同步阻塞问题:因为参与者是在本地处理的事务,是阻塞型的,在最终commit之前,一直占用锁资源,如果遇到时间较长而协调者未发回commit的情况,那么会导致锁等待。数据一致性问题:假如协调者在第二阶段中,发送commit给某些参与者失败,那么就导致某些参与者提交了事务,而某些没有提交,这就导致了数据不一致。二阶段根本无法解决的问题:假如协调者宕机的同时,最后一个参与者也宕机了,当协调者通过重新选举再参与到事务管理中时,它是没有办法知道事务执行到哪一步的。针对于以上提出的一些问题,衍生出了3PC。 分布式事务 - 3PC3PC即三阶段提交协议。 3PC相对于2PC,有了以下变化: 引入超时机制,在参与者和协调者中都加入了各自的超时策略。加入了一个新的阶段,canCommit,所以阶段分成了三个:canCommit、PreCommit、DoCommit。CanCommit这是一个准备阶段,在这一阶段中,协调者向参与者发送CanCommit消息,参与者接收后,根据自身资源情况判断是否可以执行事务操作,如果可以并且未超时,则发送yes给协调者,反之,协调者会中断事务。 PreCommit这是预备阶段,在这一阶段中,协调者向参与者发送PreCommit消息,参与者接收后,会执行事务操作,记录undo和redo日志,事务执行完毕后会将Ack消息发送给协调者,协调者在未超时的情况下收集所有参与者的信息,否则中断事务,通知所有参与者Abort消息。 DoCommit当所有参与者Ack消息完毕之后,协调者会确认发送DoCommit消息给每一个参与者,执行提交事务。原则上所有参与者执行提交完毕之后,需要发送Committed给协调者,协调者完成事务。否则协调者中断事务,参与者接收abort消息会根据之前记录的undo回滚。但也要注意,此阶段中如果参与者无法及时收到协调者发来的Docommit消息时,也会自行提交事务,因为从概率上来讲,PreCommit这个阶段能够不被abort说明全部节点都可以正常执行事务提交,所以一般来讲单个节点提交不影响数据一致性,除非极端情况。 2PC 和 3PC的区别相对于2PC,3PC主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行commit。而不会一直持有事务资源并处于阻塞状态。但是这种机制也会导致数据一致性问题,因为,由于网络原因,协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。 消息中间件上面提到的关于协调者在实践过程中由谁来担任是个问题,一般来讲这个协调者是异步的,可以在参与者和协调者之间双向通信的,有良好的消息传递机制,能够保证消息稳定投递。故而一般协调者的担任者是高性能的消息中间件例如RocketMq、Kafka、RabbitMq等。 如图就是一个2PC的实践:

July 12, 2019 · 1 min · jiezi

通俗易懂地介绍分布式锁实现

文章来源:www.liangsonghua.me作者介绍:京东资深工程师-梁松华,长期关注稳定性保障、敏捷开发、JAVA高级、微服务架构 一般情况下我们会通过下面的方法进行资源的一致性保护 // THIS CODE IS BROKENfunction writeData(filename, data) { var lock = lockService.acquireLock(filename); if (!lock) { throw 'Failed to acquire lock'; } try { var file = storage.readFile(filename); var updated = updateContents(file, data); storage.writeFile(filename, updated); } finally { lock.release(); }}但是很遗憾的是,上面这段代码是不安全的,比如客户端client-1获取锁后由于执行垃圾回收GC导致一段时间的停顿(stop-the-word GC pause)或者其他长时间阻塞操作,此时锁过期了,其他客户如client-2会获得锁,当client-1恢复后就会出现client-1client-2同时处理获得锁的状态 我们可能会想到通过令牌或者叫版本号的方式,然而在使用Redis作为锁服务时并不能解决上述的问题。不管我们怎么修改Redlock生成token的算法,使用unique random随机数是不安全的,使用引用计数也是不安全的,一个redis node服务可能会出宕机,多个redis node服务可能会出现同步异常(go out of sync)。Redlock锁会失效的根本原因是Redis使用getimeofday作为key缓存失效时间而不是监视器(monitonic lock),服务器的时钟出现异常回退无法百分百避免,ntp分布式时间服务也是个难点 分布式锁实现需要考虑锁的排它性和不能释放它人的锁,作者不推荐使用Redlock算法,推荐使用zookeeper或者数据库事务(个人不推荐:for update性能太差了) 补充:使用zookeeper实现分布式锁 可以通过客户端尝试创建节点路径,成功就获得锁,但是性能较差。更好的方式是利用zookeeper有序临时节点,最小序列获得锁,其他节点lock时需要阻塞等待前一个节点(比自身序列小的最大那个)释放锁(countDownLatch.wait()),当触发watch事件时将计数器减一(countDownLatch.countDown()),然后此时最小序列节点将会获得锁。可以利用Curator简化操作,示例如下 public static void main(String[] args) throws Exception { //重试策略 RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); //创建工厂连接 final CuratorFramework curatorFramework = CuratorFrameworkFactory.builder().connectString(connetString) .sessionTimeoutMs(sessionTimeOut).retryPolicy(retryPolicy).build(); curatorFramework.start(); //创建分布式可重入排他锁,监听客户端为curatorFramework,锁的根节点为/locks final InterProcessMutex mutex = new InterProcessMutex(curatorFramework, "/lock"); final CountDownLatch countDownLatch = new CountDownLatch(1); for (int i = 0; i < 100; i++) { new Thread(new Runnable() { @Override public void run() { try { countDownLatch.await(); //加锁 mutex.acquire(); process(); } catch (Exception e) { e.printStackTrace(); }finally { try { //释放锁 mutex.release(); System.out.println(Thread.currentThread().getName() + ": release lock"); } catch (Exception e) { e.printStackTrace(); } } } },"Thread" + i).start(); } Thread.sleep(100); countDownLatch.countDown(); } }补充:redis实现分布式锁 ...

July 11, 2019 · 2 min · jiezi

分布式事务中间件Seata的设计原理

微信公众号「后端进阶」,专注后端技术分享:Java、Golang、WEB框架、分布式中间件、服务治理等等。在微服务架构体系下,我们可以按照业务模块分层设计,单独部署,减轻了服务部署压力,也解耦了业务的耦合,避免了应用逐渐变成一个庞然怪物,从而可以轻松扩展,在某些服务出现故障时也不会影响其它服务的正常运行。总之,微服务在业务的高速发展中带给我们越来越多的优势,但是微服务并不是十全十美,因此不能盲目过度滥用,它有很多不足,而且会给系统带来一定的复杂度,其中伴随而来的分布式事务问题,是微服务架构体系下必然需要处理的一个痛点,也是业界一直关注的一个领域,因此也出现了诸如 CAP 和 BASE 等理论。 在今年年初,阿里开源了一个分布式事务中间件,起初起名为 Fescar,后改名为 Seata,在它开源之初,我就知道它肯定要火,因为这是一个解决痛点的开源项目,Seata 一开始就是冲着对业务无侵入与高性能方向走,这正是我们对解决分布式事务问题迫切的需求。因为待过的几家公司,用的都是微服务架构,但是在解决分布式事务的问题上都不太优雅,所以我也在一直关注 Seata 的发展,今天就简要说说它的一些设计上的原理,后续我将会对它的各个模块进行深入源码分析,感兴趣的可以持续关注我的公众号或者博客,不要跟丢。 分布式事务解决的方案有哪些?目前分布式事务解决的方案主要有对业务无入侵和有入侵的方案,无入侵方案主要有基于数据库 XA 协议的两段式提交(2PC)方案,它的优点是对业务代码无入侵,但是它的缺点也是很明显:必须要求数据库对 XA 协议的支持,且由于 XA 协议自身的特点,它会造成事务资源长时间得不到释放,锁定周期长,而且在应用层上面无法干预,因此它性能很差,它的存在相当于七伤拳那样“伤人七分,损己三分”,因此在互联网项目中并不是很流行这种解决方案。 为了这个弥补这种方案带来性能低的问题,大佬们又想出了很多种方案来解决,但这无一例外都需要通过在应用层做手脚,即入侵业务的方式,比如很出名的 TCC 方案,基于 TCC 也有很多成熟的框架,如 ByteTCC、tcc-transaction 等。以及基于可靠消息的最终一致性来实现,如 RocketMQ 的事务消息。 入侵代码的方案是基于现有情形“迫不得已”才推出的解决方案,实际上它们实现起来非常不优雅,一个事务的调用通常伴随而来的是对该事务接口增加一系列的反向操作,比如 TCC 三段式提交,提交逻辑必然伴随着回滚的逻辑,这样的代码会使得项目非常臃肿,维护成本高。 Seata 各模块之间的关系针对上面所说的分布式事务解决方案的痛点,那很显然,我们理想的分布式事务解决方案肯定是性能要好而且要对业务无入侵,业务层上无需关心分布式事务机制的约束,Seata 正是往这个方向发展的,因此它非常值得期待,它将给我们的微服务架构带来质的提升。 那 Seata 是怎么做到的呢?下面说说它的各个模块之间的关系。 Seata 的设计思路是将一个分布式事务可以理解成一个全局事务,下面挂了若干个分支事务,而一个分支事务是一个满足 ACID 的本地事务,因此我们可以操作分布式事务像操作本地事务一样。 Seata 内部定义了 3个模块来处理全局事务和分支事务的关系和处理过程,这三个组件分别是: Transaction Coordinator (TC): 事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。Transaction Manager (TM): 控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。Resource Manager (RM): 控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。 简要说说整个全局事务的执行步骤: TM 向 TC 申请开启一个全局事务,TC 创建全局事务后返回全局唯一的 XID,XID 会在全局事务的上下文中传播;RM 向 TC 注册分支事务,该分支事务归属于拥有相同 XID 的全局事务;TM 向 TC 发起全局提交或回滚;TC 调度 XID 下的分支事务完成提交或者回滚。与 XA 方案有什么不同?Seata 的事务提交方式跟 XA 协议的两段式提交在总体上来说基本是一致的,那它们之间有什么不同呢? ...

July 11, 2019 · 2 min · jiezi

ShardingSphere-x-Seata一致性更强的分布式数据库中间件

日前,分布式数据库中间件 ShardingSphere 将 Seata 分布式事务能力进行整合,旨在打造一致性更强的分布式数据库中间件。 背景数据库领域,分布式事务的实现主要包含:两阶段的 XA 和 BASE 柔性事务。XA 事务底层,依赖于具体的数据库厂商对 XA 两阶段提交协议的支持。通常,XA 协议通过在 Prepare 和 Commit 阶段进行 2PL(2 阶段锁),保证了分布式事务的 ACID,适用于短事务及非云化环境(云化环境下一次 IO 操作大概需要 20ms,两阶段锁会锁住资源长达 40ms,因此热点行上的事务的 TPS 会降到 25/s 左右,非云化环境通常一次 IO 只需几毫秒,因此锁热点数据的时间相对较低)。 但在 BASE 柔性事务方面,ShardingSphere 提供的接入分布式事务的 SPI,只适用于对性能要求较高,对一致性要求比较低的业务。 Seata 核心的 AT 模式适用于构建于支持本地 ACID 事务的关系型数据库。通过整合 Seata,其 AT 模式在一阶段提交+补偿的基础上,通过 TC 的全局锁实现了 RC 隔离级别的支持,可提高 ShardingSphere 的分布式事务的一致性。 整合方案整合 Seata AT 事务时,需要把 TM,RM,TC 的模型融入到 ShardingSphere 分布式事务的 SPI 的生态中。在数据库资源上,Seata 通过对接 DataSource 接口,让 JDBC 操作可以同 TC 进行 RPC 通信。同样,ShardingSphere 也是面向 DataSource 接口对用户配置的物理 DataSource 进行了聚合,因此把物理  DataSource 二次包装为 Seata 的 DataSource 后,就可以把 Seata AT 事务融入到 ShardingSphere 的分片中。 ...

July 4, 2019 · 1 min · jiezi

Paxos-Made-Simple

1. 简介用于实现高容错性分布式系统的Paxos算法,一直以来总是被认为是难以理解的,或许是因为对很多人来说,初始版本就像是”希腊语"一样(最初的论文是以希腊故事展开的形式)[5]。实际上,它也算是最浅显易见的分布式算法之一了。它的核心就是一个一致性算法——论文[5]中的“synod”算法。在下一个章节可以看到,它基本上是根据一个一致性算法所必需满足的条件自然而然地推断出来的。最后一个章节,我们通过将Paxos算法作为构建一个实现了状态机的分布式系统的一致性实现,来完整地描述它。这种使用状态机方法的论文[4]应该早已广为人知,因为它可能已经是分布式系统理论研究领域被引用最广泛的了。 2. 一致性算法2.1 问题描述假设有一组可以提出提案的进程集合。一个一致性算法需要保证:在这些被提出的提案中,只有一个会被选定。如果没有提案被提出,则不会有被选定的提案。当一个提案被选定后,进程应该能获取被选定提案的信息。 对于一致来说,安全性(Safety)需求是这样的:只有被提出的提案才能被选定。只能有一个值被选中(chosen),同时进程不能认为某个提案被选定,除非它真的是被选定的那个。 我们不会尝试去精确地描述活性(Liveness)需求。但是从总体上看,最终的目标是保证有一个提案被选定,并且当提案被选定后,进程最终也能获取到被选定提案的信息。一个分布式算法,有两个重要的属性:Safety和Liveness,简单来说:Safety是指那些需要保证永远都不会发生的事情Liveness是指那些最终一定会发生的事情 在这个一致性算法中,有三个参与角色,我们分别用Proposer,Acceptor和Learner来表示。在具体实现中,一个进程可能充当不止一种角色,但是在这里我们并不关心它们之间的映射关系。 假设不同的参与者之间可以通过发消息来进行通信,我们使用普通的非拜占庭模式的异步模型:每个参与者以任意的速度运行,可能会因停止而执行失败,也可能会重启。当一个提案被选定后,所有的参与者都有可能失败然后重启,除非这些参与者可以记录某些信息,否则是不可能存在一个解法的。消息在传输中可能花费任意时间,可能会重复,也可能丢失,但不会被损坏(不会被篡改,即不会发生拜占庭问题)。 2.2 提案的选定选定提案最简单的方式就是只有一个Acceptor存在。Proposer发送提案给Acceptor,Acceptor会选择它接收到的第一个提案作为被选提案。虽然简单,这个解决方案却很难让人满意,因为当Acceptor出错时,整个系统就无法工作了。 因此,我们应该选择其他方式来选定提案,比如可以用多个Acceptor来避免一个Acceptor的单点问题。这样的话,Proposer向一个Acceptor集合发送提案,某个Acceptor可能会通过(accept)这个提案。当有足够多的Acceptor通过它时,我们就认为这个提案被选定了。那么怎样才算是足够多呢?为了确保只一个提案被选定,我们可以让这个集合大到包含了Acceptor集合中的多数成员。因为任意两个多数集(majority)至少包含一个公共成员,如果我们再规定一个Acceptor只能通过一个提案,那么就能保证只有一个提案被选定(这是很多论文都研究过的多数集的一个普通应用[3])。 假设没有失败和消息丢失的情况,如果我们希望在每个Proposer只能提出一个提案的前提下仍然可以选出一个提案来,这就意味着如下需求: P1. 一个Acceptor必须通过它收到的第一个提案。但是这个需求会引发另外的问题。如果有多个提案被不同的Proposer同时提出,这会导致虽然每个Acceptor都通过了一个提案,但是没有一个提案是由多数人通过的。甚至即使只有两个提案被提出,如果每个都被差不多一半的Acceptor通过了,哪怕只有一个Acceptor出错都可能导致无法确定该选定哪个提案。比如有5个Acceptor,其中2个通过了提案a,另外3个通过了提案b,此时如果通过提案b的3个当中有一个出错了,那么a和b的通过数都为2, 这样就无法确定了。 P1再加一个提案被选定需要由半数以上Acceptor通过的这个需求,暗示着一个Acceptor必须要能通过不止一个提案。我们为每个提案分配一个编号来记录一个Acceptor通过的那些提案,于是一个提案就包含一个提案编号以及它的value值。为了避免造成混淆,需要保证不同的提案具有不同编号。如何实现这个功能依赖于具体的实现细节,在这里我们假设已经实现了这种保证。当一个具有value值的提案被多数Acceptor通过后,我们就认为该value被选定了。同时我们也认为该提案被选定了。 我们允许多个提案被选定,但是我们必须保证所有被选定的提案具有相同的值value。通过对提案编号的约定,它需要满足以下保证: P2. 如果具有value值v的提案被选定了,那么所有比它编号高的提案的value值也必须是v。 因为编号是完全有序的,所以条件P2就保证了只有一个value值被选定这一关键安全性属性。 一个提案能被选定,必须要被至少一个Acceptor通过,所以我们可以通过满足如下条件来满足P2: P2a. 如果一个具有value值v的提案被选定了,那么被Acceptor通过的所有编号比它高的提案的value值也必须是v。我们仍然需要P1来保证有提案会被选定。因为通信是异步的,一个提案可能会在某个Acceptor c还没收到任何提案时就被选定了。假设有个新的Proposer苏醒了,然后提出了一个具有不同value值的更高编号的提案,根据P1, 需要c通过这个提案,但这是与P2a相矛盾的。因此为了同时满足P1和P2a,需要对P2a进行强化: P2b. 如果具有value值v的提案被选定了,那么所有比它编号更高的被Proposer提出的提案的value值也必须是v。一个提案被Acceptor通过之前肯定是由某个Proposer提出,因此P2b就隐含P2a,进而隐含了P2. 为了发现如何保证P2b,我们来看看如何证明它成立。我们假设某个具有编号m和value值v的提案被选定了,需要证明任意具有编号n(n > m)的提案都具有value值v。我们可以通过对n使用数学归纳法来简化证明,这样我们可以在额外的假设下——即编号在m..(n-1)之间的提案具有value值v,来证明编号为n的提案具有value值v,其中i..j表示从i到j的集合。因为编号为m的提案已经被选定了,这就意味着存在一个多数Acceptor组成的集合C,C中的每个成员都通过了这个提案。结合归纳的假设,m被选定意味着: C中的每一个Acceptor都通过了一个编号在m..(n-1)之间的提案,并且每个编号在m..(n-1)之间的被Acceptor通过的提案都具有value值v。由于任何包含多数Acceptor的集合S都至少包含一个C中的成员,我们可以通过保持如下不变性来确保编号为n的提案具有value值v: P2c. 对于任意v和n,如果一个编号为n,value值为v的提案被提出,那么肯定存在一个由多数Acceptor组成的集合S满足以下条件中的一个: a. S中不存在任何Acceptor通过了编号小于n的提案 b. v是S中所有Acceptor已经通过的编号小于n的具有最大编号的提案的value值。通过维护P2c的不变性我们就可以满足P2b的条件了。 为了维护P2c的不变性,一个Proposer在提出编号为n的提案时,如果存在一个将要或者已经被多数Acceptor通过的编号小于n的最大编号提案,Proposer需要知道它的信息。获取那些已经被通过的提案很简单,但是预测未来会被通过的却很困难。为了避免去预测未来,Proposer通过提出承诺不会有那样的通过情况来控制它。换句话说,Proposer会请求那些Acceptor不要再通过任何编号小于n的提案了。这就导致了如下的提案生成算法:Proposer选择一个新的提案编号n,然后向某个Acceptor集合中的成员发送请示,要求它作出如下回应: (a)保证不再通过任何编号小于n的提案。 (b)当前它已经通过的编号小于n的最大编号提案,如何存在的话。 我们把这样的请求称为编号为n的prepare请求。如果Proposer收到来自集合中多数成员的响应结果,那么它可以提出编号为n,value值为v的提案,这里v是所有响应中最大编号提案的value值,如果响应中不包含任何提案,那么这个值就由Proposer自由决定。 Proposer通过向某个Acceptor集合发送需要被通过的提案请求来产生一个提案(这里的Acceptor集合不一定是响应前一个请求的集合)。这们把这个叫做accept请求。 目前我们描述了Proposer端的算法。那么Acceptor端是怎样的呢?它可能会收到来自Proposer端的两种请求:prepare请求和accept请求。Acceptor可以忽略任意请求而不用担心破坏算法的安全性。因此我们只需要说明它在什么情况下可以对一个请求作出响应。它可以在任何时候响应prepare请求也可以在不违反现有承诺的情况下响应accept请求。换句话说: P1a. 一个Acceptor可以通过一个编号为n的提案,只要它还未响应任何编号大于n的prepare请求。可以看出P1a包含了P1。 现在我们就获得了一个满足安全性需求的提案选定算法——假设在提案编号唯一的前提下。只要再做点小优化,就能得到最终的算法了。假设一个Acceptor收到了一个编号为n的prepare请求,但是它已经对编号大于n的prepare请求作出了响应,因此它肯定不会再通过任何新的编号为n的提案。那么它就没有必要对这个请求作出响应,因为它肯定不会通过编号为n的提案,于是我们会让Acceptor忽略这样的prepare请求,我们也会让它忽略那些它已经通过的提案的prepare请求。通过这个优化,Acceptor只需要记住它已经通过的提案的最大编号以及它已经响应过prepare请求的提案的最大编号。因为必须要在出错的情况下也保证P2c的不变性,所以Acceptor要在故障和重启的情况下也能记住这些信息。Proposer可以随时丢弃提案以及它的所有信息——只要它可以保证不会提出具有相同编号的提案即可。 把Proposer和Acceptor的行为结合起来,我们就能得到算法的如下两阶段执行过程:Phase 1:Proposer选择一个提案编号n,然后向Acceptor的多数集发送编号为n的prepare请求。如果一个Acceptor收到一个编号为n的prepare请示,且n大于它所有已响应请求的编号,那么它就会保证不会再通过任意编号小于n的提案,同时将它已经通过的最大编号提案(如果存在的话)一并作为响应。Phase 2:如果Proposer收到多数Acceptor对它prepare请求(编号为n)的响应,那么它就会发送一个编号为n,value值为v的提案的accept请求给每个Acceptor,这里v是收到的响应中最大编号提案的值,如果响应中不包含任何提案,那么它就可以是任意值。如果Acceptor收到一个编号为n的提案的accept请求,只要它还未对编号大于n的prepare作出响应,它就可以通过这个提案。 一个Proposer可以提出多个提案,只要它能遵循以上算法约定。它可以在任意时刻丢弃某个提案(即使针对该提案的请求或响应在提案丢弃后很久才到达,正确性依然可以保证)。如果Proposer已经在尝试提交更大编号的提案,那么丢弃也未尝不是一件好事。因此,如果一个Acceptor因为已经收到更高编号的prepare请求而忽略某个prepare或者accept请求,它应该通知对应的Proposer,然后该Proposer可以丢弃这个提案。这是一个不影响正确性的性能优化。 2.3 获取被选定的提案值为了获取被选定的值,一个Learner必须要能知道一个提案已经被多数Acceptor通过了。最直观的算法是,让每个Acceptor在通过一个提案时就通知所有Learner,把通过的提案告知它们。这可以让Learner尽快找到被选定的值,但这需要每个Acceptor和Learner之间互相通信——通信次数等于二者数量的乘积。 在假设非拜占庭错误的前提下,一个Learner可以很容易地通过另一个Learner了解一个值已经被选定了。我们可以让所有Acceptor将它们的通过信息发送给一个特定的Learner,当一个value被选定时,由它来通知其他Learner。这种方法需要额外一个步骤才能通知到所有Learner,而且它也不是可靠的,因为那个特定的Learner可能会发生一些故障。但是这种情况下的通信次数,只需要二者数量之和。 更一般地,Acceptor可以将它们的通过信息发送给一个特写的Learner集合,它们中的任何一个都可以在某个value被选定后通知所有Learner。这个集合中的Learner越多,可靠性就越好,通信复杂度也相应更高。 因为消息可能会丢失,一个value被选定后,可能没有Learner会发现。Learner可以向Acceptor询问它们通过了哪些提案,但是任一Acceptor出错,都有可能导致无法分辨是否有多数Acceptor通过了某个提案。在这种情况下,只有当一个新的提案被选定时,Learner才能发现被选定的value。如果一个Learner想知道是否已经选定一个value,它可以让Proposer利用上面的算法提出一个提案。 2.4 进展性很容易可以构造出这样一种情况,两个Proposer持续地提出序号递增的提案,但是没有提案会被选定。Proposer p为编号为n1的提案完成Phase 1, 然后另一个Proposer q为编号为n2(n2>n1)的提案完成Phase 1。Proposer p对于编号n1的Phase 2的accept请求会被忽略,因为Acceptor承诺不再通过任何编号小于n2的提案。这样Proposer p就会用一个新的编号n3(n3>n2)重新开始并完成Phase 1,这又导致了Proposer q对于Phase 2的accept请求被忽略,如此往复。 ...

June 10, 2019 · 2 min · jiezi

TiKV-成功晋级-CNCF-孵化项目

今天,CNCF(Cloud Native Computing Foundation,云原生计算基金会)技术监督委员会(TOC)宣布已经投票决议通过,正式将 TiKV 从沙箱项目晋级至孵化项目。TiKV 是一个开源的分布式事务 Key-Value 数据库,支持跨行 ACID 事务,同时实现了自动水平伸缩、数据强一致性、跨数据中心高可用和云原生等重要特性,最初由 PingCAP 团队在 2016 年作为 TiDB 的底层存储引擎设计并开发,于 2018 年 8 月被 CNCF 宣布接纳为 CNCF 沙箱云原生项目。 对于 TiKV 的此次晋级,CNCF 首席技术及运营官 Chris Aniszczyk 表示:“社区需要更多支持一致性和可伸缩性的云原生存储选项,TiKV 填补了这个空缺,而不依赖于任何分布式文件系统。自从加入 CNCF 以来,我们看到该项目在中国和国外都取得了令人瞩目的增长。随着它进入孵化阶段,我们很高兴看到该项目持续增长,期待新的贡献者继续添加更多新功能。” TiKV 最初的设计便采用云原生架构,并很好地融入了现有的 CNCF 生态系统:使用 Prometheus 进行集群监控,使用 gRPC 进行通信,可以部署在 Kubernetes 上,采用 Operator 简化安装、升级和维护。 作为一个基础组件,TiKV 可作为构建其它系统的基石。除了作为分布式 HTAP 数据库 TiDB 的存储引擎,还有更多的存储系统构建于 TiKV 之上,包括三个 Redis-on-TiKV 项目:Tidis、Titan 以及 Titea ,和一个 Prometheus-metrics-in-TiKV 项目:TiPrometheus。TiKV 的生态影响力正在持续扩大。 2018 年 12 月, TiKV 发布了 2.1 GA 版本。目前,TiKV 汇集了来自三星、摩拜、知乎、饿了么、腾讯云、一点资讯,以及 UCloud 的贡献。并已被银行、金融科技、保险、拼车、游戏等多个行业的领先企业应用在实际生产环境中,比如小米、北京银行、知乎、Shopee、BookMyShow 等。 ...

May 22, 2019 · 1 min · jiezi

阿里开源分布式事务组件-seata-seata-server-通信层解析

RPC ?seata client 和 seata server 间是需要通过网络通信来传递信息的,client 发送请求消息给 server,server 根据实际的处理逻辑,可能会给 client 发送相应的响应消息,或者不响应任何消息。在 seata 中,客户端和服务端的通信实现,被抽象成来公共的模块,它的 package 位于 io.seata.core.rpc 中。 这个包名叫 rpc,这个包下的很多类名也有 rpc 相关的字眼,而实际上在我看来,这个通信框架并不是一个常规意义的 rpc 框架,如果硬要揪书本知识,那么 rpc 的解释如下: 远程过程调用(英语:Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用。在以 dubbo 为代表的微服务时代下,dubbo 常规意义上我们都称之为 rpc 框架,rpc 的理论原则是:程序员无需额外地为这个交互作用编程。那么对于像 dubbo 这样的 rpc 实现,它能让 client 像调用本地代码 api 一样,来调用远程 server 上的某个 method。在 client 这一层直接面向 interface 编程,通过动态代理的方式,对上层屏蔽掉通信细节,在底层,将方法调用,通过序列化方式,封装成一个二进制数据串发送给 server,server 层解析该消息,通过反射的方式,将 interface 对应的 implemention 执行起来,将执行结果,扁平化成一个二进制数据串,回送给 client,client 收到数据后,拼装成 interface api 所定义的返回值类型的一个实例,作为方法调用的返回值。整个底层的细节,应用层面并不需要了解,应用层只需要以 interface.method 的方式,就像代码在本地执行一样,就能把远端 interface_implemention.method 给调用起来。 ...

May 21, 2019 · 9 min · jiezi

基于2PC和延迟更新完成分布式消息队列多条事务Golang版本

背景分布式多消息事务问题在消息队列使用场景中,有时需要同时下发多条消息,但现在的消息队列比如kafka只支持单条消息的事务保证,不能保证多条消息,今天说的这个方案就时kafka内部的一个子项目中基于2PC和延迟更新来实现分布式事务 2PC2PC俗称两阶段提交,通过将一个操作分为两个阶段:准备阶段和提交阶段来尽可能保证操作的原子执行(实际上不可能,大家有个概念先) 延迟更新延迟更新其实是一个很常用的技术手段,简单来说,当某个操作条件不满足时,通过一定手段将数据暂存,等条件满足时在进行执行 基于2PC和延迟队列的分布式事务实现系统架构实现也蛮简单的, 在原来的业务消息之后再添加一条事务消息(事务消息可以通过类似唯一ID来关联到之前提交的消息), worker未消费到事物提交的消息,就会一直将消息放在本地延迟存储中,只有当接收到事物提交消息,才会进行业务逻辑处理 业务流程生产者逐条发送业务消息组发送事务提交消息消费者消费消息队列,将业务消息存放本地延迟存储接收提交事务消息,从本地延迟存储获取所有数据,然后从延迟存储中删除该消息代码实现核心组件MemoryQuue: 用于模拟消息队列,接收事件分发事件Worker: 模拟具体业务服务,接收消息,存入本地延迟更新存储,或者提交事务触发业务回调 Event与EventListenerEvent: 用于标识事件,用户将业务数据封装成事件存入到MemoryQueue中EventListener: 事件回调接口,用于MemoryQueue接收到数据后的回调事件在发送的时候,需要通过一个前缀来进行事件类型标识,这里有三种TaskPrefix、CommitTaskPrefix、ClearTaskPrefix const ( // TaskPrefix 任务key前缀 TaskPrefix string = "task-" // CommitTaskPrefix 提交任务key前缀 CommitTaskPrefix string = "commit-" // ClearTaskPrefix 清除任务 ClearTaskPrefix string = "clear-")// Event 事件类型type Event struct { Key string Name string Value interface{}}// EventListener 用于接收消息回调type EventListener interface { onEvent(event *Event)}MemoryQueueMemoryQueue内存消息队列,通过Push接口接收用户数据,通过AddListener来注册EventListener, 同时内部通过poll来从chan event取出数据分发给所有的Listener // MemoryQueue 内存消息队列type MemoryQueue struct { done chan struct{} queue chan Event listeners []EventListener wg sync.WaitGroup}// Push 添加数据func (mq *MemoryQueue) Push(eventType, name string, value interface{}) { mq.queue <- Event{Key: eventType + name, Name: name, Value: value} mq.wg.Add(1)}// AddListener 添加监听器func (mq *MemoryQueue) AddListener(listener EventListener) bool { for _, item := range mq.listeners { if item == listener { return false } } mq.listeners = append(mq.listeners, listener) return true}// Notify 分发消息func (mq *MemoryQueue) Notify(event *Event) { defer mq.wg.Done() for _, listener := range mq.listeners { listener.onEvent(event) }}func (mq *MemoryQueue) poll() { for { select { case <-mq.done: break case event := <-mq.queue: mq.Notify(&event) } }}// Start 启动内存队列func (mq *MemoryQueue) Start() { go mq.poll()}// Stop 停止内存队列func (mq *MemoryQueue) Stop() { mq.wg.Wait() close(mq.done)}WorkerWorker接收MemoryQueue里面的数据,然后在本地根据不同类型来进行对应事件事件类型处理, 主要是通过事件的前缀来进行对应事件回调函数的选择 ...

May 18, 2019 · 3 min · jiezi

LCN502-lcn模式源码分析二

前言上一篇文章(https://segmentfault.com/a/11...)我们在springboot2.1.3上集成了lcn5.0.2并简单做了一个lcn模式的demo。LCN官网将源码都给了出来,但是分析源码的部分目前还不是很多,这篇文章主要分析一下LCN模式源码 事务控制原理分析源码之前,我们首先看一下LCN整体的框架模型: TX-LCN由两大模块组成, TxClient、TxManager。TxClient作为模块的依赖框架,提供TX-LCN的标准支持,TxManager作为分布式事务的控制放。事务发起方或者参与反都由TxClient端来控制。 原理图: lcn模式不难发现,开启处理的地方在拦截器(com.codingapi.txlcn.tc.aspect.TransactionAspect)里面 @Around("lcnTransactionPointcut() && !txcTransactionPointcut()" + "&& !tccTransactionPointcut() && !txTransactionPointcut()") public Object runWithLcnTransaction(ProceedingJoinPoint point) throws Throwable { //将执行分布式事务的方法放在DTXInfo对象里面 DTXInfo dtxInfo = DTXInfo.getFromCache(point); LcnTransaction lcnTransaction = dtxInfo.getBusinessMethod().getAnnotation(LcnTransaction.class); dtxInfo.setTransactionType(Transactions.LCN); dtxInfo.setTransactionPropagation(lcnTransaction.propagation()); //调用方法,正式开启(或继续,这里取决于是否是事务发起方)分布式事务 return dtxLogicWeaver.runTransaction(dtxInfo, point::proceed); }走进runTransaction方法,我们可以看到一下内容(伪代码,方便分析) public class DTXLogicWeaver { //执行分布式事务的核心方法 public Object runTransaction(){ //1.拿到当前模块的事务上下文和全局事务上下文 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();//下层创建了事务组 } //2.设置本地事务上下文的一些参数 if (Objects.nonNull(dtxLocalContext.getGroupId())) { dtxLocalContext.setDestroy(false); } dtxLocalContext.setUnitId(dtxInfo.getUnitId()); dtxLocalContext.setGroupId(txContext.getGroupId());//从全局上下文获取 dtxLocalContext.setTransactionType(dtxInfo.getTransactionType()); //3.设置分布式事务参数 TxTransactionInfo info = new TxTransactionInfo(); info.setBusinessCallback(business);//业务执行器(核心) info.setGroupId(txContext.getGroupId());//从全局上下文获取 info.setUnitId(dtxInfo.getUnitId()); info.setPointMethod(dtxInfo.getBusinessMethod()); info.setPropagation(dtxInfo.getTransactionPropagation()); info.setTransactionInfo(dtxInfo.getTransactionInfo()); info.setTransactionType(dtxInfo.getTransactionType()); info.setTransactionStart(txContext.isDtxStart()); //4.LCN事务处理器 try { return transactionServiceExecutor.transactionRunning(info); } finally { // 线程执行业务完毕清理本地数据 if (dtxLocalContext.isDestroy()) { // 通知事务执行完毕 synchronized (txContext.getLock()) { txContext.getLock().notifyAll(); } // TxContext生命周期是? 和事务组一样(不与具体模块相关的) if (!dtxLocalContext.isInGroup()) { globalContext.destroyTx(); } DTXLocalContext.makeNeverAppeared(); TracingContext.tracing().destroy(); } log.debug("<---- TxLcn end ---->"); } } }执行业务操作 ...

April 28, 2019 · 2 min · jiezi

springCloud集成分布式事务LCN-502-一

前言之前我写过一个基于springboot1.5.6+lcn4.1.0 的集成文章https://segmentfault.com/a/11...,基于目前更新的lcn5.0.2分析了一波源码。在此做一些记录(ps:如果对分布式事务不是很了解,可以先看下我上面贴的链接,本文基于有基础的情况去分析的) TX-LCN的3种模式LCN5.0.2有3种模式,分别是LCN模式,TCC模式,TXC模式 LCN模式: LCN模式是通过代理Connection的方式实现对本地事务的操作,然后在由TxManager统一协调控制事务。当本地事务提交回滚或者关闭连接时将会执行假操作,该代理的连接将由LCN连接池管理。 该模式的特点: - 该模式对代码的嵌入性为低。- 该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。- 该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。- 该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。TCC模式: TCC事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。主要由三步操作,Try: 尝试执行业务、 Confirm:确认执行业务、 Cancel: 取消执行业务。 该模式的特点: - 该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。- 该模式对有无本地事务控制都可以支持使用面广。- 数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。TXC模式: TXC模式命名来源于淘宝,实现原理是在执行SQL之前,先查询SQL的影响数据,然后保存执行的SQL快走信息和创建锁。当需要回滚的时候就采用这些记录数据回滚数据库,目前锁实现依赖redis分布式锁控制。 该模式的特点: - 该模式同样对代码的嵌入性低。- 该模式仅限于对支持SQL方式的模块支持。- 该模式由于每次执行SQL之前需要先查询影响数据,因此相比LCN模式消耗资源与时间要多。- 该模式不会占用数据库的连接资源。LCN原理这里引用一下官网的原理图: 总的来说,核心步骤就是创建事务组,假如事务组,通知事务组 创建事务组 是指在事务发起方开始执行业务代码之前先调用TxManager创建事务组对象,然后拿到事务标示GroupId的过程。加入事务组 添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息通知给TxManager的操作通知事务组 是指在发起方执行完业务代码以后,将发起方执行结果状态通知给TxManager,TxManager将根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方。集成springCloud (ps:我这里使用的springboot版本是2.1.3)<!----pom.xml 今天是2019.4.24 最新版本是5.0.2 ---><properties> <codingapi.txlcn.version>5.0.2.RELEASE</codingapi.txlcn.version></properties><dependency> <groupId>com.codingapi.txlcn</groupId> <artifactId>txlcn-tc</artifactId> <version>${codingapi.txlcn.version}</version> </dependency> <dependency> <groupId>com.codingapi.txlcn</groupId> <artifactId>txlcn-tm</artifactId> <version>${codingapi.txlcn.version}</version> </dependency> <dependency> <groupId>com.codingapi.txlcn</groupId> <artifactId>txlcn-txmsg-netty</artifactId> <version>${codingapi.txlcn.version}</version> </dependency>A启动类上加注解:@EnableDistributedTransaction 表明这是一个txmanager的client @SpringBootApplication@EnableEurekaClient@EnableFeignClients@EnableDistributedTransaction@MapperScan("cn.iamcrawler.crawlergoddess.mapper")@ComponentScan("cn.iamcrawler.crawlergoddess,cn.iamcrawler.crawler_common.feign")public class CrawlerGoddessApplication { public static void main(String[] args) { SpringApplication.run(CrawlerGoddessApplication.class, args); }}yml(ps 我把密码隐藏了,数据库我用的是postgresql,lcn默认的是mysql): ...

April 24, 2019 · 1 min · jiezi

浅析阿里分布式事务组件 fescar 2pc 的设计思想

二阶段提交协议的由来X/Open 组织提出了分布式事务处理的规范 DTP 模型(Distributed Transaction Processing),该模型中主要定义了三个基本组件,分别是应用程序(Application Program ,简称AP):用于定义事务边界(即定义事务的开始和结束),并且在事务边界内对资源进行操作。资源管理器(Resource Manager,简称RM):如数据库、文件系统等,并提供访问资源的方式。事务管理器(Transaction Manager ,简称TM):负责分配事务唯一标识,监控事务的执行进度,并负责事务的提交、回滚等。一般,我们称 TM 为事务的协调者,而称 RM 为事务的参与者。TM 与 RM 之间的通信接口,则由 XA 规范来约定。在 DTP 模型的基础上,才引出了二阶段提交协议来处理分布式事务。二阶段提交基本算法前提二阶段提交协议能够正确运转,需要具备以下前提条件:存在一个协调者,与多个参与者,且协调者与参与者之间可以进行网络通信参与者节点采用预写式日志,日志保存在可靠的存储设备上,即使参与者损坏,不会导致日志数据的消失参与者节点不会永久性损坏,即使后仍然可以恢复实际上,条件2和3所要求的,现今绝大多数关系型数据库都能满足。基本算法第一阶段协调者节点向所有参与者节点询问是否可以执行提交操作,并开始等待各参与者节点的响应。参与者节点执行询问发起为止的所有事务操作,并将Undo信息和Redo信息写入日志。各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个"同意"消息;如果参与者节点的事务操作实际执行失败,则它返回一个"中止"消息。第二阶段当协调者节点从所有参与者节点获得的相应消息都为"同意"时:协调者节点向所有参与者节点发出"正式提交"的请求。参与者节点正式完成操作,并释放在整个事务期间内占用的资源。参与者节点向协调者节点发送"完成"消息。协调者节点收到所有参与者节点反馈的"完成"消息后,完成事务。如下图所示:如果任一参与者节点在第一阶段返回的响应消息为"终止",或者 协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时:协调者节点向所有参与者节点发出"回滚操作"的请求。参与者节点利用之前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源。参与者节点向协调者节点发送"回滚完成"消息。协调者节点收到所有参与者节点反馈的"回滚完成"消息后,取消事务。如下图所示:缺陷分析二阶段提交协议除了协议本身具有的局限性之外,如果我们把以下情况也考虑在内:协调者宕机参与者宕机网络闪断(脑裂)那么二阶段提交协议实际上是存在很多问题的协议本身的缺陷协议本身的缺陷是指,在协议正常运行的情况下,无论全局事务最终是被提交还是被回滚,依然存在的问题,而暂不考虑参与者或者协调者宕机,或者脑裂的情况。性能问题参与者的本地事务开启后,直到它接收到协调者的 commit 或 rollback 命令后,它才会提交或回滚本地事务,并且释放由于事务的存在而锁定的资源。不幸的是,一个参与者收到协调者的 commit 或者 rollback 的前提是:协调者收到了所有参与者在一阶段的回复。如果说,协调者一阶段询问多个参与者采用的是顺序询问的方式,那么一个参与者最快也要等到协调者询问完所有其它的参与者后才会被通知提交或回滚,在协调者未询问完成之前,这个参与者将保持占用相关的事务资源。即使,协调者一阶段询问多个参与者采用的是并发询问的方式,那么一个参与者等待收到协调者的提交或者回滚通知的时间,将取决于在一阶段过程中,响应协调者最慢的那个参与者的响应时间。无论是哪一种情况,参与者都将在整个一阶段持续的时间里,占用住相关的资源,参与者事务的处理时间增加。若此时在参与者身上有其它事务正在进行,那么其它事务有可能因为与这个延迟的事务有冲突,而被阻塞,这些被阻塞的事务,进而会引起其它事务的阻塞。总而言之,整体事务的平均耗时增加了,整体事务的吞吐量也降低了。这会使得整个应用系统的延迟变高,吞吐量降低,可扩展性降低(当参与者变多的时候,延迟可能更严重)。总的来说,二阶段提交协议,不是一个高效的协议,会带来性能上的损失。全局事务隔离性的问题全局事务的隔离性与单机事务的隔离性是不同的。当我们在单机事务中提到不允许脏读时,那么意味着在事务未提交之前,它对数据造成的影响不应该对其它事务可见。当我们在全局事务中提到不允许脏读时,意味着,在全局事务未提交之前,它对数据造成的影响不应该对其它事务可见。在二阶段提交协议中,当在第二阶段所有的参与者都成功执行 commit 或者 rollback 之后,全局事务才算结束。但第二阶段存在这样的中间状态:即部分参与者已执行 commit 或者 rollback,而其它参与者还未执行 commit 或者 rollback。此刻,已经执行 commit 或者 rollback 的参与者,它对它本地数据的影响,对其它全局事务是可见的,即存在脏读的风险。对于这种情况,二阶段协议并没有任何机制来保证全局事务的隔离性,无法做到“读已提交”这样的隔离级别。协调者宕机如果在第一阶段,协调者发生了宕机,那么因为所有参与者无法再接收到协调者第二阶段的 commit 或者 rollback 命令,所以他们会阻塞下去,本地事务无法结束,如果协调者在第二阶段发生了宕机,那么可能存在部分参与者接收到了 commit/rollback 命令,而部分没有,因此这部分没有接收到命令的参与者也会一直阻塞下去。协调者宕机属于单点问题,可以通过另选一个协调者的方式来解决,但这只能保证后续的全局事务正常运行。而因为之前协调者宕机而造成的参与者阻塞则无法避免。如果这个新选择的协调者也宕机了,那么一样会带来阻塞的问题。参与者宕机如果在第一阶段,某个参与者发生了宕机,那么会导致协调者一直等待这个参与者的响应,进而导致其它参与者也进入阻塞状态,全局事务无法结束。如果在第二阶段,协调者发起 commit 操作时,某个参与者发生了宕机,那么全局事务已经执行了 commit 的参与者的数据已经落盘,而宕机的参与者可能还没落盘,当参与者恢复过来的时候,就会产生全局数据不一致的问题。网络问题-脑裂当网络闪断发生在第一阶段时,可能会有部分参与者进入阻塞状态,全局事务无法结束。当发生在第二阶段时,可能发生部分参与者执行了 commit 而部分参与者未执行 commit,从而导致全局数据不一致的问题。三阶段提交在二阶段提交中,当协调者宕机的时候,无论是在第一阶段还是在第二阶段发生宕机,参与者都会因为等待协调者的命令而进入阻塞状态,从而导致全局事务无法继续进行。因此,如果在参与者中引入超时机制,即,当指定时间过去之后,参与者自行提交或者回滚。但是,参与者应该进行提交还是回滚呢?悲观的做法是,统一都回滚。但事情往往没那么简单。当第一阶段,协调者宕机时,那么所有被阻塞的参与者选择超时后回滚事务是最明智的做法,因为还未进入第二阶段,所以参与者都不会接收到提交或者回滚的请求,当前这个事务是无法继续进行提交的,因为参与者不知道其它参与者的执行情况,所以统一回滚,结束分布式事务。在二阶段提交协议中的第二阶段,当协调者宕机后,由于参与者无法知道协调者在宕机前给其他参与者发了什么命令,进入了第二阶段,全局事务要么提交要么回滚,参与者如果引入超时机制,那么它应该在超时之后提交还是回滚呢,似乎怎么样都不是正确的做法。执行回滚,太保守,执行提交,太激进。如果在二阶段提交协议中,在第一阶段和第二阶段中间再引入一个阶段,如果全局事务度过了中间这个阶段,那么在第三阶段,参与者就可以认为此刻进行提交的成功率会更大。但这难道不是治标不治本吗,当进入第三阶段,全局事务需要进行回滚时候,如果协调者宕机,那么参与者超时之后自行进行提交事务,就会造成全局事务的数据不一致。再考虑参与者宕机的情况下,协调者应该在超时之后,对全局事务进行回滚。总结起来,三阶段提交主要在二阶段提交的基础上,为了解决参与者和协调者宕机的问题,而引入了超时机制,并因为超时机制,附带引入中间这一层。并且,三阶段提交并没有解决二阶段提交的存在的脑裂的问题。总而言之,二阶段和三阶段提交都无法完美地解决分布式事务的问题。关于三阶段提交更详细的算法和步骤,可以参考我的另外一篇文章《分布式事务概览》fescar二阶段提交fescar 是阿里最近开源的一个关于分布式事务的处理组件,它的商业版是阿里云上的 GTS。在其官方wiki上,我们可以看到,它对XA 二阶段提交思考与改进。在我们上面提到的参与者中,这个参与者往往是数据库本身,在 DTP 模型中,往往称之为 RM,即资源管理器。fescar 的二阶段提交模型,也是在 DTP 模型的基础上构建。RM逻辑不与数据库绑定fescar 2PC 与 XA 2PC 的第一个不同是,fescar 把 RM 这一层的逻辑放在了 SDK 层面,而传统的 XA 2PC,RM的逻辑其实就在数据库本身。fescar 这样做的好处是,把提交与回滚的逻辑放在了 SDK 层,从而不必要求底层的数据库必须对 XA 协议进行支持。对于业务来说,业务层也不需要为本地事务和分布式事务两类不同场景来适配两套不同的数据库驱动。基于我们先前对 XA 2PC 讨论,XA 2PC 存在参与者宕机的情况,而 fescar 的 2PC 模型中,参与者实际上是 SDK。参与者宕机这个问题之所以在 XA 2PC 中是个大问题,主要是因为 XA 中,分支事务是有状态的,即它是跟会话绑定在一起的,无法跨连接跨会话对一个分支事务进行操作,因此在 XA 2PC 中参与者一旦宕机,分支事务就已经无法再进行恢复。fescar 2PC 中,参与者实际上是SDK,而SDK是可以做高可用设计的。并且,在其第一阶段,分支事务实际上已经是被提交了的,后续的全局上的提交和回滚,实际上是操作数据的镜像,全局事务的提交会异步清理 undo_log,回滚则会利用保存好的数据镜像,进行恢复。fescar 的 2PC 中,实际上是利用了 TCC 规范的无状态的理念。因为全局事务的执行、提交和回滚这几个操作间不依赖于公共状态,比如说数据库连接。所以参与者实际上是可以成为无状态的集群的。也就是说,在 fescar 2PC 中,协调者如果发现参与者宕机或者超时,那么它可以委托其他的参与者去做。第二阶段非阻塞化fescar 2PC 的第一阶段中,利用 SDK 中的 JDBC 数据源代理通过对业务 SQL 的解析,把业务数据在更新前后的数据镜像组织成回滚日志,利用本地事务 的 ACID 特性,将业务数据的更新和回滚日志的写入在同一个 本地事务 中提交。这就意味着在阻塞在第一阶段过后就会结束,减少了对数据库数据和资源的锁定时间,明显效率会变更高。根据 fescar 官方的说法,一个正常运行的业务,大概率是 90% 以上的事务最终应该是成功提交的,因此可以在第一阶段就将本地事务提交呢,这样 90% 以上的情况下,可以省去第二阶段持锁的时间,整体提高效率。fescar 的这个设计,直接优化了 XA 2PC 协议本身的性能缺陷。协调者的高可用XA 2PC 中存在协调者宕机的情况,而 fescar 的整体组织上,是分为 server 层和 SDK 层的,server 层作为事务的协调者,fescar 的话术中称协调者为 TC(Transaction Coordinator ),称 SDK 为 TM(Transaction Manager)。截止至这篇文章发表前,fescar 的server层高可用还未实现,依据其官方的蓝图,它可能会采用集群内自行协商的方案,也可能直接借鉴高可用KV系统。自行实现集群内高可用方案,可能需要引进一套分布式一致性协议,例如raft,我认为这是最理想的方式。而直接利用高可用KV系统,例如 redis cluster,则会显得系统太臃肿,但实现成本低。事务的隔离性XA 2PC 是没有机制去支持全局事务隔离级别的,fescar 是提供全局事务的隔离性的,它把全局锁保存在了 server 层。全局事务隔离级别如果太高,性能会有很大的损耗。目前的隔离界别默认是读未提交,如果需要读已提交或者更高的级别,就会涉及到全局锁,则意味着事务的并发性会受影响。应用层业务层应该选择合适的事务隔离级别。脑裂的问题仍然没有完美解决无论是 XA 还是 fescar,都未解决上述提到的脑裂的问题。脑裂的问题主要影响了全局事务的最后的提交和回滚阶段。没有完美的分布式事务解决方案,即使是 fescar 或者 GTS,它们也必然需要人工介入。但脑裂问题是小概率事件,并且不是致命性错误,可以先通过重试的方法来解决,如果不行,可以收集必要的事务信息,由运维介入,以自动或者非自动的方式,恢复全局事务。参考资料 维基百科:二阶段提交《2PC之踵?是时候升级二阶段提交协议了》 by Tim Yang《分布式事务概览》by beanlam fescar wiki ...

March 11, 2019 · 1 min · jiezi

聊聊分布式事务

这次使用分布式事务框架过程中了学习了一些分布式事务知识,所以本文我们就来聊聊分布式事务那些事。首先我们先回顾下什么是事务。事务什么是事务?这个作为后端开发,日常开发中只要与数据库有交互,肯定就会使用过事务。现在摘抄一段wiki的解释,解释下什么是事务。是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成数据库系统具有事务特性,这是其有别与文件系统重要特性。传统的文件系统,如果正在写文件,操作系统突然崩溃,此时文件可能被破坏。数据库系统引入事务特性,可以保证数据库从一种状态转换为另一种状态。在提交工作时,可以确保要么所有修改都被保存,要么所有都不保存。通常一个事务会有多个读写操作构成。事务具有四个基本特性,俗称ACID。A(Atomicity):原子性。事务会被当做一个整体,要么所有语句都成功,要么都失败,不能存在部分语句成功,部分失败的情况。C(Consistenc):一致性。数据库的状态从一种状态转变为另外一种状态,事务开始之前和是事务结束之后,数据库完整性约束不变。什么叫数据库完整性约束不变?举个例子,若一个表姓名字段为唯一约束,若在事务提交或回滚后,姓名字段变成非唯一了,这就破坏数据库的完整性约束。I(Isolation):隔离性。多个并发事务执行,互不影响。D(Durability):持久性。事务提交之后,其对数据库相关修改能永久保存在数据库。所以该特性需要数据库系统可以在崩溃时需要恢复时也能提交的数据都不丢失。因此早期我们的系统只在存在一个数据源情况下,这个时候可以依靠数据库系统事务来保证业务的正确性。但是随着业务的不断扩展,我们业务的一个单表可能就存在千万数据,在使用再使用一个数据库实例,就会可能存在性相关能问题。这个时候我们就会考虑分库分表。但是这样就有可能导致,单个应用连接多个数据源的情况。如下图示例。上图一次购买过程,商家余额表与用户余额表处于两个单独的数据库实例中,这样单独的事务能保证扣减商家余额或用户余额要么扣减成功,要么扣减失败。但是我们却无法保证两个事务同时成功或同时失败。还有一种情况,随着系统越来越庞大,我们会选择将系统应用拆分多个微服务,让单个应用只操作一个数据源。这个时候我们就会碰到,一次业务调用,将会调用多个应用,每个应用单独操作数据源的情况,如下图。这种情况下我们更加不能保证所有调用都成功。由上面的例子下我们可以看出,随着业务发展,传统的单机事务已经无法满足我们的业务的需求,这个时候我们就需要分布式事务来保证。分布式事务摘抄一段 wiki 上解释。A distributed transaction is a database transaction in which two or more network hosts are involved.我们先来讲下实现分布式事务一些理论基础。分布式事务技术理论CAP 定理。在一个分布式系统(指互相连接并共享数据的节点的集合)中,当涉及读写操作时,只能保证一致性(Consistence)、可用性(Availability)、分区容错性(Partition Tolerance)三者中的两个,另外一个必须被牺牲。摘录极客时间从0开始学架构第22章解释虽然 CAP 理论定义是三个要素中只能取两个,但放到分布式环境下来思考,我们会发现必须选择 P(分区容忍)要素,因为网络本身无法做到 100% 可靠,有可能出故障,所以分区是一个必然的现象。如果我们选择了 CA 而放弃了 P,那么当发生分区现象时,为了保证 C,系统需要禁止写入,当有写入请求时,系统返回 error(例如,当前系统不允许写入),这又和 A 冲突了,因为 A 要求返回 no error 和 no timeout。因此,分布式系统理论上不可能选择 CA 架构,只能选择 CP 或者 AP 架构BASE 理论,分别是以下三个单词的缩写。Basically Available(基本可用):分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。Soft state(软状态):允许系统中存在中间状态,这个状态不影响系统可用性,这里指的是CAP中的不一致。Eventually consistent(最终一致性):最终一致是指经过一段时间后,所有节点数据都将会达到一致。BASE 是对CAP 中 AP 方案的一种补充。在 BASE 中用软状态和最终一致,保证了延迟后的一致性。BASE 和 ACID 是相反的,ACID 是一种强一致性模型,而 BASE 却是牺牲这种强一致性,允许数据短时间内不一致,最终一致性。接下来我们看看分布式事务有哪几种实现方案。分布式事务实现方案基于数据库资源层面2PC 两阶段提交协议3PC 三阶段提交协议基于业务层面TCC基于数据库资源层面实现方案,由于存在多个事务,我们需要存在一个角色管理各个事务的状态。我们将这个角色称为协调者,事务参与者称为参与者。参与者与协调者一般会基于某种特定协议,目前比较有名的为 XA 接口协议。基于协调者与参与者的思想设定,分别提出了 2PC 与 3PC 实现XA 分布式事务。2PC 两阶段提交协议如名字所知,这个过程主要分为两步。第一阶段,协调者(事务管理器)将涉及到事务的进行预提交,这个时候数据库资源开始被锁定。参与者将 undo 与 redo 写入事务日志。第二阶段,参与者(资源管理器)行提交事务,或者利用 undo 日志回滚事务,释放资源。整个过程如下图。分布式事务提交成功场景:分布式事务回滚场景:该方案的优点为:实现比较简单,主流数据库都支持,强一致性。MySQL 5.5 以后基于 XA 协议实现.相应该方案也存在缺点:协调者的单点问题。若协调者在提交阶段宕机,参与者一直在等待,就一直锁定资源,一直阻塞。虽然可以重新选举协调者,但是无法解决该问题。同步阻塞时间过长,整个执行过程事务是阻塞的,直到提交完成,释放资源,若在提交过程/回滚过程,因为网络延时,参与者一直未收到指令,则参与者一直被阻塞。数据不一致。第二阶段,协调者发出第一个提交信号后后宕机,则第一个参与者提交事务,第二个参与者因为未收到协调者信号,无法进行事务提交。于是针对 2PC 存在的缺点,提出改进方案,3PC。3PC 三阶段提交协议三阶段提交,在两阶段提交的基础下,改进两阶段。三阶段步骤如下。CanCommit,协调者询问参与者是否可以进行事务提交。PreCommit ,若所有参与者可以进行事务提交,协调者下达 PreCommit 命令,参与者锁定资源,并等待最终命令。所有参与者返回确认信息,协调者向各个事务下发事务执行通知,锁定资源,并将执行情况返回。部分参与者返回否认信息或协调者等待超时。这种情况,协调者认为事务无法正常执行,下发中断指令,各个参与者退出预备状态Do Commit,若第二阶段全部回应 ack,则下达 Do Commit ,进行事务最终提交,否则下达中断事务命令,所有参与者进行事务回滚。所有参与者正常执行执行事务,协调者下发最终提交指令,释放锁定资源。部分参与者执行事务失败,协调者等待超时,协调者下发回滚指令,释放锁定资源。具体见下图。三阶段提交对比两阶段,引入超时机制减少事务阻塞,解决单点故障。在第三阶段,一旦参与者无法接受到协调者信号时,等待超时之后,参与者默认执行 commit,释放资源。三阶段任然不能解决数据一致性问题。若协调者发出回滚命令,但是由于网络问题,参与者在等待时间内都无法接收到,这时参与者默认提交事务,而其他事务进行了回滚,造成事务不一致。TCCTCC 事务为了解决在事务运行过程中大颗粒度资源锁定的问题,业界提出一种新的事务模型,它是基于业务层面的事务定义。锁粒度完全由业务自己控制。它本质是一种补偿的思路。它把事务运行过程分成 Try、Confirm / Cancel 两个阶段。在每个阶段的逻辑由业务代码控制。这样就事务的锁粒度可以完全自由控制。业务可以在牺牲隔离性的情况下,获取更高的性能。TCC 分别为 Trying,Confirm,Cancel 三个单词缩写。不同于 2PC 与 3PC 基于数据库层面,TCC 基于应用层面。TCC 三个动作分别为:Trying:完成所有业务检查(一致性)预留必须业务资源(准隔离性)Confirm:真正执行业务Confirm操作要满足幂等性Cancel:释放Try阶段预留的业务资源Cancel操作要满足幂等性上面说法,一听起来有点生涩难懂,没关系我们使用实际案例解释。下面我们模拟商城一次支付过程。用户下单使用组合支付,即余额加红包支付。一次正常流程为:创建订单下单调用余额系统,扣减余额调用红包系统,扣减红包余额修改订单状态为已支付完后支付。实际过程如下图。但是这么一个支付过程调用多个子服务,我们不能保证所有服务都能成功,比如我们在调用红包系统扣减红包系统失败。这个时候我们就碰到尴尬的场景,由于红包服务失败,导致方法异常退出,这个时候订单状态为初始状态,但是用户余额已经扣减。这对用户体验非常不友好。所以这次支付过程,我们必须存在机制将这次过程当成一次整体的行为,必须保证这其中服务调用,要么都成功,要么都失败,成为一个整体的事务。这时我们可以引入 TCC 事务,将整个下单过程作为一个整体。引入后,由于余额系统扣减是失败,这个时候我们回滚订单系统与红包系统。整个过程如下图。由于余额系统的失败,我们需要撤销这次过程中所有更改,所以我们向订单系统发送撤销通知,向红包系统发出撤销通知。因此系统引入 TCC 事务后,我们需要改造我们的调用过程。系统如何引入 TCC 事务根据 TCC 事务三步,这个时候我们必须将各个服务改造成 Try Confirm Cancle 三步、TCC TRY:根据上面的业务,订单系统增加 try 方法将订单状态修改成 PAYING。余额系统增加一个 try 方法,先检查用于余额是否充足,然后先将余额扣减,然后将扣减的余额增加到冻结金额。红包系统同余额系统。从改造过程可以看出,TCC try 方法需检查各业务资源,且这过程需要引入中间状态。我们根据下图来看整个过程。TCC Confirm:TCC 第一步 TRY 如果所有子服务调用都成功,这个时候我们就需要确认各服务。各个服务增加 confirm 方法。如余额系统 confirm 方法用来将冻结金额置为0,红包系统如上。订单系统将订单状态修改为 SUCCESS。confirm 方法需要注意实现幂等。如订单系统更新前,一定要先判断该笔订单状态处于 PAYING,才能更新订单。整个过程如下图。讲到这里,必须用到 TCC 事务框架推动各服务。TCC 事务管理器感知到 TRY 方法结束后,自动调用各服务提供的 confirm 方法,将各服务状态修改为终态。TCC Cancle:如若 TCC Try 过程中,冻结红包方法失败,这时我们就需要将之前修改都撤销,修改成其初始状态。cancle 方法也需要实现幂等如 confirm 方法 如下图:看到这,我们我们可以看出 TCC Try 成功,confirm 必定要成功,try 失败,cancle 必定要成功。因为 confirm 是系统更新为终态的关键。但是现实这么无情,生产系统 confirm 或 cancle 肯定会有几率失败,这个时候就需要 TCC 框架记录调用 confirm 结果。如果 confirm 调用失败,TCC 框架需要记录下来,然后间隔一定时间再次去调用。总结与思考看完全文,基本上对分布式事务又一定了解了吧。我们基于此对此总结下。使用分布式事务,我们需要结合我们实际场景应用。如果业务还处于开始阶段,我们其实可以选择数据库事务来保证快速上线迭代。等到业务一定阶段,系统开始拆分,数据库也拆分,这时如果业务需要保证一致性,这时必须使用分布式事务。这时候使用分布式事务,我们需要基于业务考虑使用哪种。使用 2PC 或 3PC 实现的分布式框架,业务应用层无需改动,接入较简单。但是相对应能较低,数据资源锁定较长。不太适合互联网等高并发业务场景。而使用基于 TCC 实现分布式框架,相对 2PC 性能较高,可以保证数据最终一致性。但是对于应用层来说,一个方法必须改造成三个方法,且业务中需引入一些中间状态,相对而言应用改造程度较大。参考资料分布式事务:两阶段提交与三阶段提交关于分布式事务、两阶段提交协议、三阶提交协议拜托,面试请不要再问我TCC分布式事务的实现原理!2PC和3PC一点理解如果觉得好的话,请帮作者点个赞呗~ 谢谢 ...

December 14, 2018 · 1 min · jiezi

TiDB 在小米的应用实践

作者:张良,小米 DBA 负责人;潘友飞,小米 DBA;王必文,小米开发工程师。一、应用场景介绍MIUI 是小米公司旗下基于 Android 系统深度优化、定制、开发的第三方手机操作系统,也是小米的第一个产品。MIUI 在 Android 系统基础上,针对中国用户进行了深度定制,在此之上孕育出了一系列的应用,比如主题商店、小米音乐、应用商店、小米阅读等。 <center>图 1 MIUI Android 系统界面图</center>目前 TiDB 主要应用在:小米手机桌面负一屏的快递业务商业广告交易平台素材抽审平台这两个业务场景每天读写量均达到上亿级,上线之后,整个服务稳定运行;接下来我们计划逐步上线更多的业务场景,小米阅读目前正在积极的针对订单系统做迁移测试。二、TiDB 特点TiDB 结合了传统的 RDBMS 和 NoSQL 的最佳特性,兼容 MySQL 协议,支持无限的水平扩展,具备强一致性和高可用性。具有如下的特性:高度兼容 MySQL,大多数情况下无需修改代码即可从 MySQL 轻松迁移至 TiDB,即使已经分库分表的 MySQL 集群亦可通过 TiDB 提供的迁移工具进行实时迁移。水平弹性扩展,通过简单地增加新节点即可实现 TiDB 的水平扩展,按需扩展吞吐或存储,轻松应对高并发、海量数据场景。分布式事务,TiDB 100% 支持标准的 ACID 事务。真正金融级高可用,相比于传统主从(M-S)复制方案,基于 Raft 的多数派选举协议可以提供金融级的 100% 数据强一致性保证,且在不丢失大多数副本的前提下,可以实现故障的自动恢复(auto-failover),无需人工介入。TiDB 的架构及原理在 官网 里有详细介绍,这里不再赘述。<center>图 2 TiDB 基础架构图</center>三、背景跟绝大数互联网公司一样,小米关系型存储数据库首选 MySQL,单机 2.6T 磁盘。由于小米手机销量的快速上升和 MIUI 负一屏用户量的快速增加,导致负一屏快递业务数据的数据量增长非常快,每天的读写量级均分别达到上亿级别,数据快速增长导致单机出现瓶颈,比如性能明显下降、可用存储空间不断降低、大表 DDL 无法执行等,不得不面临数据库扩展的问题。比如,我们有一个业务场景(智能终端),需要定时从几千万级的智能终端高频的向数据库写入各种监控及采集数据,MySQL 基于 Binlog 的单线程复制模式,很容易造成从库延迟,并且堆积越来越严重。对于 MySQL 来讲,最直接的方案就是采用分库分表的水平扩展方式,综合来看并不是最优的方案,比如对于业务来讲,对业务代码的侵入性较大;对于 DBA 来讲提升管理成本,后续需要不断的拆分扩容,即使有中间件也有一定的局限性。同样是上面的智能终端业务场景,从业务需求看,需要从多个业务维度进行查询,并且业务维度可能随时进行扩展,分表的方案基本不能满足业务的需求。了解到 TiDB 特点之后,DBA 与业务开发沟通确认当前 MySQL 的使用方式,并与 TiDB 的兼容性做了详细对比,经过业务压测之后,根据压测的结果,决定尝试将数据存储从 MySQL 迁移到 TiDB。经过几个月的线上考验,TiDB 的表现达到预期。四、兼容性对比TiDB 支持包括跨行事务、JOIN、子查询在内的绝大多数 MySQL 的语法,可以直接使用 MySQL 客户端连接;对于已用 MySQL 的业务来讲,基本可以无缝切换到 TiDB。二者简单对比如下几方面:功能支持TiDB 尚不支持如下几项:增加、删除主键非 UTF8 字符集视图(即将支持)、存储过程、触发器、部分内置函数Event全文索引、空间索引默认设置字符集、排序规则、sql_mode、lower_case_table_names 几项默认值不同。事务TiDB 使用乐观事务模型,提交后注意检查返回值。TiDB 限制单个事务大小,保持事务尽可能的小。TiDB 支持绝大多数的 Online DDL。另,一些 MySQL 语法在 TiDB 中可以解析通过,不会产生任何作用,例如: create table 语句中 engine、partition 选项都是在解析后忽略。详细信息可以访问官网:https://pingcap.com/docs-cn/sql/mysql-compatibility/ 。五、压测5.1 目的通过压测 TiDB 了解一下其 OLTP 性能,看是否满足业务要求。5.2 机器配置组件实例数量CPU 型号内存磁盘版本操作系统TiDB3Intel(R) Xeon(R) CPU E5-2620 v3 @ 2.40GHz128GSSD Raid 52.0.3CentOS Linux release 7.3.1611PD3Intel(R) Xeon(R) CPU E5-2620 v3 @ 2.40GHz128GSSD Raid 52.0.3CentOS Linux release 7.3.1611TiKV4Intel(R) Xeon(R) CPU E5-2620 v3 @ 2.40GHz128GSSD Raid 52.0.3CentOS Linux release 7.3.16115.3 压测内容以及结果5.3.1 标准 Select 压测ThreadsQPSLatency (avg / .95 / max)812650.810.63 / 0.90 / 15.621621956.210.73 / 1.50 / 15.713231534.81.01 / 2.61 / 25.1664382171.67 / 5.37 / 49.8012839943.053.20 / 8.43 / 58.6025640920.646.25 / 13.70 / 95.13<center>图 3 标准 Select 压测图</center>5.3.2 标准 OLTP 压测ThreadsTPSQPSLatency (avg / .95 / max)8428.98578.0918.65 / 21.89 / 116.0616731.6714633.3521.86 / 25.28 / 120.59321006.4320128.5931.79 / 38.25 / 334.92641155.4423108.955.38 / 71.83 / 367.531281121.5522431114.12 / 161.51 / 459.03256941.2618825.1271.94 / 369.77 / 572.88<center>图 4 标准 OLTP 压测图</center>5.3.3 标准 Insert 压测ThreadsQPSLatency (avg / .95 / max)83625.752.20 / 2.71 / 337.94166527.242.45 / 3.55 / 160.843210307.663.10 / 4.91 / 332.416413662.834.68 / 7.84 / 467.5612815100.448.47 / 16.41 / 278.2325617286.8614.81 / 25.74 / 3146.52<center>图 5 标准 Insert 压测图</center>通过压测发现 TiDB 稳定性上与预期稍有差别,不过压测的 Load 会明显高于生产中的业务 Load,参考低 Threads 时 TiDB 的表现,基本可以满足业务对 DB 的性能要求,决定灰度一部分 MySQL 从库读流量体验一下实际效果。六、迁移过程整个迁移分为 2 大块:数据迁移、流量迁移。6.1 数据迁移数据迁移分为增量数据、存量数据两部分。对于存量数据,可以使用逻辑备份、导入的方式,除了传统的逻辑导入外,官方还提供一款物理导入的工具 TiDB Lightning。对于增量备份可以使用 TiDB 提供的 Syncer (新版已经更名为 DM - Data Migration)来保证数据同步。Syncer 结构如图 6,主要依靠各种 Rule 来实现不同的过滤、合并效果,一个同步源对应一个 Syncer 进程,同步 Sharding 数据时则要多个 Syncer 进程。<center>图 6 Syncer 结构图</center>使用 Syncer 需要注意:做好同步前检查,包含 server-id、log_bin、binlog_format 是否为 ROW、binlog_row_image 是否为 FULL、同步相关用户权限、Binlog 信息等。使用严格数据检查模式,数据不合法则会停止。数据迁移之前最好针对数据、表结构做检查。做好监控,TiDB 提供现成的监控方案。对于已经分片的表同步到同一个 TiDB 集群,要做好预先检查。确认同步场景是否可以用 route-rules 表达,检查分表的唯一键、主键在数据合并后是否冲突等。6.2 流量迁移流量切换到 TiDB 分为两部分:读、写流量迁移。每次切换保证灰度过程,观察周期为 1~2 周,做好回滚措施。读流量切换到 TiDB,这个过程中回滚比较简单,灰度无问题,则全量切换。再将写入切换到 TiDB,需要考虑好数据回滚方案或者采用双写的方式(需要断掉 Syncer)。七、集群状况7.1 配置集群配置采用官方推荐的 7 节点配置,3 个 TiDB 节点,3 个 PD 节点,4 个 TiKV 节点,其中每个 TiDB 与 PD 为一组,共用一台物理机。后续随着业务增长或者新业务接入,再按需添加 TiKV 节点。7.2 监控监控采用了 TiDB 的提供的监控方案,并且也接入了公司开源的 Falcon,目前整个集群运行比较稳定,监控如图 7。 <center>图 7 监控图</center>八、遇到的问题、原因及解决办法问题原因及解决办法在一个 DDL 里不能对多个列或者多个索引做操作。ADD/DROP INDEX/COLUMN 操作目前不支持同时创建或删除多个索引或列,需要拆分单独执行,官方表示 3.0 版本有计划改进。部分操作符查询优化器支持不够好,比如 or 操作符会使用 TableScan,改写成 union all 可避免。官方表示目前使用 or 操作符确实在执行计划上有可能不准确,已经在改进计划中,后续 3.0 版本会有优化。重启一个 PD 节点的时候,业务能捕捉到 PD 不可用的异常,会报 PD server timeout 。因为重启的是 Leader 节点,所以重启之前需要手动切换 Leader,然后进行重启。官方建议这里可以通过重启前做 Leader 迁移来减缓,另外后续 TiDB 也会对网络通讯相关参数进行梳理和优化。建表语句执行速度相比 MySQL 较慢多台 TiDB 的时候,Owner 和接收 create table 语句的 TiDB Server 不在一台 Server 上时,可能比 MySQL 慢一些,每次操作耗时在 0.5s 左右,官方表示会在后续的版本中不断完善。pd-ctl 命令行参数解析严格,多一个空格会提示语法错误。官方表示低版本中可能会有这个问题,在 2.0.8 及以上版本已经改进。tikv-ctl 命令手动 compact region 失败。在低版本中通常是因为 tikv-ctl 与集群版本不一致导致的,需要更换版本一致的 tikv-ctl,官方表示在 2.1 中已经修复。大表建索引时对业务有影响官方建议在业务低峰期操作,在 2.1 版本中已经增加了操作优先级以及并发读的控制,情况有改善。存储空间放大问题该问题属于 RocksDB,RocksDB 的空间放大系数最理想的值为 1.111,官方建议在某些场景下通过 TiKV 开启 RocksDB 的 dynamic-level-bytes 以减少空间放大。九、后续和展望目前 TiDB 在小米主要提供 OLTP 服务,小米手机负一屏快递业务为使用 TiDB 做了一个良好的开端,而后商业广告也有接入,2 个业务均已上线数月,TiDB 的稳定性经受住了考验,带来了很棒的体验,对于后续大体的规划如下:MIUI 生态业务中存在大量的类似场景的业务,后续将会与业务开发积极沟通,从 MySQL 迁移到 TiDB。针对某些业务场景,以资源合理利用为目标,推出归档集群,利用 Syncer 实现数据归档的功能。数据分析,结合 TiDB 提供的工具,将支持离线、实时数据分析支持。将 TiDB 的监控融合到小米公司开源的监控系统 Falcon 中。十、致谢非常感谢 TiDB 官方在迁移及业务上线期间给予我们的支持,为每一个 TiDB 人专业的精神、及时负责的响应点赞。更多 TiDB 用户实践: https://www.pingcap.com/cases-cn/ ...

December 4, 2018 · 3 min · jiezi