当前位置: 首页 > news >正文

读懂分布式事务

一、概述

1.1 什么是分布式事务

事务我们都很熟悉,事务提供一种机制将一个活动涉及的所有操作纳入到一个不可分割的执行单元,组成这组操作的各个单元,要么全部成功,要么全部失败。

事务有四大特性:

  • Atomic(原子性)事务是一个不可分割的工作单元,事务中的操作要么都发生,要么都不发生;
  • Consistent(一致性)事务完成时,必须使所有数据都保持一致状态;
  • Isolation(隔离性)并发事务所做的修改必须和其他事务所做的修改是隔离的;
  • Duration(持久性)事务完成之后,对数据库中数据的改变是永久性的;
    在这里插入图片描述

为了解决传统单体服务架构带来的各种问题,分布式系统会把一个应用系统拆分成可独立部署的多个服务。如果把单体架构服务器比做篮子,那代码就是鸡蛋,不要让所有鸡蛋别装在一个篮子里。在分布式事务中,事务的参与者、支持事务的服务器、事务管理器分别位于不同的分布式系统的不同节点之上。我们都知道一次大的操作由不同的小操作组成,而在分布式系统中这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。

二、理论基础

2.1 CAP理论

CAP定理,又被叫作布鲁尔定理。对于设计分布式系统来说(不仅仅是分布式事务)的架构师来说,CAP就是你的入门理论。

  • C (一致性):指写操作后的读操作可以读取到最新的数据状态,当数据分布在多个节点上,从任意结点读取的到数据都是最新的状态。如果在某个节点更新了数据,在其他节点如果都能读取到这个最新的数据,那么就称为强一致,如果有某个节点没有读取到,那就是分布式不一致。
  • A (可用性):指任何事务操作都可以得到响应结果,且不会出现响应超时或响应错误。。可用性的两个关键一个是合理的时间,一个是合理的响应。合理的时间指的是请求不能无限被阻塞,应该在合理的时间给出返回。合理的响应指的是系统应该明确返回结果并且结果是正确的。
  • P (分区容错性):分布式系统的各个节点一般部署在不同的子网上,当有台机器的网络出现了问题,此时整体仍然能够对外提供服务,这叫分区容忍性。分区容忍性是分布式系统具备的基本能力。

CAP中三者不能共有,只能选择其中的两项:

  • AP:放弃一致性,追求分区容忍性和可用性。大部分分布式系统设计时的选择。
  • CP:放弃可用性,追求一致性和分区容错性。如Zookeeper。
  • CA:放弃分区容错性,不考虑网络因素或结点挂掉的问题。关系型数据库就满足了CA,但不是一个标准的分布式系统。
    在这里插入图片描述

顺便一提,CAP理论中是忽略网络延迟的,也就是当事务提交时,从节点A复制到节点B,但是在现实中这个是明显不可能的,所以总会有一定的时间是不一致。同时CAP中选择两个,比如你选择了CP,并不是叫你放弃A。因为P出现的概率实在是太小了,大部分的时间你仍然需要保证CA。就算分区出现了你也要为后来的A做准备,比如通过一些日志的手段,是其他机器回复至可用。

2.2 BASE理论

BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写。是对CAP中AP的一个扩展

  1. 基本可用:分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。如电商网站交易付款出现问题了,商品依然可以正常浏览。
  2. 软状态:允许系统中存在中间状态,这个状态不影响系统可用性,这个状态不影响系统可用性,如订单的"支付中"、“数据同步中”等状态,待数据最终一致后状态改为“成功”状态。
  3. 最终一致:最终一致是指经过一段时间后,所有节点数据都将会达到一致。如订单的"支付中"状态,最终会变 为“支付成功”或者"支付失败"。
    BASE和 ACID 是相反的,它完全不同于ACID的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态。

三、常见解决方案

3.1 2PC

