欢迎光临散文网 会员登陆 & 注册

事务理论及其实现方案百度资源分享

2023-07-10 20:51 作者:吴小敏63  | 我要投稿

一、用户汇款场景#

用户 A 的账户存有 2200 元,用户 B 的账户存有 1600 元。现在用户 A 给用户 B 汇款 200 元,正确的执行步骤为:

  1. A 用户:A存款 = 2200 - 200

  2. B 用户:B存款 = 1600 + 200

  • 如果上面的汇款步骤正确执行完,那么 A 用户现在的存款数是 2000 元, B 用户现在的存款数是 1800。

  • 如果上面的汇款步骤没有正确执行完呢?比如遇到了异常情况, A 用户存款数已经扣去了 200, B 用户的存款数却没有加上 200。那么现在 A 用户存款数是 2000 元,B 用户的存储数还是 1600 元。此时,A 用户存款数和 B 用户存款数都出现了错误。

A 用户汇款了存款数减少了,B 用户却没有收到汇款。这种情况一定会给用户造成困扰,不得不打电话去银行询问出了啥事情?

为什么会出现这种情况?

在汇款操作的步骤中,有 2 个步骤:

  1. A 用户汇款 2. B 用户收款

日常理解来看这 2 个步骤没多大问题,但是在计算机软件执行操作中来看,A 扣款和 B 收款是2个不同动作,所以 A 扣款动作可能成功也可能失败,B 同样如此。因为程序和网络有可能出现各种异常情况。

这种分步骤的日常操作,在软件中我们需要把它们当作一个整体来操作,操作结果是要么都成功,要么都失败。

上面汇款来说,都成功就是,A 扣款减少和 B 收款增加都成功。都失败就是,A 扣款减少和 B 收款增加都失败,A 回滚到原来存款数,B 存款数不增不减。(A 的存款数 + B 的存款数)= 总资金数,总资金数在汇款前后是不变的。

A扣款操作和B收款操作当作是一个整体操作,一个不可分割的原子操作。在计算机软件中,我们把这种操作叫做事务。

二、什么是事务#

上面讨论的场景已经引出了什么是事务?

事务是将程序中的多个操作(比如多个读、写等)"糅合"在一起成为一个整体的逻辑操作单元,整个逻辑操作单元中的所有读写操作是一个执行的整体,整体被视为一个不可分割的操作,这个就称为事务。事务操作要么成功,要么失败(终止或回滚)。

如果事务操作失败了,不需要担心事务里的部分操作失败的情况,部分失败后可回滚,恢复到原来数据状态。

三、事务的 ACID 特性#

通常说到数据库事务时,都会提到 ACID 这 4 个特性。这 4 个特性是 TheoHarder 和 Andreas Reuter 于1983年为精确描述数据库的容错机制而定义的。

但各家数据库系统实现的 ACID 又不尽相同。有些系统说自己提供事务时或”兼容ACID“时,其实我们无法确信它究竟提供了什么样的保证,需要仔细查看该系统文档或代码才知晓细节。

InnoDB 默认事务隔离级别是可重复读,不满足隔离性;Oracle 默认的事务隔离级别为读已提交,不满足隔离性。因为有时完全满足就可能导致性能问题,有一个取舍平衡。

下面看看 ACID 具体含义:

  • C(Consistency) 一致性:当事务开始和结束时,数据处于一致的状态。比如上面汇款场景,总资金数(数据)在汇款前后是不变的,保持了前后一致。

    有的人说一致性是 ACID 的目的,只要保证了原子性、隔离性、持久性,也就保证了数据的一致性。

    数据库提供了某些一致性的约束,比如主键 ID,唯一索引等。对于业务数据一致性,数据库并没有提供很好的约束,业务数据的一致性需要应用程序来保证。

  • A(Atomicity) 原子性:原子通常指不可分割的最小粒度物质。事务中对数据的所有写操作像一个单一的操作一样执行,操作是不可分割的,要么都成功,要么都失败(终止或回滚)。

    ACID 中的原子性并不是关于多个操作的并发性,它没有描述多个线程访问相同数据会发生什么情况,这种情况是由 ACID 中的隔离性定义。原子性描述的是客户端发起一个包含多个写操作请求时可能发生的情况,比如一部分写入后,发生系统故障,包括进程崩溃,网络中断,磁盘满了等情况;这里原子性是把多个写操作作为一个原子操作,作为一个整体,万一遇到故障导致没能最终提交,事务会终止,数据库必须丢弃或撤销局部完成的更改。

  • I(Isolation) 隔离性:在处理程序时,事务保证了各种数据操作相互独立性,事务的中间状态对其它事务是不可见的。

    这里的隔离性意味着并发执行多个事务是相互独立、相互隔离的。经典数据库教材把隔离性定义为可串行化(一个一个的执行)。但是在实践中,串行化有心性能问题。比如 Oracel 11g,声称有“串行化”功能,但它本质是快照隔离,比串行化更弱的保证。

  • D(Durability) 持久性:事务应该保证所有成功提交的数据都能被持久化,即使发生故障,也不会丢失数据。

    数据库会保障一旦事务提交成功,即使硬件故障或数据库崩溃,事务所写入的数据也不会丢失。

