面试题|分布式事务面试题 (史上最全、持续更新、吐血推荐)

名词解释
事务:事务是由一组操作构成的可靠的独立的工作单元,事务具备ACID的特性,即原子性、一致性、隔离性和持久性。
本地事务:当事务由资源管理器本地管理时被称作本地事务。本地事务的优点就是支持严格的ACID特性,高效,可靠,状态可以只在资源管理器中维护,而且应用编程模型简单。但是本地事务不具备分布式事务的处理能力,隔离的最小单位受限于资源管理器。
全局事务:当事务由全局事务管理器进行全局管理时成为全局事务,事务管理器负责管理全局的事务状态和参与的资源,协同资源的一致提交回滚。
TX协议:应用或者应用服务器与事务管理器的接口。
XA协议:全局事务管理器与资源管理器的接口。XA是由X/Open组织提出的分布式事务规范。该规范主要定义了全局事务管理器和局部资源管理器之间的接口。主流的数据库产品都实现了XA接口。XA接口是一个双向的系统接口,在事务管理器以及多个资源管理器之间作为通信桥梁。之所以需要XA是因为在分布式系统中从理论上讲两台机器是无法达到一致性状态的,因此引入一个单点进行协调。由全局事务管理器管理和协调的事务可以跨越多个资源和进程。全局事务管理器一般使用XA二阶段协议与数据库进行交互。
AP:应用程序,可以理解为使用DTP(Data Tools Platform)的程序。
RM:资源管理器,这里可以是一个DBMS或者消息服务器管理系统,应用程序通过资源管理器对资源进行控制,资源必须实现XA定义的接口。资源管理器负责控制和管理实际的资源。
TM:事务管理器,负责协调和管理事务,提供给AP编程接口以及管理资源管理器。事务管理器控制着全局事务,管理事务的生命周期,并且协调资源。
两阶段提交协议:XA用于在全局事务中协调多个资源的机制。TM和RM之间采取两阶段提交的方案来解决一致性问题。两节点提交需要一个协调者(TM)来掌控所有参与者(RM)节点的操作结果并且指引这些节点是否需要最终提交。两阶段提交的局限在于协议成本,准备阶段的持久成本,全局事务状态的持久成本,潜在故障点多带来的脆弱性,准备后,提交前的故障引发一系列隔离与恢复难题。
BASE理论:BA指的是基本业务可用性,支持分区失败,S表示柔性状态,也就是允许短时间内不同步,E表示最终一致性,数据最终是一致的,但是实时是不一致的。原子性和持久性必须从根本上保障,为了可用性、性能和服务降级的需要,只有降低一致性和隔离性的要求。
CAP定理:对于共享数据系统,最多只能同时拥有CAP其中的两个,任意两个都有其适应的场景,真是的业务系统中通常是ACID与CAP的混合体。分布式系统中最重要的是满足业务需求,而不是追求高度抽象,绝对的系统特性。C表示一致性,也就是所有用户看到的数据是一样的。A表示可用性,是指总能找到一个可用的数据副本。P表示分区容错性,能够容忍网络中断等故障。
分布式事务与分布式锁的区别:
分布式锁解决的是分布式资源抢占的问题;分布式事务和本地事务是解决流程化提交问题。
事务简介
事务(Transaction)是操作数据库中某个数据项的一个程序执行单元(unit)。
事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
事务的四个特征:
1、Atomic原子性
事务必须是一个原子的操作序列单元,事务中包含的各项操作在一次执行过程中,要么全部执行成功,要么全部不执行,任何一项失败,整个事务回滚,只有全部都执行成功,整个事务才算成功。
2、Consistency一致性
事务的执行不能破坏数据库数据的完整性和一致性,事务在执行之前和之后,数据库都必须处于一致性状态。
【面试题|分布式事务面试题 (史上最全、持续更新、吐血推荐)】3、Isolation隔离性
在并发环境中,并发的事务是相互隔离的,一个事务的执行不能被其他事务干扰。即不同的事务并发操纵相同的数据时,每个事务都有各自完整的数据空间,即一个事务内部的操作及使用的数据对其他并发事务是隔离的,并发执行的各个事务之间不能相互干扰。
SQL中的4个事务隔离级别:
(1)读未提交
允许脏读。如果一个事务正在处理某一数据,并对其进行了更新,但同时尚未完成事务,因此事务没有提交,与此同时,允许另一个事务也能够访问该数据。例如A将变量n从0累加到10才提交事务,此时B可能读到n变量从0到10之间的所有中间值。
(2)读已提交
允许不可重复读。只允许读到已经提交的数据。即事务A在将n从0累加到10的过程中,B无法看到n的中间值,之中只能看到10。同时有事务C进行从10到20的累加,此时B在同一个事务内再次读时,读到的是20。
(3)可重复读
允许幻读。保证在事务处理过程中,多次读取同一个数据时,其值都和事务开始时刻时是一致的。禁止脏读、不可重复读。幻读即同样的事务操作,在前后两个时间段内执行对同一个数据项的读取,可能出现不一致的结果。保证B在同一个事务内,多次读取n的值,读到的都是初始值0。幻读,就是不同事务,读到的n的数据可能是0,可能10,可能是20
(4)串行化
最严格的事务,要求所有事务被串行执行,不能并发执行。
如果不对事务进行并发控制,我们看看数据库并发操作是会有那些异常情形
(1)一类丢失更新:两个事物读同一数据,一个修改字段1,一个修改字段2,后提交的恢复了先提交修改的字段。
(2)二类丢失更新:两个事物读同一数据,都修改同一字段,后提交的覆盖了先提交的修改。
(3)脏读:读到了未提交的值,万一该事物回滚,则产生脏读。
(4)不可重复读:两个查询之间,被另外一个事务修改了数据的内容,产生内容的不一致。
(5)幻读:两个查询之间,被另外一个事务插入或删除了记录,产生结果集的不一致。
4、Durability持久性
持久性(durability):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中对应数据的状态变更就应该是永久性的。
即使发生系统崩溃或机器宕机,只要数据库能够重新启动,那么一定能够将其恢复到事务成功结束时的状态。
比方说:一个人买东西的时候需要记录在账本上,即使老板忘记了那也有据可查。
MySQL的本地事务实现方案
大多数场景下,我们的应用都只需要操作单一的数据库,这种情况下的事务称之为本地事务(Local Transaction)。本地事务的ACID特性是数据库直接提供支持。
了解过MySQL事务的同学,就会知道,为了达成本地事务,MySQL做了很多的工作,比如回滚日志,重做日志,MVCC,读写锁等。
MySQL数据库的事务实现原理
以MySQL 的InnoDB (InnoDB 是 MySQL 的一个存储引擎)为例,介绍一下单一数据库的事务实现原理。
InnoDB 是通过 日志和锁 来保证的事务的 ACID特性,具体如下:
(1)通过数据库锁的机制,保障事务的隔离性;
(2)通过 Redo Log(重做日志)来,保障事务的持久性;
(3)通过 Undo Log (撤销日志)来,保障事务的原子性;
(4)通过 Undo Log (撤销日志)来,保障事务的一致性;
Undo Log 如何保障事务的原子性呢?
具体的方式为:在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为 Undo Log),然后进行数据的修改。如果出现了错误或者用户执行了 Rollback 语句,系统可以利用 Undo Log 中的备份将数据恢复到事务开始之前的状态。
Redo Log如何保障事务的持久性呢?
具体的方式为:Redo Log 记录的是新数据的备份(和 Undo Log 相反)。在事务提交前,只要将 Redo Log 持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是 Redo Log 已经持久化。系统可以根据 Redo Log 的内容,将所有数据恢复到崩溃之前的状态。
脏读、幻读、不可重复读
在多个事务并发操作时,数据库中会出现下面三种问题:脏读,幻读,不可重复读。
脏读(Dirty Read)
事务A读到了事务B还未提交的数据:
事务A读取的数据,事务B对该数据进行修改还未提交数据之前,事务A再次读取数据会读到事务B已经修改后的数据,如果此时事务B进行回滚或再次修改该数据然后提交,事务A读到的数据就是脏数据,这个情况被称为脏读(Dirty Read)。
img
幻读(Phantom Read)
事务A进行范围查询时,事务B中新增了满足该范围条件的记录,当事务A再次按该条件进行范围查询,会查到在事务B中提交的新的满足条件的记录(幻行 Phantom Row)。
img
不可重复读(Unrepeatable Read)
事务A在读取某些数据后,再次读取该数据,发现读出的该数据已经在事务B中发生了变更或删除。
img
幻读和不可重复度的区别:
幻读:在同一事务中,相同条件下,两次查询出来的 记录数 不一样;
不可重复读:在同一事务中,相同条件下,两次查询出来的 数据 不一样;
事务的隔离级别
为了解决数据库中事务并发所产生的问题,在标准SQL规范中,定义了四种事务隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。
低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。
MySQL事务隔离级别:https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html
通过修改MySQL系统参数来控制事务的隔离级别,在MySQL8中该参数为 transaction_isolation ,在MySQL5中该参数为 tx_isolation :
MySQL8:
– 查看系统隔离级别:
SELECT @@global.transaction_isolation;
– 查看当前会话隔离级别
SELECT @@transaction_isolation;
– 设置当前会话事务隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
– 设置全局事务隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
事务的四个隔离级别:
未提交读(READ UNCOMMITTED):所有事务都可以看到其他事务未提交的修改。一般很少使用;
提交读(READ COMMITTED):Oracle默认隔离级别,事务之间只能看到彼此已提交的变更修改;
可重复读(REPEATABLE READ):MySQL默认隔离级别,同一事务中的多次查询会看到相同的数据行;可以解决不可重复读,但可能出现幻读;
可串行化(SERIALIZABLE):最高的隔离级别,事务串行的执行,前一个事务执行完,后面的事务会执行。读取每条数据都会加锁,会导致大量的超时和锁争用问题;
img
问:如何保证 REPEATABLE READ 级别绝对不产生幻读?
答:在SQL中加入 for update (排他锁) 或 lock in share mode (共享锁)语句实现。就是锁住了可能造成幻读的数据,阻止数据的写入操作。

    推荐阅读