Mysql(三)--事务 & 隔离级别 & MVCC
什么是数据库事务?
数据库事务就是一组数据库操作,要么全部成功要么全部失败。
特性
事务有四个特性(ACID),并且四个特性都必须要满足:(特性其实也就是特点)
原子性(Atomicity):一组操作,要么全部成功、要么全部失败。
一致性(Consistency):事务执行前与执行后数据完整性是一致的,没有收到破坏。
隔离性(Isolation):多个事务操作之间是隔离的,互不干扰。
持久性(Durability):数据变更是永久的。
事务并发带来的问题
如果事务处理是单线程的,那不会有问题,但是多个事务并发执行,就会产生一些问题:
- 脏读:(当前事务)读取了(其他事务)未提交的数据
- 不可重复读:(当前事务)读取了(其他事务)已经提交的数据
- 幻读:(当前事务)读取了(其他事务)新增的数据
为此,InnoDB的四个隔离级别就是为了解决上述的问题:
四个隔离级别为:读未提交、读已提交、可重复读、串行化,映射图如下(这样就很清楚了)
Mysql默认
的事务隔离级别就是:可重复读
事务的实现原理(就是使用什么方式去实现事务的这个四大特性的)
以下都是以InnoDB引擎为例:
持久化:Redo log(重做日志)
redolog会记录已经提交的事务信息
,也就是修改后的数据的信息
Mysql为了提高性能不会每次修改操作都去直接写入磁盘,会先写入内存缓冲区中,后续等系统空闲时候刷盘到磁盘中就行。
但是此时会出现一个问题:如果内存缓冲区中的数据还没落盘,发生了宕机,数据就丢失了,因此引入了Redolog,在提交事务后,会立即持久化写一份Redolog记录,来帮助数据恢复。
原子性:Undo log(回滚日志)
Undo log 是用来记录事务提交之前的数据,也就是记录了修改之前的数据。
当系统发生问题,或者接收到 rollback 命令进行回滚的时候,就使用 Undolog 来帮助恢复数据。
Mysql 锁
有读锁和写锁之分
记录锁、间隙锁、临键锁,共享锁、排他锁,意向锁
MVCC(多版本控制)
MVCC 的核心点在于如何从 UndoLog(回滚日志中)读取正确的历史版本信息
逻辑如下图:
mvcc 只能解决部分幻读:开始查询不到,但是可以修改新记录,然后再查询就查到了(参考:https://xie.infoq.cn/article/7eafabc80c1ab0a8ccc7f1151)
真正解决幻读需要加锁才可以。通过间隙锁可以解决,如下所示:
快照读 和 当前读
快照读【Consistent Read】
也叫普通读,读取的是记录数据的可见版本,不加锁,不加锁的普通select语句都是快照读,即不加锁的非阻塞读。
快照读的执行方式是生成 ReadView,直接利用 MVCC 机制来进行读取,并不会对记录进行加锁。
当前读
也称锁定读【Locking Read】,读取的是记录数据的最新版本,并且需要先获取对应记录的锁。如下语句:
SELECT * FROM student LOCK IN SHARE MODE; # 共享锁
SELECT * FROM student FOR UPDATE; # 排他锁
-- 如果只使用一下语句,而不适用上面的2个语句进行加锁,则会产生幻读问题
INSERT INTO student values ... # 排他锁
DELETE FROM student WHERE ... # 排他锁
UPDATE student SET ... # 排他锁
如何解决幻读?
在 InnoDB RR(可重复读) 模式下:
快照读:使用MVCC可以解决幻读
当前读:使用记录锁
(根据主键或者唯一索引可以定位到具体行时候即为记录锁) or 间隙锁
(无法定位到具体行时候即为间隙锁)就可以解决可以解决幻读。
参考:
事务:https://segmentfault.com/a/1190000044496665#item-8-6