MySQL 事务以及 MVCC 机制(一)

0 阅读4分钟

MySQL 事务以及 MVCC 机制一


一、事务及其 ACID 属性

事务是由一组 SQL 语句组成的逻辑处理单元,事务具有以下 4 个属性,通常简称为事务的 ACID 属性

1. 原子性(Atomicity)

事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。

由 undo log 控制。

2. 一致性(Consistency)

在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性。

一致性由其他三个特性共同保证。

3. 隔离性(Isolation)

数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的"独立"环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。

事务隔离级别
隔离级别说明解决的问题
Read Uncommitted一个事务查询时,可以读到另一个未提交事务的值
Read Committed (RC)只能读事务已提交的数据,只要 commit 就能查询到解决脏读
Repeatable Read (RR)执行第一条 SQL 时会把整个库当时的数据生成快照,后续操作不受其他事务影响解决脏读、部分幻读
Serializable事务串行排队处理,性能最低解决脏读、幻读、不可重复读

Read Uncommitted:一个事务查询时,可以查到另一个未提交事务的值。俗称脏读

Read Committed:只能读事务已经提交的数据,只要 commit 就能查询到,导致查同一个数据可能值都不一样,俗称不可重复读

Repeatable Read:执行第一条 SQL 时会把整个库当时的数据生成快照,后续操作不受其他事务影响,俗称可重复读

它解决了幻读吗?解决了部分

  • 查询操作:已解决
  • 涉及修改操作(如另一个事务把张三改成 2000,当前事务执行 balance = balance + 1):它读的是数据库最新的数据,并且对于快照之后新加的数据,做查询查不到,但做修改会被修改。那这些数据对于当前事务来说是凭空出现的,这就是幻读

image.png

Serializable:顶级隔离能力,事务串行排队处理,解决了脏读、幻读/不可重复读。

4. 持久性(Durability)

事务完成之后,它对数据的修改是永久性的,即使出现系统故障也能够保持。

image.png


二、事务隔离级别配置

数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上"串行化"进行,这显然与"并发"是矛盾的。同时,不同的应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用对"不可重复读"和"幻读"并不敏感,可能更关心数据并发访问的能力。

查看当前隔离级别:

sql

复制

SHOW VARIABLES LIKE 'tx_isolation';

设置事务隔离级别:

sql

复制

SET tx_isolation = 'REPEATABLE-READ';

注意:  MySQL 默认的事务隔离级别是可重复读(Repeatable Read) 。用 Spring 开发程序时,如果不设置隔离级别,默认使用 MySQL 设置的隔离级别;如果 Spring 设置了就使用已经设置的隔离级别。

image.png

那串行模式是怎么做到一个事务没提交、另一个事务执行时被挂起等待的呢?


三、MVCC 多版本并发控制

3.1 什么是 MVCC

公司用的最多的是 RC(Read Committed)  和 RR(Repeatable Read) ,它们是怎么实现的呢?

MVCC(Multi-Version Concurrency Control) ,即多版本并发控制,就可以做到读写不阻塞,且避免了类似脏读这样的问题,主要通过 undo 日志链来实现。

3.2 MVCC 原理

我们创建一张表,除了业务字段以外,MySQL 会同步帮我们维护事务 ID 字段回滚指针

image.png

undo log 的工作流程:

  • INSERT 操作:当 insert 一条数据,在 undo log 里会同步生成一个 delete 回滚记录,这时候回滚指针指向的就是这条记录。当 insert 出现异常,就是通过执行这条回滚记录来删除数据的。
  • UPDATE 操作:当一条数据 commit 后,另一个 update 事务进来,这个时候 undo 日志会生成一条新的日志,回滚指针指向的是上一条 commit 的数据。

MySQL 通过这种日志版本链实现多版本并发控制机制,实现了读写不阻塞


四、事务优化实践原则

  1. 将查询等数据准备操作放到事务外
  2. 事务中避免远程调用,远程调用要设置超时,防止事务等待时间太久
  3. 事务中避免一次性处理太多数据,可以拆分成多个事务分次处理
  4. 更新等涉及加锁的操作尽可能放在事务靠后的位置
  5. 能异步处理的尽量异步处理
  6. 应用侧(业务代码)保证数据一致性,非事务执行