MySQL的缓冲池
InnoDB是基于磁盘的存储的,并将其中的记录按照页的方式进行管理。但是由于CPU速度和磁盘速度之间的鸿沟,基于磁盘的数据库系统通常使用缓冲池技术来提高数据库的整体性能。
缓冲池就是一块内存区域,在数据库进行读取页的操作,先将从磁盘读到的页放在缓冲池中,下次再读到相同页,先判断页是否在缓冲池。如果在,则称页在缓冲池被命中,直接读取该页;否则去读取磁盘的页。对于修改页的修改操作,首先修改缓冲池中的页,然后再以一定的频率刷新到磁盘上。页从缓冲池刷新回磁盘的操作并不是在每次页发生更新时触发,而是通过checkpoint的机制刷新回磁盘。
缓冲池中的数据页类型:索引页、数据页、undo页、插入缓冲insert buffer页、自适应哈希索引、InnoDB存储的锁信息、数据字典信息。
bin log
二进制日志bin log记录了对MySQL执行更改的所有操作,是逻辑格式的日志。(select和show操作是在查询日志,不在二进制日志中)。
InnoDB开启事务的时候,所有未提交的二进制日志会被记录到一个缓存(如果事务过大,就会写到临时文件中)中,等待该事务提交时,将缓冲中的二进制写入二进制文件。
redo log
redo log保证的是数据库的crash-safe能力,采用的策略就是常说的“两阶段提交”。
redo log记录的是物理数据页面的修改的信息(数据库中每个页的修改),面向的是表空间、数据文件、数据页、偏移量等。
一条update的SQL语句执行的流程:
将数据页加载到内存 -> 修改数据 -> 更新数据 -> 写redo log(状态为prepare)-> 写bin log -> 提交事务(数据写入成功后将redo log状态改为commit)
只有当两个日志都提交成功(刷入磁盘),事务才算真正的完成。
一旦发生系统故障(不管是宕机、断电、重启等),都可以配套使用redo log和bin log做数据恢复。
需要注意的是bin log在事务提交时,一次性将事务中所有的SQL语句按照一定的格式记录到bin log,而redo log在事务开始的时候就开始逐步写入redo log buffer。
重做日志缓冲,redo log buffer,重做日志信息会先放入这个区,然后按一定频率刷到重做日志文件。重做日志缓冲默认是8M。
- Master Thread每秒都会将redo log buffer刷回redolog
- 每次事务提交的时候,也会将redo log buffer刷回redolog
- 当redo log buffer空间低于50%,也会将redo log buffer刷回redolog
undo段
事务开始之前,根据当前版本的数据生成undo log;产生undo日志的时候,同样会伴随类似于保护事务持久化机制的redo log的产生。
保存事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制(MVCC)下的读。
MVCC即通过read view和undo log实现的,在MVCC章节重点介绍。
redo log和undo log的不同
锁为事务提供了隔离性。
原子性,一致性,持久性由redo和undo来完成。
redo:重做日志记录了事务的行为,可以更好的通过其进行重做。
事务通过redo日志文件和InnoDB存储引擎的日志缓冲来实现。当开始一个事务时,会记录该事务的LSN日志序列号;
事务执行时,会往日志缓冲里插入事务日志;事务提交时,将日志缓冲写入磁盘。
对于内存缓冲页的修改,先写入重做日志文件,然后在写入磁盘。
undo:事务有时还需要撤销,这就需要undo。
事务或者语句由于某种原因失败了,或者需要rollback请求回滚,那么就需要这些undo信息回滚到修改之前的样子。
与redo不同的是,redo是放在日志文件,undo是放在数据库内部一个特殊段segment中,即undo段(位于共享表空间)。
redo log和bin log的不同
bin log:记录所有与MySQL数据库有关的日志,包括InnoDB,MyISAM等其他存储引擎的日志;其次内容上无论bin log的记录格式是Statement还是Row,又或者是MIXED,bin log记录的都是一个事物的具体操作内容;提交时间,仅在事务提交前进行提交,即只写一次硬盘。
redo log:只记录InnoDB存储引擎的事务日志;redo log记录的是关于每个页Page的更改的物理情况;在事务进行过程中,会不断有redo entry重做日志条目写入到redo log中。
事务恢复如何进行重做redo和回滚undo
checkpoint是为了定期将buffer pool的内容刷新到磁盘。当遇到内存不足、buffer pool已满等情况时,需要将buffer pool中的内容/部分内容(特别是脏数据)转储到磁盘中。在转储时,会记录checkpoint发生的时刻。在故障恢复时候,只需要redo/undo最近的一次checkpoint之后的操作。
InnoDB在进行事务时,重做redo所有的事务,包括未提交的事务和回滚的事务。然后通过undo log回滚那些未提交的事务。
回滚事务undo:将事务更新的所有数据项恢复为日志中的旧值,事务撤销完毕时将插入一条<T abort>记录。
重做事务redo:将事务更新的所有数据项恢复为日志中的新值。
日志的种类和格式:
<T,X,V1,V2>:描述一次数据库写操作,T是执行写操作的事务的唯一标识,X是要写的数据项,V1是数据项的旧值,V2是数据项的新值。
<T,X,V1>:对数据库写操作的撤销操作,将事务T的X数据项恢复为旧值V1。在事务恢复阶段插入。
<T start>: 事务T开始
<T commit>: 事务T提交
<T abort>: 事务T中止
以下事务将进行undo:日志中只包括<T start>记录,但既不包括<T commit>记录也不包括<T abort>记录。
以下事务将进行redo:日志中包括<T start>记录,也包括<T commit>记录或<T abort>记录。
事务终止/正常回滚rollback
- 从后往前扫描日志,对于事务T的每个形如<T,X,V1,V2>的记录,将旧值V1写入数据项X中。
- 往日志中写一个特殊的只读记录<T,X,V1>,表示将数据项恢复成旧值V1,
这是一个只读的补偿记录,不需要根据它进行undo。 - 一旦发现了<T start>日志记录,就停止继续扫描,并往日志中写一个
<T abort>日志记录。
系统崩溃是的恢复(带checkpoint)
重做阶段:
- 系统从最后一个检查点checkpoint开始正向的扫描日志,将要重做的事务的列表undo-list设置为检查点日志记录中的L列表。
- 发现<T,X,V1,V2>的更新记录或<T,X,V>的补偿撤销记录,就重做该操作。
- 发现<T start>记录,就把T加入到undo-list中。
- 发现<T abort>或<T commit>记录,就把T从undo-list中去除。
撤销阶段:
- 系统从尾部开始反向扫描日志
- 发现属于undo-list中的事务的日志记录,就执行undo操作
- 发现undo-list中事务的T的<T start>记录,就写入一条<T abort>记录, 并把T从undo-list中去除。
- undo-list为空,则撤销阶段结束
总结:先将日志记录中所有事务的更新按顺序重做redo一遍,在针对需要撤销的事务按相反的顺序执行其更新操作的撤销操作,涉及undo-list。
网友评论