- Mysql
1.1日志
redo log 重做日志,是 Innodb 存储引擎层生成的日志,实现了事务中的持久性,主要用于掉电等故障恢复
undo log 回滚日志,是 Innodb 存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和 MVCC。每当 InnoDB 引擎对一条记录进行操作(修改、删除、新增)时,要把回滚时需要的信息都记录到 undo log 里,在发生回滚时,就读取 undo log 里的数据,然后做原先相反操作
bin log 二进制日志,是 Server 层生成的日志,主要用于数据备份和主从复制;
二进制日志记录了MySQL所有修改数据库的操作(不会记录查询类的操作,比如 SELECT 和 SHOW 操作),然后以二进制的形式记录在日志文件中,MySQL 在完成一条更新操作后,Server 层还会生成一条 binlog,等之后事务提交的时候,会将该事物执行过程中产生的所有 binlog 统一写 入 binlog 文件,binlog 是 MySQL 的 Server 层实现的日志,所有存储引擎都可以使用。
binlog 是追加写,写满一个文件,就创建一个新的文件继续写,不会覆盖以前的日志,保存的是全量的日志,用于备份恢复、主从复制;
binlog 有 3 种格式类型,分别是 STATEMENT(默认格式)、ROW、 MIXED,区别如下:
STATEMENT:每一条修改数据的 SQL 都会被记录到 binlog 中,主从复制中 slave 端再根据 SQL 语句重现。但 STATEMENT 有动态函数的问题,比如你用了 uuid 或者 now 这些函数,你在主库上执行的结果并不是你在从库执行的结果,这种随时在变的函数会导致复制的数据不一致;
ROW:记录行数据最终被修改成什么样了,不会出现 STATEMENT 下动态函数的问题。但 ROW 的缺点是每行数据的变化结果都会被记录,比如执行批量 update 语句,更新多少行数据就会产生多少条记录,使 binlog 文件过大,而在 STATEMENT 格式下只会记录一个 update 语句而已;
MIXED:包含了 STATEMENT 和 ROW 模式,它会根据不同的情况自动使用 ROW 模式和 STATEMENT 模式;
逻辑梳理图和关系:
首先当我们执行一条修改语句的时候,BuffePool的缓存页中如果存在数据那就直接修改,如果不存在就去磁盘中去寻找数据,找到之后进行修改,修改之后缓存页变成旧页,UndoLog用于记录旧的数据,因为他是用来事务回滚。然后我们需要把新的数据提交,然后同步到磁盘里。
有了undolog为啥还需要redolog呢?
Buffer Pool 是提高了读写效率没错,但是问题来了,Buffer Pool 是基于内存的,而内存总是不可靠,万一断电重启,还没来得及落盘的脏页数据就会丢失。
为了防止断电导致数据丢失的问题,当有一条记录需要更新的时候,InnoDB 引擎就会先更新内存(同时标记为脏页),然后将本次对这个页的修改以 redo log 的形式记录下来,也就是说redolog去保存修改后的新的数据,假如断电等等情况发生,只需要把redolog的数据恢复到bufferpool里就行。
redo log 和 undo log 这两种日志是属于 InnoDB 存储引擎的日志,它们的区别在于:
redo log 记录了此次事务「完成后」的数据状态,记录的是更新之后的值(也就是新的数据)
undo log 记录了此次事务「开始前」的数据状态,记录的是更新之前的值(也就是旧的数据)
redo log怎么保证持久性的?
Redo log是MySQL中用于保证持久性的重要机制之一。它通过以下方式来保证持久性:
Write-ahead logging(WAL):在事务提交之前,将事务所做的修改操作记录到redo log中,等到合适的时间然后再将数据写入磁盘。这样即使在数据写入磁盘之前发生了宕机,系统可以通过redo log中的记录来恢复数据。
Redo log的顺序写入:redo log采用追加写入的方式,将redo日志记录追加到文件末尾,而不是随机写入。这样可以减少磁盘的随机I/O操作,提高写入性能。
Checkpoint机制:MySQL会定期将内存中的数据刷新到磁盘,同时将最新的LSN(Log Sequence Number)记录到磁盘中,这个LSN可以确保redo log中的操作是按顺序执行的。在恢复数据时,系统会根据LSN来确定从哪个位置开始应用redo log。
既然redo log也需要在事务提交时将日志写入磁盘,为什么它比直接将Buffer Pool中修改的数据写入磁盘(即刷脏)要快呢?主要有以下两方面的原因:
刷脏是随机IO,因为每次修改的数据位置随机,但写redo log是追加操作,属于顺序IO。
刷脏是以数据页(Page)为单位的,MySQL默认页大小是16KB,一个Page上一个小修改都要整页写入。而redo log中只包含真正需要写入的部分,无效IO大大减少。
个人理解:redolog和binlog都是属于磁盘(看那个图就知道了),所以都有刷盘策略。
redo log刷盘机制
新的数据存放在RedoLogbuffer里,要想写入磁盘里有三种刷盘策略,当参数是0的时候,就是每隔1s写入,当参数是1的时候,就是提交时写入,当参数是2的时候,就是先写入系统缓存里,然后再由操作系统调fsync函数写入磁盘
bin log刷盘机制
Binlog是由执行器提交事务,binlog的数据和记录一开始会被保存到 binlog cache里,当参数sync_binlog是0时,由操作系统决定何时刷到磁盘,参数为1,提交时再刷到磁盘,参数为N,也是由操作系统决定,n次事务提交后再刷到磁盘
能不能只用binlog不用relo log?
不行,binlog是 server 层的日志,是整个mysql都有的,用来恢复磁盘的数据,没办法记录哪些脏页还没有刷盘,redolog 是存储引擎层的日志,用来恢复事务的数据,可以记录哪些脏页还没有刷盘,这样崩溃恢复的时候,就能恢复那些还没有被刷盘的脏页数据
binlog 两阶段提交过程是怎么样的?
prepare 阶段::把 XID(内部 XA 事务的 ID) 写入到redo log,然后将redo log持久化到磁盘,同时将 redo log 对应的事务状态设置为 prepare
commit 阶段:把 XID 写入到 binlog,然后将 binlog 持久化到磁盘,,将 redo log 状态设置为 commit
update语句的具体执行过程是怎样的?(最终总结)
具体更新一条记录 UPDATE t_user SET name = 'xiaolin' WHERE id = 1; 的流程如下:
1.执行器负责具体执行,会调用存储引擎的接口,通过主键索引树搜索获取 id = 1 这一行记录:
如果 id=1 这一行所在的数据页本来就在 buffer pool 中,就直接返回给执行器更新;
如果记录不在 buffer pool,将数据页从磁盘读入到 buffer pool,返回记录给执行器。
2.执行器得到聚簇索引记录后,会看一下更新前的记录和更新后的记录是否一样:
如果一样的话就不进行后续更新流程;
如果不一样的话就把更新前的记录和更新后的记录都当作参数传给 InnoDB 层,让 InnoDB 真正的执行更新记录的操作;
3.开启事务, InnoDB 层更新记录前,首先要记录相应的 undo log,因为这是更新操作,需要把被更新的列的旧值记下来,也就是要生成一条 undo log,undo log 会写入 Buffer Pool 中的 Undo 页面,不过在内存修改该 Undo 页面后,需要记录对应的 redo log。
4.InnoDB 层开始更新记录,会先更新内存(同时标记为脏页),然后将记录写到 redo log 里面,这个时候更新就算完成了。为了减少磁盘I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘。这就是 WAL 技术,MySQL 的写操作并不是立刻写到磁盘上,而是先写 redo 日志,然后在合适的时间再将修改的行数据写到磁盘上。
5.至此,一条记录更新完了。
6.在一条更新语句执行完成后,然后开始记录该语句对应的 binlog,此时记录的 binlog 会被保存到 binlog cache,并没有刷新到硬盘上的 binlog 文件,在事务提交时才会统一将该事务运行过程中的所有 binlog 刷新到硬盘。
7.事务提交(就是前面的两阶段提交):
prepare 阶段::把 XID(内部 XA 事务的 ID) 写入到redo log,然后将redo log持久化到磁盘,同时将 redo log 对应的事务状态设置为 prepare
commit 阶段:把 XID 写入到 binlog,然后将 binlog 持久化到磁盘,,将 redo log 状态设置为 commit