2PC(Two-phase commit protocol),二阶段提交,它将整个事务流程分为两个阶段:准备阶段(prepare phase)、提交阶段(commit phase)。 二阶段提交是一种强一致性设计,2PC 引入一个事务协调者的角色来协调管理各参与者的提交和回滚,二阶段分别指的是准备和提交两个阶段。
在这里插入图片描述

  1. 准备阶段:协调者会给各参与者发送准备命令,但是不会提交事务,此时除提交以外所有都准备完了,就差临门一脚。
  2. 提交阶段:
    a、假如在第一阶段所有参与者都返回准备成功,那么协调者则向所有参与者发送提交事务命令,然后等待所有事务都提交成功之后,返回事务执行成功。
    b、假如在第一阶段有一个参与者返回失败,那么协调者就会向所有参与者发送回滚事务的请求,即分布式事务执行失败。

假如第二阶段提交失败的话,可以分为两种情况:

  • 第一种是第二阶段执行的是回滚事务操作,那么答案是不断重试,直到所有参与者都回滚了,不然那些在第一阶段准备成功的参与者会一直阻塞着。
  • 第二种是第二阶段执行的是提交事务操作,那么答案也是不断重试,因为有可能一些参与者的事务已经提交成功了,这个时候只有一条路,就是头铁往前冲,不断的重试,直到提交成功,到最后真的不行只能人工介入处理。

优点:原理简单,实现很方便。

缺点:

  • 同步阻塞:在阶段一里执行prepare操作会占用资源,一直到整个分布式事务完成,才会释放资源,这个过程中,如果有其他人要访问这个资源,就会被阻塞住。

  • 单点故障:协调者是个单点,如果协调者在commit出现故障,那么其它参与者一直处于锁定状态。

  • 事务状态丢失:即使把协调者做成一个双机热备的,一个协调者挂了自动选举其他的协调者出来,但如果协调者挂掉的同时,接收到commit消息的某个库也挂了,此时即使重新选举了其它的协调者,也不知道这个分布式事务当前的状态。

  • 数据不一致问题:在commit阶段,当协调者向所有的参与者发送commit请求后,发生了网络异常导致协调者在还未发完commit请求之前崩溃,可能会导致只有部分的参与者接收到commit请求,剩下没收到commit请求的参与者将无法提交事务,也就可能导致数据不一致的问题。

    总结:2PC 是一种尽量保证强一致性的分布式事务,因此它是同步阻塞的,而同步阻塞就导致长久的资源锁定问题,总体而言效率低,并且存在单点故障问题,在极端条件下存在数据不一致的风险。

3.2 3PC

3PC 包含了三个阶段,分别是准备阶段、预提交阶段和提交阶段,对应的英文就是:CanCommit、PreCommit 和 DoCommit。

(1)CanCommit阶段:事务管理器向各个数据库发送CanCommit消息,然后各个库返回结果。这一阶段主要是确定分布式事务的参与者是否具备了完成commit的条件,并不会执行事务操作。

(2)PreCommit阶段:事务管理器根据数据库的反馈情况来决定是否继续执行事务的PreCommit操作。如果各个数据库都返回成功,则进入PreCommit阶段,事务管理器发送PreCommit消息给各个数据库(相当于2PC里的阶段一),执行各个SQL语句,但不提交。如果有某个库对CanCommit消息返回了失败,则TM发送abort消息给各个库,结束这个分布式事务。

(3)DoCommit阶段:如果各个库对PreCommit阶段都返回了成功,那么发送DoCommit消息给各个库提交事务,各个库如果都返回成功给事务管理器,那么分布式事务成功。如果有个库对PreCommit返回的是失败,或者超时一直没返回,那么事务管理器认为分布式事务失败,直接发abort消息给各个库进行回滚,各个库回滚成功之后通知事务管理器,分布式事务回滚。
在这里插入图片描述

看起来是把 2PC 的提交阶段变成了预提交阶段和提交阶段,但是 3PC 的CanCommit阶段协调者只是询问参与者的自身状况,比如你现在还好吗?负载重不重?而预提交阶段就是和 2PC 的准备阶段一样,除了事务的提交该做的都做了。提交阶段和 2PC 的一样,让我们来看一下图。

