Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > Mysql实现事务及扩展知识

Mysql是如何实现事务的以及相关扩展知识总结

作者:小海獭敲代码_KD

在mysql中事务是一种机制、一个操作序列,是访问和更新数据库的程序执行单元,下面这篇文章主要介绍了Mysql是如何实现事务的以及相关扩展知识总结的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

最近在准备八股,整理了一些笔记,希望可以帮助到大家,有错误也希望大家指出来,大家相互学习。

为什么要有这四个东西?

数据库事务要解决的核心矛盾是:

如果只有一个最简单的办法:每次事务都直接改磁盘数据。那么:

于是四个组件进行分工协作:

扩展知识

锁的类型

Redo Log(重做日志)的工作原理

InnoDB修改数据时不会直接写到磁盘上的数据页,那样随机I/O太多,性能太差。他用的WAL策略:先把修改内容顺序追加到redo log(这是顺序写极快),后台再找机会把数据页刷到磁盘。

redo log 采用循环写的方式,有两个指针:write pos 表示当前写到哪了,checkpoint表示已经刷盘的位置。两个指针之间就是待刷盘的脏数据

+---+---+---+---+
| 0 | 1 | 2 | 3 |   redo log 文件组
+---+---+---+---+
    ^       ^
    |       |
checkpoint  write_pos

事务提交时,redo log必须落盘,这个行为由innodb_flush_log_at_trx_commit控制:

Undo Log(回滚日志)与版本链

每条数据都有两个隐藏字段:trx_id记录最后修改这条数据的事务ID,roll_pointer指向undolog里的上一个版本。多次修改就会形成一条版本链。 假设原始数据name='张三',事务100改成'李四',事务200又改成'王五':

当前数据页:name='王五', trx_id=200, roll_pointer →
    undo log:name='李四', trx_id=100, roll_pointer →
        undo log:name='张三', trx_id=0, roll_pointer=null

MVCC读数据时,根据事务的ReadView沿着版本链往回找,找到第一个自己能看见的版本。可重复读隔离级别下;ReadView在事务开始时生成一次,后面一直用这个。读已提交隔离级别下,每次查询都生成新的ReadView.

锁的实现细节

InnoDB的行锁实际上是加在索引上的,不是加在数据行上的。如果SQL没走索引,就会锁全表。

-- 假设 id 是主键,name 无索引
begin;
update user set age = 20 where id = 1;     -- 只锁 id=1 这一行
update user set age = 20 where name = '张三'; -- 锁全表!
commit;

InnoDB有三种行锁:

可重复读隔离级别下默认用Next-Key Lock,就是为了防幻读

锁类型锁定对象包含记录本身?主要目的
Record Lock单个索引记录防止修改/删除
Gap Lock索引之间的间隙防止插入(解决幻读)
Next-Key Lock记录 + 记录前的间隙结合两者优点,默认锁定方式

面试官追问

MVCC能解决幻读吗?

回答:能解决快照读的幻读,解决不了当前读的幻读。快照读就是普通的select,走MVCC版本链,事务开始时的ReadView定死了能看到哪些数据,后面别人插入的新行看不见。但当前读比如 select...for update,读的是最新数据,这时候要靠间隙锁来防幻读,锁住查询范围内的间隙不让别人插入。

1. 快照读(Snapshot Read)

什么是快照读? 就是最普通的 SELECT * FROM table WHERE ...。它不加任何锁,读取的是ReadView里的历史版本,极大地提高了查询性能.

2. 当前读(Current Read)

什么是当前读? 当你需要读取“最新版本”的数据,或者准备更新数据时,就是当前读。常见的语句有:

3. 最容易搞混的“幻读”场景(面试高频)

虽然上面说 MVCC 解决了快照读的幻读,但有一种特殊操作会让 MVCC “破功”,让你看到幻象:

场景演示:

  1. 事务 A:先用“快照读”查 id=5 的记录,结果是不存在的。

  2. 事务 B:插了一条 id=5 的记录并提交

  3. 事务 A:如果此时再 SELECT,依然查不到(快照保护)。但是,如果事务 A 此时执行 UPDATE table SET name='new' WHERE id=5,这个 UPDATE 是“当前读”,它能看到事务 B 提交的那行。

  4. 结果UPDATE 成功了,事务 A 发现自己居然更新了一条“不存在”的记录!此时再 SELECT,id=5 的记录就变出来了。

这就是所谓的“快照读下的幻读”: 因为你的事务里发生了一次“写操作”(当前读),强行更新了版本号,导致那条新纪录在你的 ReadView 里变现了。

undo log 什么时候会被清理?

回答:undolog不是事务提交就能删的,要等没有事务需要用到这些旧版本了才能清理。purge线程会扫描系统里活跃的事务,找到最老的ReadView,比它还老的undolog才能删除。所以长事务会导致undo log堆积,这也是为什么要避免长事务。

为什么redo log 要用循环写而不是一直追加?

回答:redolog的作用是崩溃恢复,checkpoint之前的日志对应的数据页已经刷盘了,这部分日志就没用了,可以覆盖掉复用空间。一直追加的话磁盘会被撑爆。而且redolog大小固定有个好处,连续的磁盘空间顺序写性能更好。binlog是一直追加的,因为它要用于主从复制和数据恢复,历史记录不能丢。

总结 

到此这篇关于Mysql是如何实现事务的以及相关扩展知识总结的文章就介绍到这了,更多相关Mysql实现事务及扩展知识内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文