什么是事务?
数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由开始和结束之间执行的全部数据库操作组成。
这是百科的定义,也是我们在面试的时候最常回答的关键字。
可以这么说:事务是数据库执行过程的一个逻辑单位,由一个有限的数据库操作序列组成。
举例来说,当我们购物下订单时,有这么两个操作(当然不止这俩):付款,减库存。这两个操作序列就是一个事务,他不能拆分执行,必须同时成功和失败。
当我们面试时最常遇到的问题是什么?
或者不那么世俗的说——毕竟我们学习并不全是为了面试嘛,啊呸!不面试谁看那么多,脑子内存本来就不大。
小伙手撕MySQL事务,发生了什么?
关于ACID抢眼回答:
数据库的每一个操作其实是一条日志。
原子性,在 InnoDB 里面是通过 undo log 来实现的,它记录了数据修改之前的值(逻辑日志),一旦发生异常,就可以用 undo log 来实现回滚操作。
持久性怎么实现呢?数据库崩溃恢复(crash-safe)是通过什么实现的?
是通过 redo log 和 double write 双写缓冲来实现的,我们操作数据的时候,会先写到内存的buffer-pool中,同时记录 redo log,如果在刷盘之前出现异常,在重启后就可以读取 redo log 的内容,写入到磁盘,保证数据的持久性。
当然,恢复成功的前提是数据页本身没有被破坏,是完整的,如果数据页本身损坏了,这个可以通过双写缓冲 (double write)保证。
说了这么多事务,你甚至可能在实际编程中并没有手动操作过事务,那么如何手动开启和关闭事务呢?
Mysql数据库默认是开启自动提交事务的,也就是说当你执行update/delete/insert操作时数据库引擎会自动开启一个事务,并且在操作完成后自动提交事务。
你可能会说,事务不是有隔离性吗,为什么还有这么多问题?好了继续往下看
事务操作时并不隔离其他的事务,比如以下操作:
小伙手撕MySQL事务,发生了什么?
请点击输入图片描述
而如果此时事务1 回滚了事务,事务2就读到了脏数据;
这种隔离机制在数据库实际使用中,显然不合适,但有助于理解其他事务。
事务只能读取其他事务提交之后的数据,我们来模拟以下这个过程会出现的问题:
小伙手撕MySQL事务,发生了什么?
可以看到,这种隔离级别会导致数据在一个事务两次读取的数据不一致,也就是不可重复读;
这也是**最常用的事务隔离机制,他可以保证在一个事务里读取的数据是一样的,也就是数据的可重复读。**你可能会问,数据库是如何实现可重复读的?这个问题会在之后解答,先来看看什么是幻读。
一个事务前后两次读取数据数据不一致,是由于其他事务插入数据造成的,这种情况我们把它叫做幻读。
比如:
小伙手撕MySQL事务,发生了什么?
第二次查询我们发现多了一条数据,这就叫幻读。需要记住的是只有其他事务插入导致的数据不一致才叫幻读。
所以你会说,既然我们比较常用的是RR(可重复读)的隔离级别会有幻读的情况,那为什么还经常使用它?其实在MySQL的InnoDB引擎使用RR的隔离级别但配合锁的机制已经避免了幻读情况。
我们先来看一下MySQL是如何实现数据的可重复的。
有两种方案。
MVCC的核心思想是:可以查到在当前事务开始之前已经存在的数据,即使它在后面被其他事务修改或者删除了。而当前事务之后新增的数据,当前事务是查不到的。
那么问题来了,如何保证当前事务数据的一致性呢?也就是说怎么保证数据被其他事务修改和删除或者新增了数据,而当前事务并不受影响呢?
读取数据事务开始的时候,MySQL为事务创建了快照,也就是在事务内查询的数据都是快照版本,这样就可以保证数据的一致性。
那么快照又是如何实现的呢?
InnoDB 为每行记录都实现了两个隐藏字段:
我们把这两个事务ID理解为版本号。
从插入数据开始,我们来看一下MySQL如何用这两个版本号来隔离事务。
小伙手撕MySQL事务,发生了什么?
此时又有一个事务进来,增加了一条数据并提交结束。
小伙手撕MySQL事务,发生了什么?
MVCC 的查找规则1:只能查找创建时间小于等于当前事务 ID 的数据
小伙手撕MySQL事务,发生了什么?
再次回到事务2查询
小伙手撕MySQL事务,发生了什么?
MVCC 的查找规则2: 能查找删除时间大于当前事务id的数据,也就是在事务之后删除的数据在当前事务依然能查得到。
事务5,尝试修改数据
小伙手撕MySQL事务,发生了什么?
此时回到事务2再次查询数据
小伙手撕MySQL事务,发生了什么?
按照查找规则:只能查找创建时间小于等于当前事务 ID 的数据,和删除时间大于当前事 务 ID 的行(或未删除)。
解释:id为1的数据删除版本大于当前事务2,创建版本小于事务2,依然可以查到,
id为2的数据创建版本为1的数据,小于事务2,删除版本大于事务2,可以查到
id为3的数据创建版本为3大于事务2,查不到;
id为2的数据创建版本为5的数据,大于事务2,依然查不到;
所以,在事务2内,无论外部发生了什么翻天覆地的变化,事务2自始自终查到的都是自己的快照数据,保证了数据的一致性,一定要理解MVCC的核心思想。
之后会更新结合锁机制,InnoDB是如何在RR级别解决幻读的。