不管哪一个阶段有参与者返回失败都会宣布事务失败,这和 2PC 是一样的(当然到最后的提交阶段和 2PC 一样只要是提交请求就只能不断重试)。

与2PC相比,主要有两个改进点:
(1)引入了CanCommit阶段。
(2)在DoCommit阶段,各个库自己也有超时机制。如果一个库收到了PreCommit自己还返回成功了,过了一段时间还没收到事务管理器发送的DoCommit消息或者是abort消息,直接判定为事务管理器可能出故障了,则会自己执行DoCommit操作提交事务。在3PC里面不会因为故障导致某个库一直锁住某个资源,导致长时间的资源阻塞。

3PC 的阶段变更有什么影响:

首先准备阶段的变更成不会直接执行事务,而是会先去询问此时的参与者是否有条件接这个事务,因此不会一来就干活直接锁资源,使得在某些资源不可用的情况下所有参与者都阻塞着。

预提交阶段的引入起到了一个统一状态的作用,它像一道栅栏,表明在预提交阶段前所有参与者其实还未都回应,在预处理阶段表明所有参与者都已经回应了。假如你是一位参与者,你知道自己进入了预提交状态那你就可以推断出来其他参与者也都进入了预提交状态。

但是多引入一个阶段也多一个交互,因此性能会差一些,而且绝大部分的情况下资源应该都是可用的,这样等于每次明知可用执行还得询问一次。

参与者超时能带来什么样的影响:

2PC 是同步阻塞的,事务管理器挂在了提交请求还未发出去的时候是最伤的,所有参与者都已经锁定资源并且阻塞等待着。

那么引入了超时机制,参与者就不会傻等了,如果是等待提交命令超时,那么参与者就会提交事务了,因为都到了这一阶段了大概率是提交的,如果是等待预提交命令超时,那该干啥就干啥了,反正本来啥也没干。

然而超时机制也会带来数据不一致的问题,比如在等待提交命令时候超时了,参与者默认执行的是提交事务操作,但是有可能执行的是回滚操作,这样一来数据就不一致了。

3PC 相对于 2PC 做了一定的改进:引入了参与者超时机制,并且增加了预提交阶段使得故障恢复之后协调者的决策复杂度降低,但整体的交互过程更长了,性能有所下降,并且还是会存在数据不一致问题。

所以 2PC 和 3PC 都不能保证数据100%一致,因此一般都需要有定时扫描补偿机制。

3.3 TCC

TCC 指的是Try - Confirm - Cancel,2PC 和 3PC 都是数据库层面的,而 TCC 是业务层面的分布式事务,一共分为三个阶段:

  • try阶段:这个阶段是对各个服务的资源做检测以及对资源进行锁定或者预留。
  • confirm阶段:这个阶段是在各个服务中执行实际的操作。
  • cancel阶段:如果有任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经执行成功业务逻辑的回滚操作。
    其实从思想上看和 2PC 差不多,都是先试探性的执行,如果都可以那就真正的执行,如果不行就回滚。比如说一个事务要执行A、B、C三个操作,那么先对三个操作执行预留动作。如果都预留成功了那么就执行确认操作,如果有一个预留失败那就都执行撤销动作。
    TCC模型还有个事务管理者的角色,用来记录TCC全局事务状态并提交或者回滚事务。

可以看到流程还是很简单的,难点在于业务上的定义,对于每一个操作你都需要定义三个动作分别对应Try - Confirm - Cancel。

适用场景:
对一致性要求很高的系统的核心链路,如支付、交易相关的场景。严格保证分布式事务要么全部成功,要么全部自动回滚,严格保证链路的正确性。而且,最好各个业务执行的时间都比较短。

