MySQL——事务和隔离级别

什么是事务?

事务就是一组原子性的SQL查询(要么都执行,要么都不执行),或者说是一个独立的工作单元。

为什么需要事务?

银行应用是解释事务必要性的一个经典例子。

假设一个银行的数据库有两张表:支票表和储蓄表。现在要从用户Jane的支票账户转移200美元到他的储蓄账户,那么至少需要几个步骤:

以上六个步骤必须打包到一个事务中,任何一个步骤失败,都必须回滚所有步骤

否则万在执行第三步之后服务器忽然掉电了,从支票表的账户扣了200万,但是钱并没有到储蓄表的账户,因此要解决这种问题,就要保证转账业务里的所有数据库的操作是不可分割的,要么全部执行成功,要么全部失败,不允许出现中间状态的数据。

数据库中事务就能达到这样的效果:

我们在转账前先开启事务,等所有数据操作执行完成后,才提交事务,对已经提交的事务来说,该事物对数据库所做的修改将永久生效,如果中途发生中断或错误,那么改事务期间对数据库所做的修改将会被回滚到没执行该事物之前的状态

事务特性有什么?(ACID)

事务是由MySQL的引擎来实现的,我们常见的InnoDB引擎是支持事务的。

**原子性(Atomicity):**一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节,而且事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样,就好比买一件商品,购买成功时,则给商家付了钱,商品到手;购买失败时,则商品在商家手中,消费者的钱也没有花出去。

**一致性(Consistency):**是指事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。比如,用户A和用户B在银行分别有800元和600元,总共1400元,用户A给用户B转账200,分别为两个步骤,从A的账户扣除200元和对B的账户增加200元。一致性就是要求上述操作步骤后,最后的结果是用户A还有600元,用户B还有800元,总计1400元,而不会出现用户A扣除了200元,但是用户B未增加的情况。

**隔离性(lsolation):**数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据不一致,因为多个事务同时使用相同的数据时,不会相互干扰,每个事务都有一个完整的数据空间,对其他并发事务是隔离的,也就是说,消费者购买商品这个事务,是不影响其他消费者购买的。

**持久性(Durability):**事务处理结束后,对数据的修改就是永久的,即使系统故障也不会丢失。

InnoDB引擎通过什么技术来保证事务这四个特性的呢?

持久性是通过redo log(重做日志)来保证的;

原子性是通过undo log(回滚日志)来保证的;

隔离性是通过MVCC(多版本并发控制)或锁机制来保证的;

一致性是通过持久性+原子性+隔离性来保证的

为什么事务要有隔离性,我们就要先知道并发事务时会引发什么问题?

在同时处理多个事务的时候,就可能出现脏读、不可重复读、幻读的问题

**脏读:**一个事务读到了另一个未提交事务修改过的数据,就意味着发生了【脏读】现象

假设一个银行转账的场景:

时间 事务A(转账) 事务B(查账) 余额
T1 开始事务 1000
T2 将余额改为 800(未提交) 800(脏数据)
T3 读取余额 → 读到 800
T4 发生异常,回滚事务 1000(恢复)
T5 基于 800 做业务决策(错误!)

**不可重复读:**在一个事务内多次读取到同一个数据,如果出现前后两次读到的数据不一样的情况,就意味着发生了【不可重复读】现象

时间 事务A(统计) 事务B(修改订单) 订单金额
T1 开始事务 100
T2 读取订单金额 → 100
T3 开始事务
T4 将金额改为 200
T5 提交事务 200
T6 再次读取订单金额 → 200

**注意:**记录的数量和记录集合没有变但是同一条记录的值不一致

**幻读:**在一个事务内多次查询某个符合条件的记录数量,如果出现前后两次查询到的记录数量不一样的情况,就意味着发生了【幻读】现象

时间 事务A 事务B 表中的行
T1 开始事务 1, 2
T2 查询 id BETWEEN 1 AND 3 → 返回 (1,2)
T3 开始事务
T4 插入 id=3 并提交 1, 2, 3
T5 再次查询 id BETWEEN 1 AND 3 → 返回 (1,2,3)

