mysql日志

Posted 汪汪爱学习

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql日志相关的知识,希望对你有一定的参考价值。

基础知识

在讲Redo log工作原理之前,先来了解一下mysql的一些基础知识,日志类型,包括:

1)物理日志:记录了物理页上的数据修改,redo log属于物理日志,恢复时速度快

2)逻辑日志:记录sql的逻辑修改,binlog属于逻辑日志,人工易读

redo log

redo log是什么?它是重做日志,记录了引擎下的事务日志,用于记录在某个数据页上的数据修改情况,是InnoDB引擎特有的。设计它的主要目的是用于数据库异常重启时,已经提交的事务数据不会丢失。提到redo log不得不提WAL技术。

WAL与redo Log

"In computer science, write-ahead logging (WAL) is a family of techniques for providing atomicity and durability (two of the ACID properties) in database systems."——维基百科

在计算机领域,WAL(Write-ahead logging,预写式日志)是数据库系统提供原子性和持久化的一系列技术。在使用WAL的系统中,所有的修改都先被写入到日志中,然后再被应用到系统状态中。如果使用了WAL,那么在重启之后系统可以通过比较日志和系统状态来决定是继续完成操作还是撤销操作。

为什么需要WAL呢?客户端提交的sql,直接记录在数据库存储结构(即B+ Tree)里,有什么不好呢?答案是为了效率考虑,因为MyQL InnoDB引擎的数据存储介质是磁盘,每次的数据修改,可能散布在磁盘的各处,随机读写的效率低。所以即先写日志,再更新数据库。WAL的关键点是因为写日志时是顺序写,效率远快于磁盘的随机写

redo log是WAL的一种,默认情况下,每个InnoDB引擎至少有一个重做日志组,每个组下至少有两个重做日志文件,并且redo log 文件的大小是固定的。假如我们的redo log配置的是由4个1GB文件组成的集合,如下图所示:

write pos 是当前要写入日志的位置,当写到末尾即log file3写满时,会重新到log file0开始写入。

checkpoint 是当前待擦除的位置, checkPoint之前的位置标示已经刷到磁盘的数据,check point与write point之间的数据,一般就是与数据库中有差异的数据,俗称“脏页”。所以,如果当write pos追上 check pos时,即 redo空间满了的时候,需要待部分数据“刷盘”完成后,才能继续执行更新事务,所以有时候MySQL的性能会偶尔“抖”一下。当然为了避免在业务高峰期,突然的性能抖动,mysql为在认为系统空闲的时候,悄悄刷盘。


crash-safe与redo Log

每个事务的redo log都有一个LSN(Log Sequence Number),它是一个单调递增的值。数据页和redo log都有各自的LSN。DB宕机后重启,InnoDB会首先去查看数据页中的LSN的数值。这个值代表数据页被刷新回磁盘的LSN的大小。然后再去查看redo log的LSN的大小。如果数据页中的LSN值大说明数据页领先于redo log刷新回磁盘,不需要进行恢复。反之需要从redo log中恢复数据。


总结一下

通过WAL技术,写库之前先顺序写log,是InnoDB性能“快”的原因之一,同时redo log也可以用于确保异常重启时的数据恢复,即crash-safe的能力,你get到了吗?


BinLog

MySQL服务端架构上分为服务器层和引擎层,server层负责功能层面的事情,还有一块就是引擎层,主要负责存储相关的事宜。而Server 层也有自己的日志,即binlog。不管你使用的是哪一种存储引擎,都会产生binlog。它记录了MySQL对数据库执行更改的所有操作,可以用来做数据归档和数据恢复。在MySQL5.1之前,所有的binlog都是基于SQL语句级别的。但是应用这种格式的binlog进行数据恢复时,如果SQL语句带有rand或uuid等函数,可能导致恢复出来的数据与原始数据不一致。因此从5.1版本开始,MySQL引入了binlog_format参数,该参数有三种可选值:statement、row和mixed,如下

参数

含义

优点

缺点

statement

将sql语句直接作为bin log

批量操作时,比较节约空间

可能会引起主从不一致,比如rand()函数

row

记录行变更的情况

主从完全一致,可以利用bin log快速恢复数据。

批量操作时,占用空间比较大

mixed

默认statement模式,如果可能会发生不一致的时候,则采用row格式

确保主从一致,并且比row格式节约空间

无法只利用bin log快速恢复数据


一般情况下,出于主从一致和运维考虑(利用binlog,执行数据快速恢复),推荐将binlog_format设置为row格式。曾经在工作中,因为逻辑bug,导致发生了批量的“误删除”记录事件,通过查找时间和关键字,DBA快速帮我找出了对应的row格式的bin log,利用这些bin log,我们快速的恢复了被“误删除”的数据,避免了一次事故的产生。

binlog与主从同步

从库同步主库的记录,主要就是靠的是复制主库的binlog到从库重新执行,即可。具体需要三个线程来操作:

(1)binlog输出线程:每当有从库连接到主库的时候,主库都会创建一个线程然后发送binlog内容到从库。

在从库里,当复制开始的时候,从库就会创建两个线程进行处理:

(2)从库I/O线程:当START SLAVE语句在从库开始执行之后,从库创建一个I/O线程,该线程连接到主库并请求主库发送binlog里面的更新记录到从库上。从库I/O线程读取主库的binlog输出线程发送的更新并拷贝这些更新到本地文件,其中包括relay log文件。

(3)从库的SQL线程:从库创建一个SQL线程,这个线程读取从库I/O线程写到relay log的更新事件并执行。

可以知道,对于每一个主从复制的连接,都有三个线程。拥有多个从库的主库为每一个连接到主库的从库创建一个binlog输出线程,每一个从库都有它自己的I/O线程和SQL线程。