相对于 2PC、3PC ,TCC 适用的范围更大,但是开发量也更大,毕竟都在业务上实现,而且有时候你会发现这三个方法还真不好写。不过也因为是在业务上实现的,所以TCC可以跨数据库、跨不同的业务系统来实现事务。
在这里插入图片描述

3.4 本地消息表

此方案的核心是将需要分布式处理的任务通过消息日志的方式来异步执行。消息日志可以存储到本地文本、数据库或消息队列,再通过业务规则自动或人工发起重试。人工重试更多的是应用于支付场景,通过对账系统对事后问题的处理。

在这里插入图片描述

假设有一个在线商城系统,包含订单服务和库存服务,它们运行在不同的服务器上,因此需要使用分布式事务来保证订单和库存的一致性。当一个客户下单时,订单服务会向库存服务发起扣减库存的请求。如果请求成功,订单服务再创建订单并提交事务;如果请求失败,则订单服务回滚事务。

然而,在分布式事务中,网络故障、服务故障或其他因素可能导致事务提交或回滚失败,从而导致订单和库存不一致。因此,我们需要使用本地消息表来解决这个问题。

本地消息表是一个本地数据库表,用于保存需要在分布式事务中执行的操作。当订单服务接收到客户的下单请求时,它会将扣减库存的请求和创建订单的操作插入到本地消息表中,然后立即提交事务。此时,订单服务并不会向库存服务发起扣减库存的请求,而是将扣减库存的请求作为消息发送到一个消息队列中。

库存服务从消息队列中读取消息,并执行扣减库存的操作。如果扣减库存成功,则将消息标记为已消费,并将操作的结果保存到本地数据库中。如果扣减库存失败,则将消息标记为未消费,并在一定时间后重新发送消息。这样可以保证库存服务在故障恢复后能够重新处理消息。

通过本地消息表,订单服务和库存服务可以保证操作的原子性,从而避免了分布式事务中的问题。如果订单服务在创建订单时发生故障,它可以从本地消息表中读取操作,重新执行扣减库存的请求,从而保证订单和库存的一致性。同样的,如果库存服务在扣减库存时发生故障,它可以从本地数据库中读取操作,重新执行扣减库存的请求,从而保证订单和库存的一致性。

3.5 消息队列

为了确保消息的可靠性,通常情况下,在完成一阶段的 DB 事务之后再发送消息,但这种方式存在一定的风险,因为业务程序很难保证 DB 事务提交后消息一定能成功发送。即使将发送失败的消息放到内存队列中稍后重试,也不能完全保证消息发送的成功率达到100%。

为了解决这个问题,RocketMQ 提供了半消息(Half Msg)机制,即先发送一个半消息,类似于 Prepare 操作,这个半消息不会立即被投递给消费者,而是等待本地事务执行的结果。如果本地事务执行成功,则将半消息状态改为“Committing”,表示消息已经发送并且事务已经提交。如果本地事务执行失败,则将半消息状态改为“Rollback”,表示消息已经发送但是事务需要回滚。

RocketMQ 接收到半消息后,会向消息生产者发送一个“预提交”(Pre-Commit)请求。生产者需要根据请求返回“提交”或“回滚”操作。如果生产者返回“提交”操作,则 RocketMQ 将消息状态改为“已提交”(Committed)并将消息发送给消费者。如果生产者返回“回滚”操作,则 RocketMQ 将消息状态改为“已回滚”(Rollbacked),消息不会被投递给消费者。

这种方式可以确保消息发送的可靠性和一致性,并且避免了传统方式的风险。但需要注意,该机制存在单点故障和性能瓶颈等问题,因此需要综合考虑使用的利弊。