**注意:**幻读记录的数量或记录集合变了

事务的隔离级别有哪些?

脏读:读到其他事务未提交的数据

不可重复读:前后读取的数据不一致

幻读:前后读取的记录数量不一致

SQL标准提出四种隔离级别来规避这些现象,隔离级别越高,性能效率就越低,这四个隔离级别如下:

读未提交(read uncomitted,RU),指一个事务还没提交时,它做的变更就能被其他事务看到;允许脏读、不可重复读、幻读,读时不加锁,直接读最新数据,性能最高,但是几乎不用(数据可能完全错误)

**读提交(read comitted,RC),**指一个事务提交之后,它做的变更才能被其他事务看到;

**可重复读(repeatable read,RR),**指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB引擎的默认隔离级别;

**串行化(serializable,SL),**会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,访问的事务必须等前一个事务执行完成,才能继续执行;

MySQL在【可重复读】隔离级别下,可以很大程度上避免幻读现象的发生,所以MySQL并不会使用【串行化】隔离级别,因为使用【串行化】隔离级别会影响性能

分析一下每个阶段看到的是余额是多少?

在【读未提交】隔离级别下:事务B修改余额后,虽然没有提交事务,但是此时的余额已经可以被事务A看到了,于是事务A中余额V1是200,V2是200,余额V3是200

在【读提交】隔离级别下:事务B修改余额后,只有在事务B提交事务后,才能被事务A看到,于是事务A中,余额V1是100,V2是200,余额V3是200

在【可重复读】隔离级别下:事务A只能看到启动事务时的数据,在提交事务之后才可以看到新数据,所以在初始和提交状态之间,状态都是一致的,余额V1是100,V2是100,余额V3是200

在【串行化】隔离级别下:因为事务A在读操作,事务B无法进行写操作,在事务A提交之后才可以进行事务B的写操作,余额V1是100,V2是100,余额V3是200

四种隔离级别具体如何实现的呢?

对于【读未提交】隔离级别的事务来说,因为可以读到未提交事务修改的数据,所以直接读取最新的数据即可

对于【读提交】和【可重复读】隔离级别的事务来说,它们是通过Read View来实现的,它们的区别在于创建Read view的时机不同,大家可以把Read View理解成一个数据快照,就像相机拍照那样,定格某一个时刻的风景。【读提交】隔离级别是在每个语句执行前都会生成一个Read view,而【可重复读】隔离级别是启动事务时生成一个Read View,然后整个事务期间都在用这个Read view

对于【串行化】隔离级别的事务来说,通过加读写锁的方式来避免并行访问

为什么隔离级别是读未提交,读已提交,可重复读,串行化,不是其它的什么?

相关推荐
DeboPXK1 小时前
NSK VH25EM 高防尘法兰型导轨技术手册
服务器·网络·数据库·经验分享·规格说明书
小挪号底迪滴2 小时前
Redis 和 MySQL 数据不一致怎么办?缓存更新策略实战
redis·mysql·缓存
the sun342 小时前
数据库中间件 ShardingSphere的安装与连通性配置
mysql
翼龙云_cloud2 小时前
阿里云国际代理商:如何使用RDS MySQL 构建网站数据库?
数据库·mysql·阿里云
程序猿乐锅2 小时前
【 苍穹外卖day03 | 菜品管理 】
java·开发语言·数据库·mysql
hughnz2 小时前
贝克休斯WellLink Assurance vs 帕特森-UTI REX:钻井报警系统的两条技术路线之争
大数据·数据库·人工智能
闪电悠米2 小时前
黑马点评-Redis ZSet-实现关注 Feed 流
服务器·网络·数据库·redis·缓存·junit·lua
天疆说2 小时前
在 Ubuntu 24.04 上安装 MATLAB R2021b
数据库·ubuntu·matlab
码云数智-大飞2 小时前
Go Channel 详解:并发通信的正确姿势
前端·数据库·git