目录
2Mysql是如何保证原子性的?【要么同时成功,要么同时失败】
3.Mysql是如何保证持久性的?【事务一旦提交不能进行回滚】
那么如何实现分布式事务【4种实现方式】呢?又是如何进行选型的呢?
你能简单描述一下你在项目中是如何集成Seata的吗?【实际如何集成可以查资料】
什么是事务?
事务就是操作数据库的最小执行单元,一个事务内的操作要么全部成功,要么全部失败,保证数据的一致性。
事务的四大特性?
事务的四大特性(ACID):原子性Atomicity 一致性Consistency 隔离性Isolation 持久性Durability
原子性:一个事务内的操作要么全部成功,要么全部失败。
持久性:事务一旦提交【不能回滚】,将数据持久化到磁盘中,保证数据不会丢失
隔离性:【事务之间互不影响】两个事务修改同一个数据,必须按顺序执行,并且前一个事务如果未完成,那么中间状态对另一个事务不可见
一致性:【事务执行前后都必须保证数据的总和是一致的】要求任何写到数据库的数据都必须满足预先定义的规则,它基于其他三个特性实现的【【转账的前后金额是一致的,少100,那边就会多100】 】
======================ACID拓展=============================
原子性:事务的原子性是如何保证的?就是如何保证事务回滚时,能够获取到以前的数据?
主要是利用Innodb的undolog日志(回滚日志),当事务进行回滚的时候能够根据undolog日志获取到原本的数据。
持久性:持久性的关键就是事务一旦提交,不能进行回滚。通过undolog实现事务的原子性,通过redolog实现事务的持久性。
undolog是记录旧数据的备份,redolog是记录新数据的备份。
在事务提交之前,只需要将redolog日志进行持久化到磁盘当中,不需要将数据持久化到数据库中。当系统崩溃时,虽然数据没有持久化到数据库当中,但是可以通过Redolog日志获取到新数据,将所有数据恢复到最新的状态。
MySql事务是如何保证ACID四大特性的?
1.Mysql是如何保证一致性的?【数据的一致性】
从数据库层面,数据库通过原子性,隔离性和持久性来保证 一致性。
所以说ACID中C(一致性【数据一致性】)是目的,原子性,隔离性,持久性三个是保证数据的一致性的手段。所以要想保证数据的一致性就需要保证事务中的原子性和隔离性和持久性。第二种,2采用排他锁,就是对当前数据进行操作时就进行加锁,其他事务不能进行操作该数据,只有等待当前线程执行完成释放锁过后才能获取锁。
2Mysql是如何保证原子性的?【要么同时成功,要么同时失败】
使用Innodb的undo log日志(进行回滚),用来记录旧的是数据,当事务执行失败,进行回滚就可以从undolog日志中获取原本的数据。
简明知意,就是回滚日志,是实现原子性的关键,当事务回滚时能够撤销已经执行成功的sql语句,并执行日志中的sql语句恢复旧数据。
3.Mysql是如何保证持久性的?【事务一旦提交不能进行回滚】
利用innodb的redo log日志。
redo log记录的是新数据的备份,在事务提交前,需要将Redo Log持久化到磁盘当中,不需要将数据持久化,当系统崩溃时,虽然数据没有持久化,系统可以根据redo Log的内容,将所有数据恢复到最新的状态
mysql事务的执行流程?
宏观上:开启事务,执行事务,提交事务,出现异常回滚事务。
实际上(举例说明):条件:事务需要将A=1修改成A=3。
执行流程:首先开启事务,将A=1存储到undolog日志用于回滚,修改A=3并将A=3存储到redolog日志(用于存储最新的数据),将redolog持久化到磁盘当中,修改成功,提交事务。
1.A.开启事务.
2.B.记录A=1到undo log.
3.C.修改A=3.
4.D.记录A=3到redo log.
5.E.记录B=2到undo log.
6.F.修改B=4.
7.G.记录B=4到redo log.
8.H.将redo log写入磁盘。
I.事务提交
Spring事务传播(机制)有哪些?
事务传播机制指的是多个方法相互调用【A方法中调用B方法,方法上都有事务】时,事务如何在这些方法之间进行传播。
事务是数据库操作的最⼩⼯作单元,事务中的一组操作要么全部成功,要么全部失败。
在Spring中规定了7种类型的事务传播行为
增删改直接使用事务的注解@Transactional
其中默认就是 Propagation propagation() default Propagation.REQUIRED; boolean readOnly() default false;
查找使用事务注解@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
举例:A方法中调用B方法 【A方法和B方法都有事务】
Spring事务失效的场景
-
未开启事务:@EnableTransactionManagement 注解用来启用spring事务自动管理事务的功能,这个注解千万不要忘记写了
-
数据库不支持事务
-
注解修饰的方法不是public类型的:@Transaction 可以用在类上、接口上、public方法上,如果将@Trasaction用在了非public方法上(不是公共的了),事务将无效。
-
数据源未配置事务管理器【将datasource交给事务管理器处理】
-
自身调用问题,比如:入口方法A没有事务,在方法内部调用该类中的B方法,B方法的事务会失效,因为入口方法没有事务就是在非代理情况下执行,那么B方法也会非代理执行。
-
异常类型错误[抛出异常的类型事务接收不到.]:@Transactional(rollbackFor = {异常类型列表})
-
异常被吞了【执行方法出现异常,抛出异常时,异常丢失了,所以事务认为执行成功了.】
-
业务和spring事务代码必须在一个线程中
事务的并发有哪些问题?
首先要知道为了会造成事务并发,就是当多个事务对数据库的共享资源(例如数据库的一行数据)进行并发操作,从而导致数据不一致的问题。
事务的并发可能出现那些问题呢?
1脏读:读取到的数据可能是其他事务未提交到数据库的数据。【就是读取到其他事务正在修改的数据,但这个事务不一定会将这个数据提交到数据库】
2不可重复读:同一个事务中多次使用相同条件读取到的数据值不一致 【事务B在读取这条数据的时候,事务A对这条数据进行修改了。】
3幻读:同一个事务使用相同的条件读取到的数据条数不一致【事务A 按照一定条件进行数据读取, 期间事务B 插入了相同搜索条件的新数据 】。
但是当你在 MySQL 中测试幻读的时候,并不会出现上图的结果,幻读并没有发生,因为MySQL 的可重复读隔离级别其实解决了幻读问题,它使用到了“间隙锁”。
间隙锁:就是对当前线程修改的数据进行加锁,加了3把锁,就是防止当前线程正在操作这条数据的时候,其他线程进行插入相同条件的数据,删除相同条件的数据,修改相同条件的数据。
第一类丢失更新:也叫回滚丢失,事务A和事务B同时对同一条数据进行写操作,事务B先完成了修改,此时事务A异常终止,回滚后造成事务B的更新也丢失了
第二类丢失更新:也叫覆盖丢失,事务A和事务B同时对同一条数据进行写操作,事务B先完成了修改,事务A再次修改并提交,把事务B提交的数据给覆盖了【同一时间对】
丢失更新问题
脏读,幻读,不可重复读,都是发生在一个事务在写,一个事务在读的时候出现的问题。
丢失更新发生在两个事务同时都在做写操作的时候出现的
丢失更新分为:回滚丢失更新(一类)和覆盖丢失更新(二类)
事务隔离级别有那些?分别解决了什么问题?
首先要明白事务隔离级别是什么意思?
事务隔离级别:指的是一个事务与其他事务的隔离程度。就是用来解决事务并发问题。
比如脏读,不可重复读,幻读,更行丢失(回滚丢失)/(覆盖丢失)
事务隔离级别包含4种:
读未提交(READ UNCOMMITTED)
允许事务A能够读取到事务B未提交的数据(三个都可能会出现)。
读提交(READ COMMITTED)
事务A只能读取到事务B已经提交的数据(可能会出现不可重复读(两次查询数据值不一样)和幻读(两次查询数据条数不一样,(事务A查询条件和事务B一样))问题)。
可重复读(REPEATABLE READ)【默认的】
事务A多次读取共享资源(数据)(例如数据库的一行数据),读取的数据值都是一致的(可能会出现幻读)。可重复读虽然能够对读取的数据进行加锁(悲观锁),但是这时候加锁并不能锁住其他事务去进行insert数据,然后这时候就可能出现幻读,读取的条数与以前不一致。
串行化(SERIALIZABLE)【乐观锁和悲观锁】:
事务A多次读取共享资源,读取到的数据的行和值都是一致的(事务并发问题都不会出现)。隔离效果最好,可以解决脏读,不可重复读,幻读的问题。但是性能最低,类似于单线程,事务A对共享资源进行操作时,其他事务必须等待事务A执行结束。
事务隔离级别的原理?
事务隔离级别的原理是MVCC(Multi Version Concurrency Control -多版本并发控制)。
MVCC多版本并发控制指的是“通过多个版本号,来解决多个事务对同一个数据读写冲突。”,当一个事务读取数据的时候,会采用一种类似快照的方式将数据保存到undolog日志当中,事务每次读取都是从undolog日志中进行读取。不同事务的数据快照版本是不一样的,即使其他事务修改了数据,对于当前事务也是不可见的,仍然只会从第一次快照到undolog日志中的数据。
MVCC只工作在REPEATABLE READ和READ COMMITED隔离级别下。READ UNCOMMITED不是MVCC兼容的,因为查询不能找到适合他们事务版本的行版本;它们每次都只能读到最新的版本。SERIABLABLE也不与MVCC兼容,因为读操作会锁定他们返回的每一行数据。
可重复读:读取的数据第一次读取快照到undolog日志中的数据(读快照,读取的数据是旧数据)
读提交:是每次读取的时候都重新生成一次快照(当前读(读写锁)-读取的数据是最新的数据)
===================================================
读写锁指的是每次读操作都需要一个共享锁(其他事务只要是读取也可以共享这把锁),写操作需要一个写锁(排他锁),共享锁之间和共享锁不会产生互斥,共享锁和写锁之间会产生互斥,写锁与写锁之间产生互斥,当产生锁(写锁与写锁)竞争时,需要等待上一个操作的锁释放,另一个操作才能获得锁。
获取读锁的读语句:select * from xxx lock in share mode;
获取写锁的读语句:select * from xxx for update
什么是丢失更新,如何解决?
丢失更新发生在两个事务同时都在做写操作的时候出现的。
丢失更新包括:第一类丢失更新(回滚丢失)和第二类丢失更新(覆盖丢失)
第一类丢失更新【回滚丢失】:
事务A和事务B两个事务同时对同一条数据进行写操作,事务B先完成了修改,此时事务A异常终止,回滚后造成事务B的更新也丢失了
以上的例子很容易解释清楚第一类丢失更新,也就是 A事务回滚时,把已经提交的B事务的更新数据覆盖了。但是这种丢失更新已经被数据库消灭掉了, 我们在使用的过程中是不会遇到了。通过设置隔离级别可以防止 Repeatable Read
第二类丢失更新【覆盖丢失】:
事务A和事务B两个事务同时对同一条数据进行写操作,事务B先完成了修改,事务A再次修改并提交,把事务B提交的数据给覆盖了
上面的例子里由于汇入事务覆盖了取款事务对存款余额所做的更新,导致银行最后损失了100元,相反如果转账事务先提交,那么用户账户将损失100元。(实际上和不可重复读是同一种问题)。使用悲观锁,乐观锁都可以解决。
如何解决丢失更新?
采用乐观锁或者悲观锁进行解决。
悲观锁和乐观锁是两种思想,用于解决线程并发场景下资源竞争的问题
-
悲观锁,每次操作数据的时候,都会认为会出现线程并发问题(会同时对数据进行修改),一开始就会进行上锁,执行完成过后才会释放锁,线程安全,性能较低,适用于写多读少的场景
-
乐观锁,每次操作数据的时候,认为不会出现线程并发问题。不会使用加锁的形式而是在提交更新数据的时候,判断一下在操作期间有没有其他线程修改了这个数据,如果有,则重新读取,再次尝试更新(Retry),循环上述步骤直到更新成功 。否则就执行操作。线程不安全,执行效率相对较高,适用于读多写少的场景
悲观锁一般用于并发冲突概率大,对数据安全要求高的场景。【会进行加锁,不会出现线程安全问题。乐观锁在执行更新时频繁失败,需要不断重试,反而会浪费CPU资源】
乐观锁一般用于高并发,多读少写的场景【乐观锁不会进行加锁,其他线程可以进行同时访问,提高响应效率】
乐观锁的使用场景?
ES是采用的乐观锁,因为ES的使用场景为读多写少,基本上不会出现线程并发问题。
什么是微服务?
微服务目前没有一个统一的定义,但通常而言,微服务是一种架构模式/风格。
微服务也是一个分布式系统,它将一个单体应用进行拆分成多个微服务,每个微服务独立开发、维护和部署,每个服务也都可以有自己的数据库,服务之间使用HTTP通信,互相协调完成整个系统的业务。
它的优点是服务之间解耦合,不同的服务可以有不同的编程语言,支持分布式和集群能够承载更大的服务器压力,支持敏捷开发
他的缺点是分布式事务很复杂【要保证两个业务同时成功或者同时失败】,部署麻烦,技术成本高(使用多种语言进行编程),服务间通信对性能也有一定的损耗(因为使用了http进行通信)
什么是分布式事务?
分布式事务指的是在分布式微服务中,一个请求 需要到对多个数据库的增删改操作,要保证多个数据库的数据的一致性就需要用到分布式事务【要么同时成功要么同时失败】。
【举例,在分布式微服务中,会将一个大应用拆分成多个用户模块,订单模块,商品模块等,当一个当用户购买商品时,需要扣减商品模块的库存,在订单模块添加订单数据,这时候一个请求需要对这两个(或多个)数据库进行操作,就需要使用分布式事务保证多个数据库的数据一致性。】
那么如何实现分布式事务【4种实现方式】呢?又是如何进行选型的呢?
常见的4种分布式事务实现方案,2PC【seata-应用层层面】,TCC【事务补偿】,消息队列实现最终一致性【RocketMQ】,最大努力通知【支付宝,】
2PC,它将整个事务流程分为两个阶段,P指的是准备阶段,C指的是提交阶段。它是一个阻塞协议,
不适用于并发较高[因为需要等待参与者提交事务的状态],事务生命周期长的分布式事务。
TCC,它是基于补偿性事务的AP系统的一种实现,补偿也就是说先按照预定方案执行,如果失败了就走补偿方案。它可以自定义数据库操作的粒度(即数据统计的详细程度)。使得降低锁冲突【等待别的线程释放锁】,提高吞吐量(单位时间内成功地传送数据的数量),但是对应用的侵入性强,每个接口都需要实现try confirm cancel方法。改动成本高,实现难度大可以用在登录送积分,送优惠券等等场景
可靠消息最终一致性【rocketMQ】,系统A本地事务执行成功,并通知系统B执行,通常通过MQ实现。实现服务之间解耦,数据保持弱一致性,并且实时性要求不高的场景
最大努力通知【支付宝的支付结果通知,扣款通知】,是在不影响主业务的情况下,尽可能的保证数据的一致性,它适用于一些最终一致性敏感度低的业务,比如支付结果通知
什么是2PC?
两阶段提交协议(2PC),它将整个事务流程分为两个阶段,P(prepare)指的是准备阶段,C(commit)指的是提交阶段。它是一个阻塞协议。
基于XA的2PC【基于数据库层面实现】
2PC的传统方案是在数据库层面实现的,如Oracle、MySQL都支持2PC协议。
为了让大家更明确XA方案的内容程,下面新用户注册送积分为例来说明:
基于XA的2PC执行流程:【TM、RM】
1、应用程序(AP)持有用户库和积分库两个数据源。
2、应用程序(AP)通过TM(事务管理器)通知用户库RM(资源管理器)新增用户,同时通知积分库RM为该用户新增积分,RM此时并未提交事务,此时用户和积分资源锁定【资源不会进行释放】。
3、TM收到执行回复,只要有一方失败则分别向【其他RM】发起回滚事务,回滚完毕,资源锁释放。
4、TM收到执行回复,全部成功,此时向所有RM发起提交事务,提交完毕,资源锁释放【提交事务或回滚事务过后才释放锁资源】。
但是也有很多缺陷
性能问题: 同步请求,需要等待所有事务参与者执行完毕,事务协调器才能通知进行全局提交,最后才会释放资源。
事务管理器单点故障:一旦事务管理器挂掉,导致参与者接收不到提交或回滚的通知。
丢失消息导致数据不一致:在提交阶段,如果事务管理器出现故障,一部分事务参与者收到了提交消息,另一部分事务参与者没有收到提交消息,造成数据不一致问题(就是通知了一部分提交失误了,然后事务管理器宕机了,造成数据不一致问题。)
基于Seata的2PC【TM、TC、RM】【经常使用】
Seata把一个分布式事务理解成一个包含了若干分支事务的全局事务。全局事务的职责是协调其下管辖的分支事务达成一致,要么一起成功提交,要么一起失败回滚。
它是一个是开源的分布式事务框架。
主要优点是性能较好,且不长时间占用连接资源,它以高效并且对业务0侵入(不会对原有代码进行干扰)的方式解决微服务场景下面临的分布式事务问题。
与 传统2PC 的模型类似,Seata定义了3个组件来协调分布式事务的处理过程。
TC【Transaction coordinator】:事务协调器,接收TM的指令发起的事务的提交或回滚,并通知RM提交或者回滚事务
TM【TransAction management】:事务管理器,负责开启全局事务,并最终向TC发起全局事务的提交或回滚
RM【Resource managment】:资源管理器,控制分支事务的提交和回滚。控制分支事务,负责分支注册、状态汇报,并接收事务协调器TC的指令,驱动分支事务的提交和回滚
还拿新用户注册送积分举例Seata的分布式事务过程:
基于Seata的2PC执行流程
1. 用户服务的 TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID。
2. 用户服务的 RM 向 TC 注册 分支事务,该分支事务在用户服务执行新增用户逻辑,并将其纳入 XID 对应全局事务的管辖。
3. 用户服务执行分支事务,向用户表插入一条记录写undolog日志(用于回滚数据) 。
4. 当调用积分服务时。积分服务的RM 向 TC 注册分支事务,该分支事务执行增加积分的逻辑,并将其纳入 XID 对应全局事务的管辖。
5. 积分服务执行分支事务,向积分记录表插入一条记录写undolog日志(用于回滚数据),执行完毕后,返回用户服务。
6. 用户服务分支事务执行完毕【添加用户时会赠送积分这是两个分支事务,都执行完成后会返回到用户服务。然后会返回一个执行结果给用户服务】。
7. 用户服务的TM 会根据返回的执行结果向 TC 发起对 XID 的全局提交或回滚。
8. TC 然后对改 XID 下的全部分支事务完成提交或回滚请求。
基于Seata的2PC相比传统2PC有什么区别,以及优点?
传统2PC无论第二阶段的决议是commit还是rollback,释放资源的锁都要保持到第二阶段完成才释放。
而Seata的做法是在第一阶段就将本地事务提交(主要是依靠undolog日志,可以进行回滚),这样就可以省去第二阶段持锁资源的时间,整体提高效率。
你能简单描述一下你在项目中是如何集成Seata的吗?【实际如何集成可以查资料】
事务协调器:安装并启动Seata客户端【seata-server】
主业务端:
-
第一步,导入Seata依赖
-
第二步,yml中配置事务组名,同时需要添加配置文件file.conf,registry.conf,需要注意yml中事务组名与file.conf中的事务组名一致
-
第三步,配置DataSource【启动类@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class})】,需要使用Seata对DataSource进行代理
-
第四步,数据库中添加undolog日志表(避免失败,可以进行回滚)【设置到事务的都需要加上,主事务和分支事务】
-
第五步,业务方法上加注解@GlobalTransactional(rollbackFor = Exception.class)全局事务注解
事务参与者:
-
前四步与主业务端相同,第五步不需要了【在主业务中加上】
什么是TCC?
TCC, 又称为补偿性事务, 具有弱一致性。补偿指的是按照事先预定的方案去执行,如果失败了就走补偿方案(撤销)
TCC是Try,Confirm,Cancel三个词语的缩写,
Tcc要求每个分支事务实现三个操作:预处理Try,确认Confirm【类似于提交】,撤销Cancel【类似于回滚】。
TM事务管理器向所有分支事务发送预处理Try的操作,分支事务对需要操作的资源进行锁定,任何一个分支事务操作失败,TM会向所有分支事务发送Cancel(撤销)操作,如果全部分支事务操作成功,则向所有分支发送Confirm(确认)操作。其中Confirm/Cancel操作如执行失败,TM事务管理器会进行重试。
它的优点是异步执行效率高,它能对分布式事务中的各个资源 分别锁定,分别提交与释放
它的缺点是对应用的侵入性强,每个接口都需要实现try confirm cancel方法。改动成本高,实现难度大
什么是CAP理论 , 哪些技术用到AP,哪些用到CP?
CAP理论指的是,在一个分布式系统中,一致性(Consistency),可用性(Avaliability),分区容错性(Partition Tolerance),三个要素最多只能同时实现两点,不能同时兼顾。
分布式容错:指的是分布式系统某个节点或网路分区出现故障的时候,不会影响整体的使用。
可用性:一直可以正常地读写操作【但是不会保证数据的一致性】。简而言之就是客户可以一直正常访问并得到系统地正常响应,不会出现访问超时等问题。
一致性:在分布式系统中,读取数据应该是最新的数据,进行写操作后再去进行获取应该获取到最新的值,数据的一致性。
分区容错性是分布式系统的核心关键,如果一个节点或网络分区出现故障,整体就挂掉了,这就不叫作分布式系统。
满足CP,也就是满足一致性和容错性,舍弃可用性,如果系统要保证数据地强一致性(必须等待数据同步才进行响应)就可以考虑。常见的如Redis,Nacos,ZooKeeper
满足AP,也就是满足可用性和容错性,舍弃一致性,如果系统允许数据的弱一致性(最终一致性即可,获取的数据不是最新的,但最终是一致的)可以考虑。常见的如MySQL,Eureka
什么是Base理论?
Base理论指的是基本可用(Basic Available),软状态(Soft),最终一致性(eventually coonsistent)。它是基于的AP【可用性和分区容错性】的扩展,在分布式系统中某个节点或网络分区出现故障时,系统的整体不受影响,正常使用。允许在一段时间内数据不一致,但要保证数据的最终一致性。
基本可用(Basic Available)
响应时间上的损失:正常情况下,处理用户请求需要0.5s返回结果,但是由于系统出现故障,处理用户请求的时间变成3s。【但是还是可以使用--基本可用】
系统功能上的损失:正常情况下,用户可以使用系统的全部功能,但是由于系统访问量突然剧增,系统的非核心功能无法使用。
软状态(Soft)
指的是允许系统的数据存在中间状态,并认为中间状态并不会影响系统的整体的可用性,比如正在支付中,数据正在同步中.....
最终一致性(eventually coonsistent)
最终一致性指的是数据经过一段时间后,所有的节点数据最终都是一致性的。比如订单的"支付中"状态,最终会变为“支付成功”或者"支付失败" ,使订单状态与实际交易结果达成一致,但需要一定时间的延迟等待。