四、MySQL中的事务#

MySQL事务介绍#

事务的概念其实最早是从数据库系统中来的。

事务处理系统使应用程序员能够集中精力去编写业务代码,而不必关心事务管理的各种细节。

在 MySQL 等关系型数据库中,事务是保证数据状态一致性的一个重要手段。

在 MySQL 中,一个事务可能包含多条 SQL 语句的操作,这些语句作为一个整体要么都执行成功,要么都执行失败。

MySQL 支持事务的存储引擎有 InnoDB、NDB Cluster 等,其中 InnoDB 的使用最为广泛,MyISAM 存储引擎不支持事务。MySQL 服务层并不实现事务。

MySQL 事务处理使用下面语句:

START TRANSACTION/BEGIN :开启一个事务

ROLLBACK : 回滚一个事务

COMMIT :提交事务

当然我们也可以设置 MySQL 事务自动提交模式:

  • SET AUTOCOMMIT=0 禁止自动提交

  • SET AUTOCOMMIT=1 开启自动提交

MySQL事务ACID实现#

在 MySQL 中,分析下 InnoDB 存储引擎中 ACID 的实现。

  1. 隔离性

隔离性是通过不同锁机制、MVCC(多版本并发控制)来实现事务间的隔离,实现安全并发。MVCC 解决了不可重复读,或者实现了可重复读。还有的使用快照或结合快照。

MySQL 中有 4 种隔离级别,分别是 READ UNCOMMITTED(读未提交)、READ COMMITTED(读已提交)、REPEATABLE READ(可重复读)、SERIALIZABLE(串行化)。

事务隔离级别是要解决什么问题?

  1. 脏读

    脏读是指读到了其他事务未提交的数据。未提交的数据意味着数据可能会回滚,也就是最终可能不会存到数据库里,不存在的数据,这就是脏读。

  2. 可重复读

    在一个事务内,最开始读到的数据,和事务结束前任意时候读到的同一批数据是一致的。这个通常针对数据更新操作。

  3. 不可重复读

    与上面的可重复读形成对比,在同一事务内,不同的时刻读到的同一批数据可能不一样,因为这批数据可能会受到其它事务的影响。比如事务更改了这批数据并提交了。这个通常针对更新操作。

  4. 幻读

    幻读是针对数据插入操作。

    比如有 2 个事务 A 和 B,事务 A 对一批数据作了更改,但是未提交,此时事务 B 插入了与事务 A 更改前的数据记录相同的记录,并且事务 B 先于 事务 A 提交了,此时,在事务 A 中查询,会发现刚刚更改的数据未起作用,看起来没有修改过,但真实情况是,这是事务 B 插入进来的数据,这种情况感觉让用户产生了幻觉,这就是幻读。

事务隔离就是为解决上面提到的脏读、不可重复读、幻读这 3 个问题, 下表对 4 种隔离级别对解决这 3 个问题,可以和不可以:

隔离级别脏读不可重复读幻读读未提交可能可能可能读已提交不可能可能可能可重复读不可能不可能可能串行化不可能不可能不可能

上图说明:

可能 - 可能出现问题。比如 读未提交 隔离级别可能出现脏读问题。

不可能 - 表示不可能出现问题。比如 读已提交 隔离级别不可能出现脏读问题。

串行化的隔离级别最高,可以解决所有这 3 个问题 - 脏读、不可重复读、幻读。其它隔离级别只能解决部分问题,甚至有的隔离级别都不能解决。

为下文作准备:InnoDB 存储引擎提供了两种事务日志:redo log(重做日志)和 undo log(回滚日志)。MySQL Server 提供了 binlog日志。

  1. 原子性

undo log 保证事务的原子性。它记录了事务开始前需要回滚的一些信息。事务失败需要回滚时,可以从 undo log 日志撤销已经执行的 SQL,回滚就是一个反向操作,事务提交是正向操作 ->,回滚就是反向操作 <-。

  1. 持久性

持久性就是事务操作最终要持久化到数据库中,持久性是由 内存+redo log 来保证的,MySQL 的 InnoDB 存储引擎,在修改数据的时候,会同时在内存和 redo log 中记录这次操作,宕机的时候可以从 redo log 中恢复数据。

redo 日志记录了事务 commit 后的数据,用来恢复未写入 data file 的已成功事务更新的数据。

如果上面原子性、隔离性、持久性都实现了,那么一致性也就实现了。

一个问题:redo log 和 undo log 区别是什么?

  • undo log 记录了事务开始前的数据状态,记录的是更新之前的值

  • redo log 记录了事务完成后的数据状态,记录的是更新之后的值

事务提交之前发生了崩溃,重启后会通过 undo log 回滚事务;事务提交之后发生了崩溃,重启后会通过 redo log 恢复事务。

来看张图:

上面就是对 MySQL 中 AID 的实现原理简单介绍分析。


事务理论及其实现方案百度资源分享的评论 (共 条)

分享到微博请遵守国家法律