热衷学习,热衷生存!
积淀、分享、成长,让本人和别人都能有所播种!
一、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...