对于 RocketMQ 消费者而言,事务消息和非事务消息是没有区别的,具体流程如下图:
在这里插入图片描述
如果不顺利,以上任何一步发生了异常会如何?我们挨个看一下:

  1. 如果在第1步出现异常导致半消息发送失败,则本地数据库事务不会执行,整个操作会失败。在此情况下,数据库和消息的状态是一致的,即都没有提交。
  2. 如果在第2步发生异常或返回超时,生产者认为操作已失败,因此不会执行数据库操作,进而无法继续进行后续操作。而另一方面,Broker已经成功存储了半消息,但却迟迟等不到后续的提交操作,等待时间超时后,Broker会向生产者发送询问,询问该半消息是应提交还是回滚。此时,生产者可以通过访问数据库来确认本地数据库事务的完成状态,并回答Broker的询问,从而使Broker得知如何处理该半消息。
  3. 如果在第3步中数据库操作失败,生产者可以在第4步告知Broker回滚半消息,或者报告状态为“未知”,让Broker稍后通过回查决定是提交还是回滚该半消息。
  4. 如果在第4步提交或回滚半消息失败,Broker会在一段时间后发起回查,与第2步异常情况类似。
  5. 如果在第5、6、7步回查失败,即回查发生异常或者回查仍然返回“未知”,或回查失败,RocketMQ会在稍后重新调度该消息,最多回查15次。

3.6 最大努力通知

最大努力通知方案的核心在于最大努力通知服务,这个服务的核心在于根据上游服务定义的重试规则对调用事变的消息,重试几次,最大努力尝试调用成功。
它跟可靠消息服务的区别在于:

  • 可靠消息服务会保证,如果下游服务执行不成功,会一直不停的重试,直到下游服务执行成功为止,最终达到数据一致性,但是中间可能有很长的一段时间数据是不一致的。
  • 最大努力通知服务,如果一次请求没成功,那么就将消息存到数据库里去,同时记录下它的重试规则,以及上一次重试的时间,是第几次重试,然后开启一个后台线程进行扫描,每次扫出来就根据规则去重新调用下游服务。

相关文章:

读懂分布式事务

一、概述 1.1 什么是分布式事务 事务我们都很熟悉,事务提供一种机制将一个活动涉及的所有操作纳入到一个不可分割的执行单元,组成这组操作的各个单元,要么全部成功,要么全部失败。 事务有四大特性: Atomic&#xf…...

多目标粒子群算法求解帕累托前沿Pareto,Pareto的原理,测试函数100种求解之21

目录 背影 parte前沿的定义 注意事项 基于多目标粒子群的帕累托前沿求解 主要参数 MATLAB代码 效果图 结果分析 展望 背影 在目标优化过程种,很多时候都两个或者多个目标,并且目标函数不能同时达到最优,鱼与熊掌不可兼得,这个时候可以通过求解帕累托前沿,通过帕累托前沿…...

数组:二分查找、移除数组等经典数组题

二分查找:相关题目链接:https://leetcode.cn/problems/binary-search/题目重现:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值…...

负责任动物纤维标准RAF

【负责任动物纤维标准RAF】RAF-Responsible Animal Fiber, 中文翻译为负责任动物纤维标准。RAF标准包含了三个子标准,即RWS(责任羊毛标准)、RMS(责任马海毛标准)和RAS(责任羊驼毛标准)。RWS&…...

storybook使用info插件报错

报错内容: RangeErrorMaximum call stack size exceededCall StackprettyPrintvendors-node_modules_pmmmwh_react-refresh-webpack-plugin_lib_runtime_RefreshUtils_js-node_mod-4ff2dd.iframe.bundle.js:160:27undefinedvendors-node_modules_pmmmwh_react-refresh-webpack-…...

【每日一题Day129】LC1247交换字符使得字符串相同 | 贪心

交换字符使得字符串相同【LC1247】 有两个长度相同的字符串 s1 和 s2,且它们其中 只含有 字符 "x" 和 "y",你需要通过「交换字符」的方式使这两个字符串相同。 每次「交换字符」的时候,你都可以在两个字符串中各选一个字…...

性能优化之node中间件耗时

背景 中间件在node框架中是很基本的套件,使用不当很容易对页面性能造成影响。除了node服务端外,前端做的SSR项目也要特别重视这块 哪些场景会造成中间件耗时特别严重? 罪魁祸首是:await阻塞 举个例子: 1.如何得到 …...

