前言:
MySql 中有三种 log 是非常中要的,因为 MySql 之所以能反对 事务(实现长久化、回滚等)、数据库解体复原、主从复制等,都是基于这三种日志的。
至关重要的三种 log:
- binlog 二进制日志
- redo log 重做日志
- undo log 回滚日志
本文主线:
- 简要介绍三种日志
- MySql 事务处理中三种日志承当的角色
简要介绍三种日志:
1、binlog 二进制日志:
binlog 二进制日志(归档日志),这个日志是由 MySql 的 server 层 进行保护的;不论以后 MySql 应用的是什么存储引擎,binlog 归档日志都是反对的;
对 MySql 的 server 层不分明的大大们能够参考此文章:查问 SQL 的执行流程
作用:
- 用于复制,在主从复制中,从库利用主库上的 binlog 进行重播,实现主从同步。
- 数据恢复,用于数据库的基于工夫点的还原。
内容:
逻辑格局的日志,binlog 是用于记录所有数据库表构造和表数据变更的二进制日志,比方 insert、update、delete、create、truncate 等等操作,不会记录 select、show 操作,因为它们没有对数据自身产生扭转。
常见格局:
MySql 的 binlog 的默认格局是应用 STATEMENT;须要记住的是应用此格局时,如果将事务隔离级别设置为 RC 读已提交 时,在进行主从复制的时候会存在 bug,导致复制后主从数据不统一;
具体存在什么样的 bug 能够参考此文章:互联网我的项目中 mysql 应该选什么事务隔离级别
留神:
binlog 二进制日志文件在默认状况下并没有启动,须要手动进行开启的;有人可能会质疑,开启此日志文件的话,对数据库的性能会产生什么样的影响?
质疑是对的,的确开启此日志文件会影响到数据库的性能,然而这个影响是非常无限的,依据 MySql 的官网手册理解到,开启此日志会使性能降落 1% 左右,这个损失大体上是能够承受的。
2、redo log 重做日志:
redo log 重做日志,这个日志是由 MySql 的 innodb 存储引擎 提供保护的,此日志文件只存在于 innodb 存储引擎下;
作用:
确保事务的 持久性 。redo 日志记录事务执行后的状态,用来复原未写入 data file 的已胜利事务更新的数据。避免在产生故障的工夫点,尚有脏页未写入磁盘,在重启 mysql 服务的时候,依据 redo log 进行重做,从而达到事务的持久性这一个性。
各位大大,如果下面这段话如果看的不是很明确,能够持续往下看看呀,通过下文中事务的例子能够很好了解;
内容:
物理格局的日志,记录的是物理数据页面的批改的信息,简略说就是记录着 xxx 页做了 xxx 批改;
innodb 存储引擎提供了重做日志文件组(group),每个重做日志文件组蕴含着重做日志文件;默认是提供了一个重做日志文件组,文件组下蕴含两个大小雷同的重做日志文件;
innodb 存储引擎的重做日志文件写入流程:先写重做日志文件 1,当文件 1 被程序写满时,会切换到重做日志文件 2,再当重做日志文件 2 也被写满时,会再切换到重做日志文件 1 中,顺次循环;所以说重做日志文件是 循环笼罩写入的 。
因为重做日志是循环笼罩写入的,所以不能应用其进行整个数据库的数据恢复,它只能保障数据库宕机时的事务的完整性数据;如果想要复原全副数据的话,只能应用 binlog 二进制日志(归档日志)进行复原。
留神:大家能够手动批改重做日志文件组下的文件数量,并能够指定每个重做日志文件的大小,通过上面的参数:
- innodb_log_file_size 指定重做日志文件的大小
- innodb_log_files_in_group 重做日志文件组下的文件数量
扩大:
留神:重做日志文件的大小设置对于 innodb 存储引擎的性能有很大的影响。
重做日志文件不能设置的太大,也不能设置的太小;如果设置的太大,在数据库意外宕机后进行复原时会须要很长时间;
然而也不能设置的太小,因为设置的太小会导致一个事务的日志须要屡次切换重做日志文件进行写入,那么在笼罩掉之前的重做日志时,须要将要被笼罩的重做日志对应在内存中的脏页进行写盘(刷盘);
因为不写盘的话,如果重做日志被笼罩掉了,而后数据库意外宕机了,那么之前没有写盘的数据将没法在数据库重启时进行复原了,并且如果频繁的进行重做日志的笼罩的话,那么就会频繁的进行脏页刷盘,进而导致数据库性能的抖动。
那重做日志应该设置多大呢?一般来说,redo log 日志文件的全副大小,应该足够包容服务器一个小时的流动内容。
如果统计出一小时的重做日志写入量为 500M 的话,因为 redo log 日志文件默认有 2 个,所以须要设置 innodb_log_file_size=250M;
留神:如果各位大大们对 脏页等名词 不太分明的话,能够通过《丁奇大佬 – MySQL 实战 45 讲》、《mysql 技术底细 innodb 存储引擎》进行学习理解呀。
3、undo log 回滚日志:
留神 undo log 回滚日志的写入是在事务开始执行前就曾经开始了;
作用:
保证数据的原子性,保留了事务产生之前的数据的一个版本,能够用于回滚,同时能够提供多版本并发管制下的读(MVCC),也即非锁定读;
内容:
逻辑格局的日志,在执行 undo 的时候,仅仅是将数据从逻辑上复原至事务之前的状态,而不是从物理页面上操作实现的,这一点是不同于 redo log 的。
比如说咱们要 insert 一条数据,那么 undo log 就会生成一条对应的 delete 日志,并且与以后事务的 txid(事务惟一标识)进行了关联,用于反对回滚和 MVCC;
像如果在一个事务中执行了多个蕴含 update、insert、delete 的语句时,因为事务具备原子性,如果其中一个语句执行失败,那么之前执行胜利的语句要就要被回滚撤销掉,此时就会依据以后事务 txid 在 undo log 中查找到保留的日志进行执行回滚;
留神:写入 undo log 的这个操作也是须要记录到 redo log 重做日志中的;
为什么也要记录到 redo log 中呢?举个场景,当事务在没执行完时 mysql 意外宕机了,那么在数据库重启后须要复原事务,如果事务须要回滚,然而你在内存中保留的 undo log 还没有写入磁盘,在宕机时就失落了,那么就会导致无奈回滚,就会呈现数据不统一的问题;而如果记录到了 redo log 中,则能够依据 redo log 进行重做,而后依据重做的 undo log 进行回滚。
扩大:
留神:为了晋升数据库性能,mysql 都是在内存的缓冲区中记录日志,而后依据策略再进行刷盘的(将缓冲区记录的日志写入到磁盘中进行保留);
通过 innodb 存储引擎的架构图能够进行深刻理解:
如果想深刻学习,请参考此文章 你竟然还不晓得 Mysql 存储引擎 InnoDB 分为内存架构、磁盘架构?
MySql 事务处理中三种日志承当的角色:
首先咱通过一个 update 语句的执行流程图展现出三种日志文件在事务处理中承当的角色!SQL 语句如下:
update T set c=c+1 where ID=2;
为什么流程图中没有 undo log:
在流程图中能够看到 redo log、binglog,那怎么没有 undo log 呀,因为 undo log 在 事务开始执行前 就曾经开始写入了。
为什么上图事务中必须存在 redo log 和 binlog 呢:
首先,下面介绍这两种日志时,曾经说道了 binlog 是 mysql 的 server 层保护的日志,次要用来做主从复制和数据备份复原应用;而 redo log 是 InnoDB 存储引擎独有的日志,是用来实现 crash-safe 能力;
crash-safe 是什么?
即在 InnoDB 存储引擎中,事务提交过程中任何阶段,MySQL 忽然奔溃,重启后都能保障事务的完整性,已提交的数据不会失落,未提交残缺的数据会主动进行回滚。
为了实现 crash-safe 除了须要 redo log 外,还须要 undo log 对未残缺的事务进行回滚的;
想要对 crash-safe 进行具体理解的大大们能够参考此文章:MySQL 的 crash-safe 原理解析
上图中处于 prepare 阶段、处于 commit 阶段是什么:
其实这是 mysql 的外部 XA 事务!俗称日志的两阶段提交协定!看到这大家是不是有点含糊了,据说过分布式 XA 事务,怎么还有外部 XA 事务呀?
其实大家晓得的分布式 XA 事务指的是 mysql 的内部 XA 事务;外部 xa 事务次要是 mysql 外部为了保障 binlog 与 redo log 之间数据的一致性而存在的,这也是由其架构决定的 (binlog 由 mysql 的 server 层提供反对,而 redo log 则是由 innodb 存储引擎层提供反对);
对 mysql 的架构不太熟悉的大大能够参考此文章:查问 SQL 的执行流程
binlog 与 redo log 之间数据的一致性问题是什么:
因为 redo log 和 binlog 都是用来 ” 复原 ” 数据的,redo log 是用来复原事务,保障事务的完整性的,而 binlog 是能够用来进行整体数据恢复,或基于某个工夫点进行复原的,并且用于主从复制;
如果在进行一个事务执行时,曾经实现了 redo log 重做日志的写入,然而还没有写入 binlog,此时数据库意外宕机了,如果没有保障两个日志 逻辑上统一 的话;
那么在数据库重启时会依据 redo log 进行事务复原,并将在事务中曾经解决好但未写盘的数据进行复原;然而因为 binlog 没有写入,所以会导致在应用 binlog 进行数据恢复时,或者是主从复制时,呈现数据不统一的状况;
如果保障了两个日志的逻辑统一的话,那么在应用 redo log 复原事务时,会判断 binlog 是否也写入胜利了,如果 binlog 也写入胜利,那么才会进行事务复原,否则将不进行事务复原,会应用 undo log 进行回滚的;
MySql 外部 XA 事务是什么呢:
外部 XA 事务就是将事务提交分为了两个阶段,prepare 阶段和 commit 阶段!
prepare:写入 redo log,并将回滚段置为 prepared 状态,此时 binlog 不做操作。
commit:innodb 开释锁,开释回滚段,设置提交状态,写入 binlog,而后存储引擎层提交。
扩大:
各位大大们,上图 update 的语句执行流程中,写入 redo log 重做日志,写入 binglog 二进制日志,其实都只是 先 写入到日志缓冲区中,前面还会波及到日志缓冲区中数据写入到具体磁盘中的日志文件中;
那何时才会将缓冲区的日志数据写入到磁盘中?这是由 mysql 中的几个配置参数管制的;
缓冲区的日志写入到磁盘中的流程:
在介绍配置参数前,咱们先理解下缓冲区的日志写入到磁盘中的流程是什么样:
- 用户空间:是指用户程序运行的空间,例如 mysql 就运行在此空间内;
- 内核空间:内核空间是指操作系统内核运行的空间,是为了保障操作系统内核的可能平安稳固地运行而为内核专门开拓的空间;
为什么须要内核空间呢?
次要是为了平安起见,不能让用户空间的程序间接去磁盘空间中读取数据,必须由经由内核空间通过 DMA 来获取;并且用户空间和内核空间两者之间是相互隔离的。
redo log 刷盘配置参数:
innodb_flush_log_at_trx_commit 配置参数用来管制重做日志刷新到磁盘的策略。
该参数的值存在三种:
- 0:示意事务提交时不进行写入重做日志擦操作,MySql 会应用其后盾线程每一秒将日志缓冲区中的重做日志写入到 OS cache(磁盘缓存),同时立刻调用 fsync 操作将 OS cache 中的重做日志写入到磁盘文件中(刷盘);
- 1:示意在事务提交时就会进行重做日志的写入操作,实时的将日志缓冲区中的重做日志写入到 OS cache(磁盘缓存),同时立刻调用 fsync 操作将 OS cache 中的重做日志写入到磁盘文件中(刷盘); 此值为默认值 ,因为当设置值为 1 时能够保障事务的 ACID 中的持久性;
- 2:示意在事务提交时就会进行重做日志的写入操作,然而只是将日志缓冲区中的重做日志写入到 OS cache(磁盘缓存),不会立刻调用 fsync 操作进行刷盘,MySQL 前面会被动将 OS cache 中的重做日志数据每秒批量进行一次刷盘;抉择此值时,mysql 的并发性最好,然而存在危险,当操作系统一旦宕机,会丢数据,然而如果 MySql 数据库宕机的话,则不会失落数据,因为数据保留在了 OS cache 中;
binlog 刷盘配置参数:
sync_binlog 配置参数用来管制二进制日志刷新到磁盘的策略。
该参数罕用的三种值:
- 0:示意在事务提交后,mysql 会将日志缓冲区中的 binlog 数据写入到 OS cache(磁盘缓存),但并不会调用 fsync 操作进行刷盘,而是由操作系统本人管制它的缓存的刷盘; 此值为默认值 。
- 1:示意在事务提交时就会进行 binlog 的写入操作,实时的将日志缓冲区中的重做日志写入到 OS cache(磁盘缓存),同时立刻调用 fsync 操作将 OS cache 中的重做日志写入到磁盘文件中(刷盘);
- N:示意每次进行提交后都只是将日志缓冲区中的 binlog 数据写入到 OS cache(磁盘缓存),在进行 N 次事务提交当前,Mysql 将执行一次 fsync 操作指令将 OS cache 中的 binlog 数据批量刷新到磁盘中; 留神 N 代表的是数值;
一道经典的面试题:
面试官:你有遇到过数据库宕机重启,事务失落的状况么?
咱们:其实这道题就是 redo log、binlog 的刷盘机会,下面曾经聊了这两种日志的全副刷盘机会了,大家能够两两联合进行答复就好了;
也能够间接参考此文中提供的答案进行答复:面试官: 谈谈你对 mysql 事务的意识?
各位大大们,MySql 中至关重要的三种日志就介绍到此啦;因为自己水品无限,如有问题请留言探讨呀!
♡ 点赞 + 评论 + 转发 哟
如果本文对您有帮忙的话,请挥动下您爱发财的小手点下赞呀,您的反对就是我一直创作的能源,谢谢啦!
您能够微信搜寻 【木子雷】 公众号,大量 Java 学习干货文章,您能够来瞧一瞧哟!