看起来,通过bin log进行主从同步,流程比较清晰,也比较简单。这里有没有问题呢?有,这里同步可以认为是单线程在操作,是否可以支持多线程并行执行呢?

看起来很简单的一个问题,但确非常复杂,因为有如下两条原则必须保证:

1.一个事务产生的多条bin log 在一个线程中执行

2.同一条记录的多个事务产生的bin log 在一个线程中执行

MySQL5.6开始支持并行复制,策略包括:

1)一个库里面的bin log放到一个线程中,同一个库里面的bin log顺序执行。这对于有多个分库的业务场景,比较适合。

2)一个“提交组”里的事务,可以放到通过并发执行。“提交组”里的事务,就是相互不影响的事务。

binlog与事务的两阶段提交

服务层有server 有binlog,引擎层有redo log。这两个日志是如何保持一致的呢,这就是我们接下来要提到的两阶段提交,具体流程如下


即将redo log的写入拆成了两个步骤:prepare和commit,来确保bin log和 redo log是同步的。


Undo Log

Undo Log是逻辑日志,用于记录变更前的值,当我们对记录做了变更操作时就会产生undo记录。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。

undo log一般用于回滚和mvcc。

Undo Log与事务原子性

Undo Log由undo log segments组成,包含rollback segments。InnoDB最大支持128个rollback segment,其中32分配给临时表空间,96个分配给常规表的事务数据修改,innodb_undo_logs控制rollback segment的数量。undo segment位于共享表空间内。

在Innodb当中,INSERT操作在事务提交前只对当前事务可见,Undo log在事务提交后即会被删除,因为新插入的数据没有历史版本,所以无需维护Undo log。而对于UPDATE、DELETE,责需要维护多版本信息。 在InnoDB当中,UPDATE和DELETE操作产生的Undo log都属于同一类型:update_undo。(update可以视为insert新数据到原位置,delete旧数据,undo log暂时保留旧数据)。

undo log的主要目的是支持MVCC和回滚。举例如下:

 Session1(以下简称S1)和Session2(以下简称S2)同时访问(不一定同时发起,但S1和S2事务有重叠)同一数据A.

S1想要将数据A修改为数据B,S2想要读取数据A的数据。没有MVCC只能依赖加锁了,谁拥有锁谁先执行,另一个等待。但是高并发下效率很低。

InnoDB存储引擎通过多版本控制的方式来读取当前执行时间数据库中行的数据,如果读取的行正在执行DELETE或UPDATE操作,这是读取操作不会因此等待行上锁的释放。相反的,InnoDB会去读取行的一个快照数据(Undo log)。在InnoDB当中,要对一条数据进行处理,会先看这条数据的版本号是否大于自身事务版本(非RU隔离级别下当前事务发生之后的事务对当前事务来说是不可见的),如果大于,则从历史快照(undo log链)中获取旧版本数据,来保证数据一致性。而由于历史版本数据存放在undo页当中,对数据修改所加的锁对于undo页没有影响,所以不会影响用户对历史数据的读,从而达到非一致性锁定读,提高并发性能。

另外,如果出现了错误或者用户手动执行了rollback,系统可以利用undo log中的备份将数据恢复到事务开始之前的状态。与redo log不同的是,磁盘上不存在单独的undo log 文件,他存放在数据库内部的特殊段(segment)中。

Undo Log与MVCC

为了便于理解MVCC的实现原理,这里简单介绍一下undo log的工作过程

在不考虑redo log 的情况下利用undo log工作的简化过程为:

序号 动作

1 开始事务

2 记录数据行数据快照到undo log

3 更新数据

4 将undo log写到磁盘

5 将数据写到磁盘

6 提交事务

1)为了保证数据的持久性数据要在事务提交之前持久化

2)undo log的持久化必须在在数据持久化之前,这样才能保证系统崩溃时,可以用undo log来回滚事务


Innodb通过undo log保存了已更改行的旧版本的信息的快照。

InnoDB的内部实现中为每一行数据增加了三个隐藏列用于实现MVCC。

列名

长度(字节)

作用

DB_TRX_ID

6

插入或更新行的最后一个事务的事务标识符。(删除视为更新,将其标记为已删除)

DB_ROLL_PTR

7

写入回滚段的撤消日志记录(若行已更新,则撤消日志记录包含在更新行之前重建行内容所需的信息

DB_ROW_ID

6

行标识(隐藏单调自增id)


MVCC工作过程

MVCC只在READ COMMITED 和 REPEATABLE READ 两个隔离级别下工作。READ UNCOMMITTED总是读取最新的数据行,而不是符合当前事务版本的数据行。而SERIALIZABLE 则会对所有读取的行都加锁

SELECT

InnoDB 会根据两个条件来检查每行记录:

InnoDB只查找版本(DB_TRX_ID)早于当前事务版本的数据行(行的系统版本号<=事务的系统版本号,这样可以确保数据行要么是在开始之前已经存在了,要么是事务自身插入或修改过的)

行的删除版本号(DB_ROLL_PTR)要么未定义(未更新过),要么大于当前事务版本号(在当前事务开始之后更新的)。这样可以确保事务读取到的行,在事务开始之前未被删除。

INSERT:InnoDB为新插入的每一行保存当前系统版本号作为行版本号

DELETE:InnoDB为删除的每一行保存当前的系统版本号作为行删除标识

UPDATE:InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识

总结一下

用于实现MVCC,提高读写并发,同时用于支持回滚,保证事务的原子性。


以上是关于mysql日志的主要内容,如果未能解决你的问题,请参考以下文章

怎么开启mysql日志功能

mysql日志全部都没有开启,怎么回事

mysql.log日志放在哪

怎么查看mysql日志

MYSQL日志

怎么查看mysql日志