我们操作 Mysql 数据的时候,都是把数据页加载到 Buffer Pool 中才可以访问,但是事务是需要具有持久性的,如果我们只再内存的Buffer Pool中修改了页面,假设在事务提交后突然发生了某个故障,导致内存中的数据都失效了,那么这个已经提交了的事务对数据库中所做的更改也就跟着丢失了。为了保证事务的持久性,我们需要在事务提交完成之前把该事务所修改的所有页面都刷新到磁盘。
但是会面临一些问题:
我们只是想让已经提交了的事务对数据库中数据所做的修改永久生效,即使后来系统崩溃,在重启后也能把这种修改恢复出来。所以我们其实没有必要在每次事务提交时就把该事务在内存中修改过的全部页面刷新到磁盘,只需要把修改了哪些东西记录一下就好。
当系统崩溃后,重启只需要按照记录的内容更新数据页,那么事务对数据库做的修改又可以被恢复出来。这种被记录的内容被称为redo log(重做日志)
。使用 redo log 的优点:
InnoDB为了解决磁盘速度过慢的问题而引入了Buffer Pool。
同理,写入redo日志时也不能直接直接写到磁盘上,实际上在服务器启动时就向操作系统申请了一大片称之为redo log buffer
的连续内存空间。
我们可以通过启动参数innodb_log_buffer_size
来指定log buffer的大小,在MySQL 5.7.21这个版本中,该启动参数的默认值为16MB
。
事务运行过程中产生的一组redo日志在mtr结束时会被复制到 log buffer 中,这些日志页不会一直存在内存中,在一些情况下它们会被刷新到磁盘里,比如:
log buffer
空间不足时: 如果当前写入 log buffer 的redo日志量已经占满了 log buffer 总容量的大约一半左右,就需要把这些日志刷新到磁盘上。Buffer Pool
页面刷新到磁盘,但是为了保证持久性,必须要把修改这些页面对应的redo log
刷新到磁盘。log buffer
中的 redo log 到磁盘。redo log 只是为了系统崩溃后恢复脏页用的,如果对应的脏页已经刷新到了磁盘(flush 链表中的更改已经落到了磁盘上),也就是说即使现在系统崩溃,那么在重启后也用不着使用redo日志恢复该页面了,所以该redo 日志也就没有存在的必要了,那么它占用的磁盘空间就可以被后续的redo日志所重用。也就是说:判断某些redo日志占用的磁盘空间是否可以覆盖的依据就是它对应的脏页是否已经刷新到磁盘里。
Innodb 有个一全局变量checkpoint_lsn(checkpoint log sequence number)
来代表当前系统中可以被覆盖的redo日志总量是多少。假如某个页a被刷新到了磁盘,mtr_1生成的redo日志就可以被覆盖了,所以我们可以进行一个增加checkpoint_lsn
的操作,我们把这个过程称之为做一次checkpoint。
checkpoint 操作也会释放 log buffer 的空间,因为 log buffer 的容量是有限的,所以 Innodb 采用循环记录的方式。check point 会释放出已经刷新到磁盘的 redo log, 释放出空间。不用去细想checkpoint是怎么操作的,只需要知道经过checkpoint后,log buffer 会释放出已经刷新到磁盘的 redo日志空间
前边说为了保证事务的持久性,用户线程在事务提交时需要将该事务执行过程中产生的所有redo日志都刷新到磁盘上。这一条要求太狠了,会很明显的降低数据库性能。如果有的同学对事务的持久性要求不是那么强烈的话,可以选择修改一个称为innodb_flush_log_at_trx_commit
的系统变量的值,该变量有3个可选的值:
innodb_flush_log_at_trx_commit
的默认值。事务需要保证原子性(要么全部成功要么全部失败),所以当事务执行到一半出现错误或程序手动指定ROLLBACK
语句时,需要将已执行的修改恢复(回滚),这样看起来这个事务就什么都没做,符合原子性的要求。
要满足事务的回滚,当对一条记录做改动时(这里的改动可以指INSERT、DELETE、UPDATE
),都需要把回滚时所需的东西都给记下来。比如:
这些为了回滚而记录的称之为撤销日志(undo log
)。这里需要注意的是select
操作不会产生任何更改,所以并不需要记录对应的 redo log
。
当开启事务的时候,innodb 会为当前事务分配一个唯一的事务id
, 本质上就是一个数字,它的分配策略和行记录的隐藏列row_id
(当用户没有为表创建主键和UNIQUE键时InnoDB自动创建的列)的分配策略大抵相同,具体策略如下:
Max Trx ID
的属性处,这个属性占用8个字节的存储空间Max Trx ID
属性加载到内存中,将该值加上256之后赋值给我们前边提到的全局变量(因为在上次关机时该全局变量的值可能大于Max Trx ID属性值), 这也是保证事务id在重启后不重复的一个方案InnoDB记录行格式的时候:聚簇索引的记录除了会保存完整的用户数据以外,而且还会自动添加名为trx_id、roll_pointer(roll_pointer指向的是 记录所对应的 undo log)的隐藏列,如果用户没有在表中定义主键以及UNIQUE键,还会自动添加一个名为row_id的隐藏列。所以一条记录在页面中的真实结构看起来就是这样的:
其中 roll_pointer 是指向记录对应的 undo log
的一个指针
虽然 insert 操作实际上可能向聚簇索引和所有二级索引都插入一条记录, 但是记录 undo log 只需要记录聚簇索引都情况即可, 因为聚簇索引和二级索引是一一对应的, 在回滚插入操作的时候, 知道了主键信息就可以根据主键把所有二级索引删除
在 insert undo log 中就只记录了表id、主键长度、主键值
PAGE_FREE
的指针指向被删除记录链表的头节点
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有