Mysql事务
- 事务概念
- 事务特性
- 事务并发
- 事务隔离级别
- MVCC多版本并发控制
事务概念
小钢同学今天发工资了,赶紧打开招商银行app看看工资到账了没有,查看余额300
嗯,今天心情好,给对象转账50大元买lv包包去,最后的结果肯定是小钢同学的钱包余额是250,小钢同学对象钱包余额+50
转账的整个过程在mysql中涉及到一系列动作,下面仔细来看看
现在来考虑一下这个问题,如果在第6步的时候更新数据库失败了会发生什么情况?
- 小钢同学的钱包余额为250元
- 小钢同学对象的钱包余额为0元
转账的50元不翼而飞了?小钢百思不得其解,为此还和对象吵了一架,明明钱给转过去了呀,对象还说没收到?
还好小钢是个技术高超的程序员大佬,趁着和对象冷战期去研究了一下数据库事务,发现事务可以完美解决这个问题,于是赶紧连夜给平台提了bug
数据库中的「事务(Transaction)」:让多个数据库操作要么全部成功,要么全部失败,不允许出现中间态数据,例如上面情况,如果第6步更新数据库失败,事务会负责回滚,小钢转出去的50元会被继续增加回去,小钢余额还是拥有300元
事务特性
首先得注意的是并不是所有的mysql存储引擎都支持事务,常见的Innodb是支持事务的,而MyISAM则不支持事物,所以mysql默认的存储引擎是Innodb
事务有以下四大特性:
- 原子性:所有操作要么全部成功,要么全部失败,不允许出现中间态
比如文章开头所说的小钢转账问题,如果中间任何一个步骤出现问题,事务需要回滚到最初始状态,就好比没发生这件事情一样
- 一致性:数据库从一个一致性的状态转移到另一个一致性的状态,满足完整性约束
小钢给对象转账之前,小钢余额300元,对象余额0元,总和300元;
小钢给对象转账50元以后,事务提交完成,小钢余额250,对象余额50元,总和300元
转账前和转账后总和不变,这就是一致性
- 持久性:事务一旦提交,数据的改变就是持久性的,就算磁盘宕机恢复也还是修改后的数据
事物提交后,小钢余额250,对象余额50元,这个数据是一个持久性的状态,除非再次修改
- 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致
事务并发
mysql事务核心在于隔离性,本文重点讲解一下隔离性
首先还是谈一下事务并发会带来什么问题?
- 脏读:如果一个事务「读到」了另一个「未提交事务修改过的数据」,就意味着发生了「脏读」现象
事务A和事务B同时开始执行,事务A先读取小钢余额是300元,事务B紧接着将小钢余额更新为250元,此时事务B没有提交事务,事务A在这个时候再次查询小钢余额发现变成了250元,此时事务B提交事务失败回滚了,小钢余额变为了初始300元,事务A却还认为小钢余额为250元,此时事务A读到的250元数据就是脏数据
- 不可重复读:在一个事务内多次读取同一个数据,如果出现前后两次读到的数据不一样的情况,就意味着发生了「不可重复读」现象
事务A和事务B同时开始执行,事务A读取小钢余额300元,然后先去干其他事情了,事务B读取小钢余额300元并更新为250元,然后成功提交事务,此时事务A再次读取小钢余额为250元,在同一次事务执行中读取数据不一致,这就是不可重复读问题
- 幻读:在一个事务内多次查询某个符合查询条件的「记录数量」,如果出现前后两次查询到的记录数量不一样的情况,就意味着发生了「幻读」现象
事务A和事务B同时执行,事务A负责将所有工资等于100元的员工工资加薪到200元,比如此时有5个员工需要加薪,事务A完成了更新操作,但是还未提交事务,事务B此时新增一个新员工的数据工资为100元,然后提交事务,然后这个时候事务A再次查询工资等于100元的员工,发现变成了6个员工,就好像发生了幻觉一样,这就是幻读
事务隔离级别
事务四种隔离级别
- 未提交读:一个事务未提交时,他所做的变更可以被其他事务看到
会发生脏读、不可重复读、幻读
- 已提交读:一个事务提交以后,他所做的变更才能被其他事务看到
会发生不可重复读、幻读
- 可重复读:事务开启以后,直到提交事务这个时间段,所能看到的数据就是事务开启时的数据,其他事务变更记录看不到
会发生幻读
- 可串行化:对事务加锁,如果发生了读写冲突,会等拿到锁的事务提交以后才会继续执行其他事务
可以解决所有事务并发问题
那么可能会有一个问题,为什么可串行化可以解决所有事务并发问题,为什么数据库不用可串行化隔离级别?
原因:可串行化需要加锁,太慢了,对于优秀的开源系统怎能容忍这种效率问题
mysql默认的隔离级别是可重复读,虽然有幻读的风险,但是概率并不大
MVCC多版本并发控制
mysql的可重复读就是巧妙的借用了MVCC多版本并发控制的思想
- 针对快照读普通查询(select * from table_name):通过 MVCC 方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的,所以就很好的避免幻读问题
- 针对当前读(select … for update 等语句):通过 next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行 select … for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题
快照读是如何使用MVCC多版本控制思想的呢?原理是什么?
待更新…