3-1 图文并茂说明raid0,raid1, raid10, raid01, raid5等原理

文章目录简介RAID类型RAID0RAID1RAID5RAID6RAID10RAID01RAID对比图简介 一、RAID 是什么? RAID ( Redundant Array of Independent Disks )即独立磁盘冗余阵列,简称为「磁盘阵列」,其实就是用多个独立的磁盘组成在一起…...

西北工业大学大学物理(I)下2019-2020选填考题解析

单选题12个,24分。1量子数考查前三个量子数由薛定谔方程决定,最后一个关于自旋的由狄拉克方程决定由这些量子数可以给出原子的壳层结构。考试其实考的不深,记住这个表就够了。2 书上18、19章量子物理的著名实验:光电效应&#xff…...

自动化测试selenium

目录 一、为什么引入自动化测试? 二、为什么选择selenium作为自动化测试工具? 三、环境部署 四、什么是驱动?驱动的工作原理? 五、selenium的基础语法 元素定位 元素操作 点击元素 模拟键盘输入 清除对象输入的文本…...

熟悉GC常用算法,熟悉常见垃圾收集器,具有实际JVM调优实战经验

程序的栈和堆 栈先进后出,且里面的数据自动释放, 堆内的空间则需要手动释放 java python go 只管创建,不用像c,c需要手动释放空间, 因为他们都会开一个进程GC(Garbage Collector),由垃圾回收…...

常量和变量——“Python”

各位CSDN的uu们你们好呀,今天,小雅兰的内容是Python的一些基础语法噢,会讲解一些常量和变量的知识点,那么,现在就让我们进入Python的世界吧 常量和表达式 变量和类型 变量是什么 变量的语法 变量的类型 常量和表达式 …...

《蓝桥杯每日一题》KMP算法·AcWing 141. 周期

1.题目描述一个字符串的前缀是从第一个字符开始的连续若干个字符,例如 abaab 共有 55 个前缀,分别是 a,ab,aba,abaa,abaab。我们希望知道一个 N 位字符串 S 的前缀是否具有循环节。换言之,对于每…...

URL介绍

前言Internet上的每一个网页都具有一个唯一的名称标识,通常称之为URL(Uniform Resource Locator, 统一资源定位器)。它是www的统一资源定位标志,简单地说URL就是web地址,俗称“网址”。一、URL概念URL是对互联网上得到…...

学习 Python 之 Pygame 开发魂斗罗(一)

学习 Python 之 Pygame 开发魂斗罗(一)Pygame回忆Pygame1. 使用pygame创建窗口2. 设置窗口背景颜色3. 获取窗口中的事件4. 在窗口中展示图片(1). pygame中的直角坐标系(2). 展示图片(3). 给部分区域设置颜色5. 在窗口中显示文字6. 播放音乐7. 图片翻转与…...

ARM uboot 源码分析8 - uboot的环境变量

一、uboot 的环境变量基础 1、环境变量的作用 (1) 让我们可以不用修改 uboot 的源代码,而是通过修改环境变量,来影响 uboot 运行时的一些数据和特性。譬如说,通过修改 bootdelay 环境变量,就可以更改系统开机自动启动时倒数的秒…...

【蓝牙mesh】Network协议层介绍

【蓝牙mesh】Network协议层介绍 Network层简介 上一章节我们讲解了蓝牙Mesh中Lower层的功能和数据格式。 Lower层的数据往下传输就到了网络层(Network Layer)。网络层定义了收到Lower层的数据后,如何对其进行判断、封装、加密、认证&#xf…...

基于遗传算法的配电网故障定位(Matlab代码实现)

👨‍🎓个人主页:研学社的博客💥💥💞💞欢迎来到本博客❤️❤️💥💥🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密…...

Leetcode.1247 交换字符使得字符串相同

