热衷学习,热衷生存!😄
积淀、分享、成长,让本人和别人都能有所播种!😄
一、MySQL 日志
MySQL
日志次要包含谬误日志、查问日志、慢查问日志、事务日志、二进制日志几大类。其中比拟重要的就是二进制日志binlog
(归档日志)、事务日志redo log
(重做日志)和undo log
(回滚日志)。
日志关系如下图:
二、redo log
redo log
(重做日志)是 InnoDB
存储引擎独有的,它让 MySQL
有了解体复原的能力。
当 MySQL
实例挂了或者宕机了,重启的时候 InnoDB
存储引擎会应用 rede log
日志复原数据,保障事务的持久性和完整性。如下图:
MySQL
中数据是以页为复数存储,当你查问一条记录时,硬盘会把一整页的数据加载进去,加载进去的数据叫做数据页,会放到 Buffer Pool
中。后续的查问都是先从 Buffer Pool
中找,没有找到再去硬盘加载其余的数据页直到命中,这样子能够缩小磁盘 IO
的次数,进步性能。更新数据的时候也是一样,优先去 Buffer Pool
中找
,如果存在须要更新的数据就间接更新。而后会把“在某个数据页做了什么批改”记录到重做日志缓存(redo log buffer
)里,在刷盘的时候会写入 redo log
日志文件里。
如下图:
小贴士:每条 redo 记录由“表空间号 + 数据页号 + 偏移量 + 批改数据长度 + 具体批改的数据”组成。
刷盘机会
现实状况下,事务一提交就会进行刷盘操作,然而实际上是刷盘的机会是依据策略来决定的。
InnoDB
存储引擎为 redo log
的刷盘策略提供了 innodb_flush_log_at_trx_commit
参数,它反对三种策略:
- 0:设置为 0 的时候,每次提交事务时不刷盘。
- 1:设置为 1 的时候,每次提交事务时刷盘。
- 2:设置为 2 的时候,每次提交事务时都只把
redo log buffer
写入page cache
。
innodb_flush_log_at_trx_commit
参数默认为 1,当事务提交的时候会调用 fsync
对redo log
进行刷盘,将 redo log buffer
写入 redo log
文件中。
另外,Innodb
存储引擎有一个后盾线程,每隔 1
秒,就会把会 redo log buffer
中的内容写入到文件系统缓存 page cache
,而后调用fsync
刷盘。
如上图,所以说一个没有提交事务的 redo log
记录,也会被刷盘。
上面是各种刷盘策略的流程图。
innodb_flush_log_at_trx_commit = 0
如上图,如果宕机了或者 MySQL
挂了可能造成 1
秒内的数据失落。
innodb_flush_log_at_trx_commit = 1
如上图,只有事务提交胜利,redo log
记录就肯定在磁盘里,不会有工作数据失落。
如果执行事务的时候 MySQL
挂了或者宕机了,这部分日志失落了,然而因为事务没有提交,所以日志丢了也不会有损失。
innodb_flush_log_at_trx_commit = 2
如上图,当事务提交胜利时,redo log buffer
日志会被写入 page cache
,而后后盾线程会刷盘写入redo log
,因为后盾线程是1
秒执行一次所以宕机或者 MySQL
挂了可能造成 1
秒内的数据失落。
日志文件组
硬盘上存储的 redo log
日志文件不止一个,而是一个 日志文件组 的模式呈现的,每个的 redo log
文件大小都是一样的。它采纳的是环形数组模式,从头开始写,写到开端回到头循环写,如下图所示:
在 日志文件组 中有两个重要的属性,别离是witre pos、checkpoint
- wirte pos:是以后记录的地位,一边写一边后移。
- checkpoint:是以后要擦除的地位,也是后盾推移。
每次刷盘 redo log
记录到 日志文件组 中,wirte log
地位就会后移更新。
每次 MySQL
加载 日志文件组 复原数据时,会清空加载过的 redo log
,并把checkpoint
后移更新。
write pos
和 checkpoint
之间的还空着的局部能够用来写入新的 redo log
记录。
如果 witre pos
追上 checkpoint
,示意 日志文件组 满了,这时候不能再写入新的 redo log
记录,MySQL
得停下来,清空一些记录,把 checkpoint
举荐一下。
redo log 小结
redo log
的作用和它的刷盘机会、存储模式。
能够思考一个问题:只有每次把批改后的数据页间接刷盘不就好了,为什么还要用 redo log
刷盘?不都是刷盘吗?有什么区别?
实际上,数据页大小是 16KB
,刷盘比拟耗时,可能就批改了数据页的几byte
数据,没有必要把整页的数据刷盘。而且数据页刷盘都是随机写,因为一个数据页对应的地位可能是在硬盘文件的随机地位,所以性能很差。
如果是写redo log
,一行记录就占了几十byte
,只有蕴含了表空间号、数据页号、磁盘文件偏移量、批改值,再加上是程序写,所以刷盘效率很高。
所以用 redo log
模式记录批改内容,性能会远远超过刷数据页的形式,这也让数据库的并发能力更强。
三、binlog
redo log
是物理日志,记录的是“在某个数据页做了什么批改”,属于 Innodb
存储引擎。
而 binlog
日志是逻辑日志,记录内容是语句的原始逻辑,属于 MySQL Server
层。所有的存储引擎只有产生了数据更新,都会产生 binlog
日志。
binlog
日志的作用
能够说 MySQL
数据库的 数据备份、主备、主主、住从 都离不开 binlog
,须要依赖binlog
来同步数据,保证数据一致性。
binlog
会记录所有波及更新数据的逻辑规定,并且按程序写。
记录格局
binlog
日志有三种格局,能够通过 binlog_format
参数设置,有以下三种:
- statement
- row
- mixed
设置 statement
记录的内容是 SQL
语句原文,比方执行一条update T set update_time = now() where id = 1
,记录内容如下:
同步数据时,会执行记录的 SQL
语句,然而有个问题 update_time = now()
这里会获取到以后零碎问题,间接执行会导致与原库数据不统一。
为了解决这种问题,咱们须要将 binlog_format
设置成 row
,记录的不再是简略的SQL
语句了,还蕴含了操作的具体数据,记录内容如下:
row
格局记录的内容看不到详细信息,通过 mysqlbinlog
工具解析进去。
update_time = now()
变成了具体的工夫,条件前面的 @1、@2
都是该行数据第 1 个~2 个字段的原始值(假如这张表只有 2 个字段)。
设置成 row
带来的益处就是同步数据的一致性,通常状况都设置成 row
,这样能够为数据库的复原与同步带来更好的可靠性。然而这种格局须要大量的容量来记录,比拟占用空间,复原与同步时会更耗费IO
资源,影响执行速度。
所以又有了一种折中计划,设置为mixed
,记录的内容是前两者的混合。
MySQL
会判断这条 SQL
语句是否会引起数据不统一,如果是就用 row
格局,否则就用 statement
格局。
写入机制
binlog
的写入机会为事务执行过程中,先把日志写到 binlog cache
,事务提交的时候再把binlog cache
写到 binlog
文件中(理论先会写入 page cache
,而后再由fsync
写入 binlog
文件)。
因为一个事务的 binlog
不能被拆开,无论这个事务多大,也要确保一次性写入,所以零碎会给每个线程调配一块内存作为 binlog cache
。能够通过binlog_cache_size
参数管制单线程 binlog_cache
大小,如果存储内容超过了这个参数,就要暂存到磁盘。
binlog
日志刷盘流程如下:
- 上图的
write
,是指把日志写入到文件系统的page cache
,并没有把数据长久化硬盘,所以速度比拟快。 - 上图的
fsync
才是将数据库长久化到硬盘的操作。
write
和 fsync
的机会能够由参数 sync_binlog
管制,能够配置成0、1、N(N>1)
。
- 设置成 0 时:示意每次提交事务都只会
write
,由零碎自行判断什么时候执行fsync
。 - 设置成 1 时:示意每次提交事务都会执行
fsync
,就和redo log
日志刷盘流程一样。 - 设置成 N 时:示意每次提交事务都会
write
,然而积攒N
个事务后才fsync
。
设置为 0 时如下图:
从上图可知,sync_bilog = 0
设置成 0
,只把日志写入page cache
尽管性能失去了进步,然而事务提交了 fsync
的时候宕机了,可能造成 binlog
日志的失落。
设置为 2 时如下图:
在呈现 IO
瓶颈的场景里,将 sync_binlog
设置成一个比拟大的值,能够晋升性能。
同样的,如果机器宕机,会失落最近 N
个事务的 binlog
日志。
两阶段提交
redo log
(重做日志)让 InnoDB
存储引擎有了解体复原的能力。
binlog
(归档日志)保障了 MySQL
集群架构数据的一致性。
尽管它们都属于长久化的保障,然而侧重点不一样。
在执行更新语句过程,会记录 redo log
与binlog
两块日志,以根本的事务为单位,redo log
在事务执行过程中能够一直写入,而 binlog
日志只有在提交事务的时候才会写入,所以它们写入的机会不一样。
思考一个问题,如果 redo log 和 binlog 两份日志之间的逻辑不一样,会呈现什么问题呢?MySQL 是怎么解决这个问题的呢?
比方有这样一个场景,假如有这么一条语句 update T set c = 1 where id = 2
(c 原值为 0),如果执行过程中写完redo log
日志后,在写入 binlog
的时候产生了异样,会呈现什么状况呢?
如下图:
因为 binlog
日志没写完就异样,这个时候 binlog
日志外面没有对应的批改记录,之后应用 binlog
同步的数据的时候就会少这一次的更新,这一行数据 c = 0
,而原库应用redo log
日志复原,这一行数据c = 1
,最终数据不统一。如下图:
为了解决两份日志之间的逻辑不统一的问题,InnoDB
存储引擎应用 两阶段提交 计划。
将 redo log
日志的写入拆分成两个步骤 prepare
和commit
,如下图:
应用 两阶段提交 后,写入 binlog
时产生异样也没关系,因为 MySQL
依据 redo log
日志复原数据时,发现 redo log
日志处于 prepare
阶段,并且没有对应 binlog
日志(依据事务 id 对应),所以就会回滚事务。
再想一个场景,redo lgo
设置 commit
阶段产生异样,事务会不会回滚呢?
并不会回滚事务,尽管 redo log
是处于 prepare
阶段,然而存在对应的事务 binlog
日志,所以 MySQL
认为是残缺的,所以不会回滚事务。
undo log
想要保障事务的原子性,就须要在产生异样时,对曾经执行的操作进行回滚,在 MySQL
中复原机制是通过undo log
(回滚日志)实现的,所有事务进行的批改都会先被记录到这个回滚日志,而后再执行其余相干的操作。如果执行过程中遇到异样的话,咱们间接利用回滚日志中的信息将数据回滚到批改之前的样子。并且,回滚日志会先于数据长久化到磁盘上。这样就保障了即便遇到数据库忽然宕机等状况,当用户再次启动数据库的时候,数据库还可能通过查问回滚日志来回滚将之前未实现的事务。
另外,MVCC
的实现依赖:暗藏字段、Read View
、undo log
。在底层实现中,InnoDB
通过数据行的 DB_TRX_ID
和Read View
来判断数据的可见性,如不可见,则通过数据行 DB_ROLL_PTR
找到 undo log
中的历史版本。每个事务读到的数据版本可能是不一样的,在同一个事物里,用户只能看到该事务创立 Read View
之前曾经提交的批改和该事务自身做的批改。
总结
MySQL InnoDB
引擎应用 redo log
日志保障事务的持久性,应用 undo log
日志保障事务的原子性。
MySQL
数据库的 数据备份、主备、主主、主从 离不开 binlog
,须要依赖binlog
来同步数据,保证数据的一致性。
参考:https://javaguide.cn/database…