说到MySQL事务,大家更多就是知道增删改查。以及事务就是开启事务,提交或者回滚事务,其他的一概模糊,可能大家更多的是停留在应用层面。说到MySQL的事务隔离级别,小马特意翻阅了一些网上教程,诸如菜鸟教程,发现并无相关的介绍。还是整理一下吧,毕竟一般也是面试时候的必考题。
什么是事务
什么是事务?小马曾经就有被问到这样的问题。
1、在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。
2、事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
3、事务用来管理 insert,update,delete 语句。
一般来说,事务是必须满足4个条件(ACID)::原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。
原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
彩蛋来了,一般情况下我们对MySQL执行比如update和insert,delete语句,但是直接执行完就可以了,那么你可知道这里其实MySQL有一步自动commit的操作,像开启事务一样的,只不过这里是隐式的提交,这个是否自动提交的值其实是可以设置的。也就是如果你把它设置为非自动提交,那么像你平时执行完update后还要执行commit。教程是这么说的:
在 MySQL 命令行的默认设置下,事务都是自动提交的,即执行 SQL 语句后就会马上执行 COMMIT 操作。因此要显式地开启一个事务务须使用命令 BEGIN 或 START TRANSACTION,或者执行命令 SET AUTOCOMMIT=0,用来禁止使用当前会话的自动提交。
MySQL事务隔离级别
谈到事务的ACID四个条件之一隔离性,什么是隔离性,可以通俗理解为两个事务之间互相互不干扰。其实它也是有级别的,那就是事务隔离级别。注意,敲黑板,这是考点。
事务的隔离级别有四个:未提交读(read uncommitted)、已提交读(read committed)、可重复读(repeatable read)、串行化(serializable)。也可以记为读未提交、读已提交、可重复读、串行化,更好理解和记忆。
未提交读:A事务已执行,但未提交;B事务查询到A事务的更新后数据;A事务回滚;也就是B读到了A的未提交数据,然后A最后还不提交直接回滚了,导致B最后取到的数据为脏数据,也叫作脏读。
已提交读:A事务执行更新;B事务查询;A事务又执行更新;B事务再次查询时,前后两次数据不一致;也就是B读到了A未提交期间的数据,然后A改了数据,B又去读数据,最后A提交了修改,B又去读了数据。导致B多次获取到的数据和最后A提交的数据都是不同的,这个也叫不可重复读。
可重复读:A事务无论执行多少次,只要不提交,B事务查询值都不变;B事务仅查询B事务开始时那一瞬间的数据快照;也就是只要A事务还没提交,B每次读,都是B事务开始时的快照数据。类似于改操作时对相应的数据行加了行级锁。但试想一个问题,假设你在更改数据,读出1000条数据,要把性别字段由原来的A代表男B代表女改为1代表男2代表女,在你开启B事务执行期间,A事务对表新增一条数据,还是用A代表男的数值。那么最后你提交完B事务,还是会存在一条未修改过来的数据。这也叫做幻读。
串行化:不允许读写并发操作,写执行时,读必须等待,读写数据都会锁住整个索引范围的数据,可以理解为类似锁表;这样就不会幻读了,但是对并发的支持不友好。
MYSQL默认的事务隔离级别为可重复读(repeatable read)。隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。借用一张图便于记忆。
各级别考可能导致的问题
Redis事务就没有隔离级别的概念,批量操作在发送EXEC命令前被放入队列缓存,并不会被实际执行(本身事务执行就不会互相干扰),也就是在事务提交之前不会被实际执行,也就不存在“事务内的查询要看到事务里的更新,事务外查询不能看到”这个头疼的问题。也就是它是提交后命令打包执行的,不存在事务中更新了一半未提交被别人读取值的情况。
mvcc在事务隔离级别中的作用
仅InnoDB支持mvcc。应对高并发事务, MVCC比单纯的加锁更高效;MVCC只在READ COMMITTED和REPEATABLE READ两个隔离级别下工作;MVCC可以使用乐观(optimistic)锁和悲观(pessimistic)锁来实现;各数据库中MVCC实现并不统一。
MySQL大部分的事务型存储引擎都不是简单的行级锁,基于提升性能的角度考虑,一般都会实现多版本并发控制(mvcc)。
mvcc的实现,是根据数据在某个时间点的快照来实现的,也就是说,一个事务,不管执行多长时间,该事务从头到尾看到的数据都是一致的。根据事务的开始时间不同,每个事务在同一时刻看到的数据可能是不一致的。
InnoDB的MVCC,是通过在每行后面加两个隐藏列来实现的(分别保存行的创建时间和过期时间),当然里面存的并不是真正意义的时间,而是系统版本号。每开始一个新的事务,系统版本号都会递增,事务开始时间的版本号会作为当前事务的版本号。