题目链接 Leetcode.1247 交换字符使得字符串相同 Rating : 1597 题目描述 有两个长度相同的字符串 s1和 s2,且它们其中 只含有 字符 "x"和 "y",你需要通过「交换字符」的方式使这两个字符串相同。 每次「交换字符」的时…...

python语音识别whisper

一、背景 最近想提取一些视频的字幕,语音文案,研究了一波 二、whisper语音识别 Whisper 是一种通用的语音识别模型。它在不同音频的大型数据集上进行训练,也是一个多任务模型,可以执行多语言语音识别以及语音翻译和语言识别。 …...

Prometheus -- 浅谈Exporter

Prometheus系统 – Exporter原理 为什么我们需要Exporter? 广义上讲所有可以向Prometheus提供监控样本数据的程序都可以被称为一个Exporter。而Exporter的一个实例称为target,如下所示,Prometheus通过轮询的方式定期从这些target中获取样本…...

如何确定RocketMQ中消费者的线程大小

背景 随着物联网行业的发展、智能设备数量越来越多,随着设备活跃量过大,常常存在一些高并发的请求,形成了流量尖峰,过多的请求会压垮服务器,影响其他服务运行。因此,为了保护云端服务,需要对请求…...

OpenAPI SDK组件之Spring Aop源码拓展

Spring Aop 看这个分享的应该都用过Spring Aop,这里就不再过多介绍了它是什么了。 我抽取了Spring Aop的部分源码,通过它实现请求参数可变拦截,同时apisdk离开Spring框架,仍然可以正常运行。 讲拦截也好,通知也罢&a…...

蓝桥杯C/C++VIP试题每日一练之龟兔赛跑预测

💛作者主页:静Yu 🧡简介:CSDN全栈优质创作者、华为云享专家、阿里云社区博客专家,前端知识交流社区创建者 💛社区地址:前端知识交流社区 🧡博主的个人博客:静Yu的个人博客…...

为你的Vue2.x老项目安装Vite发动机吧

天下苦webpack久矣,相信作为前端开发者一定经历过在项目迭代时间较长的时候经历漫长等待的这一过程,每一次保存都会浪费掉大量时间,这是webpack这种机制所带来的问题。 于是,尤大为我们带来了新一代前端构建工具:vite…...

ZCMU--5012: 铺设道路(差分思路)

Description 春春是一名道路工程师,负责铺设一条长度为 n 的道路。 铺设道路的主要工作是填平下陷的地表。 整段道路可以看作是 n 块首尾相连的区域,一开始,第 i 块区域下陷的深度为 di。  春春每天可以选择一段连续区间 [L,R]&…...

算法模板总结(自用)

算法模板总结滑动窗口双指针算法数组相关合并两个有序数组左右指针技巧快慢指针技巧字符串相关左右指针反转字符串问题快慢指针替换空格字符问题链表相关快慢双指针删除链表的倒数第N个节点链表相交环形链表链表操作几数之和两数之和四个数组的四数之和三数之和同一数组中四数之…...

【架构师】零基础到精通——架构发展

博客昵称:架构师Cool 最喜欢的座右铭:一以贯之的努力,不得懈怠的人生。 作者简介:一名Coder,软件设计师/鸿蒙高级工程师认证,在备战高级架构师/系统分析师,欢迎关注小弟! 博主小留言…...

C++(20):三路比较运算符

C20增加了三路比较运算符<>&#xff08;戏称航天飞机运算符&#xff09;&#xff0c;用于对类的比较运算符进行统一的设计。有两种使用方式&#xff1a;默认比较对于某些类&#xff0c;如果按照其成员逐一比较即可决定比较运算符的值&#xff0c;那么可以使用默认的三路运…...

MySQL workbench 字符集、字符序的概念与联系

在数据的存储上&#xff0c;MySQL提供了不同的字符集支持。而在数据的对比操作上&#xff0c;则提供了不同的字符序支持。 MySQL提供了不同级别的设置&#xff0c;包括server级、database级、table级、column级&#xff0c;可以提供非常精准的设置。 什么是字符集、字符序&am…...