MySQL系列:日志

Mysql日志是什么?

日志,就是一种将行为动作记录到一个地方,这个地方可以是文件,文本等可存储的载体。

MySQL日志记录了MySQL数据库日常操作和错误信息。MySQL有不同类型的日志文件,从日志当中可以查询到MySQL数据库的运行情况、用户的操作、错误的信息等。

MySQL日志分类

在 mysql 里的日志种类有很多,从总体上来讲可以分为 Server 层和存储引擎层的。

MySQL 中主要有7种日志文件:

  • 错误日志(error log)
  • 查询日志
    • 通用查询日志(general log)
    • 慢查询日志(slow query log)
  • 二进制日志(bin log)
  • 中继日志(relay log)
  • 事务日志
    • 重做日志(redo log)
    • 回滚日志(undo log)

除了这几种日志,在需要的时候还会创建 DDL日志。

1. 错误日志(error log)

记录MySQL运行过程中的Error、Warning、Note等信息,系统出错或者某条记录出问题可以查看Error日志。

操作
查看错误日志的位置:

mysql> show variables like 'log_error';
+---------------+----------------------------------------+
| Variable_name | Value                                  |
+---------------+----------------------------------------+
| log_error     | /var/lib/mysql/node1.longshuai.com.err |
+---------------+----------------------------------------+

上面默认查询到的是 stderr,表示标准错误输出,如果有终端存在,则只会在终端打印错误。
也可以在 my.cnf 里设置错误日志位置:

[mysqld]
 log-error=错误日志位置

2. 查询日志

查询日志分为通用查询日志(默认关闭)和慢查询日志,它们是通过查询是否超出变量 long_query_time 指定时间的值来判定的。

  • 在超时时间内完成的查询是通用查询,可以将其记录到通用查询日志中
  • 超出时间的查询是慢查询,可以将其记录到慢查询日志中

2.1 通用查询日志(general log)

通用查询日志记录了用户的所有操作,包括 sql 语句的查询更新,它有点类似于我们平时使用 debug 级别的日志。

当开启general log后,mysql中的所有操作将会记录下来,这样general_Log文件会占用很大的磁盘空间,因此一般默认都是不开启。

操作
查看通用查询日志是否开启:

show variables like '%general%';

开启:

set global general_log=on;

设置日志保存的位置:

set global general_log_file='D:\\log\\general.lg'

2.2 慢查询日志(slow query log)

慢查询日志记录了执行时间超过指定阈值的 SQL 语句,主要用于性能瓶颈分析。

如果启用了慢查询日志,执行时间超过 long_query_time 并且执行次数达到 min_examined_row_limit 次的查询语句都会被记录到慢查询日志中。

操作
查看慢查询日志是否开启:

show variables like '%slow_query_log%';

开启:

set global slow_query_log=1;

设置慢查询日志保存文件:

set global slow_query_log_file='D:\\log\\slow.log'

时间阈值的查询和设置:

show variables like 'long_query_time%';

set global long_query_time=8;

3. 二进制日志(bin log)

二进制日志属于逻辑语句的记录,记录了引起或可能引起数据库改变的事件信息,比如update/delete/insert/truncate/create,但并不包括 select 和 show 这样的查询语句。

作用:复制和恢复数据

  • 在MySQL的一主多从结构中,用于主从数据库的同步
  • 数据库的数据被破坏了,可以用binlog进行复制(主从复制)和恢复数据

操作
查看MySQL的二进制日志是否开启:

show variables like '%log_bin%';

启动二进制日志:
MySQL 默认是关闭的,可以在配置文件 my.ini 中的 [mysqld] 语句下设置 log-bin。

[mysqld]
# server_id=1234
log-bin=[on|filename]

查看二进制日志:
Show binary logs查看当前的二进制日志文件个数及其文件名。

mysql> show binary logs;
+-------------------+-----------+
| Log_name          | File_size |
+-------------------+-----------+
| binary_log.000001 |       154 |
+-------------------+-----------+
1 row in set (0.00 sec)

bin log默认情况下是二进制格式,可以使用mysqlbinlog(mysql官方提供的binlog查看工具)来查看。

4. 中继日志(relay log)

从服务器IO线程将主服务器的二进制日志读取过来记录到从服务器本地文件,然后从服务器SQL 线程会读取 relay log 日志的内容并应用到从服务器,从而使从服务器和主服务器的数据保持一致。

relay log 除了包含 binlog 的内容,还会记录当前恢复到哪个位置。如果从服务器断开重连,则可以从上次记录的位置开始恢复。

操作
查看中继日志:

show variables like '%relay%';

MySQL主从复制的流程

  1. 主库db的更新事件(update、insert、delete)被写到binlog
  2. 从库启动并发起连接,连接到主库
  3. 主库创建一个 binlog dump 线程,把 binlog 的内容发送到从库
  4. 从库的IO线程读取主库传过来的binlog内容并写入到relay log
  5. 从库的SQL线程从relay log里面读取内容,从 Exec_Master_Log_Pos 位置开始执行读取到的更新事件,将更新内容写入到slave的db

5. 事务日志

事务日志是 InnoDB 存储引擎为了支持事务的持久化而设计的。

redo log 用来保证事务的持久性,undo log 用来保证事务的原子性和 MVCC。

InnoDB 完成一次更新操作的具体步骤:

  1. 开启事务
  2. 查询待更新的记录到内存,并加 X 锁
  3. 记录 undo log 到内存 buffer
  4. 记录 redo log 到内存 buffer
  5. 更改内存中的数据记录
  6. 提交事务,触发 redo log 刷盘
  7. 记录 bin log
  8. 事务结束

5.1 重做日志(redo log)

binlog 记录的是逻辑操作语句,偏向于过程记录。redo log 记录了对数据文件的物理更改,偏向于结果的记录,并保证总是日志先行(WAL,Write-Ahead Logging),即在持久化数据文件前,保证之前的 redo 日志已经写到磁盘。由于 redo log 是顺序整块写入,所以性能要更好。

重做日志两部分组成:

  • 重做日志缓冲(redo log buffer),是易失的
  • 重做日志文件(redo log file),是持久的

为什么需要 redo log?
在 MySQL 中,如果每一次的更新要写进磁盘,这么做会带来严重的性能问题:

  • Innodb 是以页为单位进行磁盘交互的,而一个事务很可能只修改一个数据页里面的几个字节,这时将完整的数据页刷到磁盘的话就太浪费资源了。
  • 一个事务可能涉及修改多个数据页,并且这些数据页在物理上并不连续,使用随机 IO 写入性能太差。

为了解决这个问题,MySQL 采用了 WAL 的思路来提升更新效率。当有记录需要更新,InnDB 把记录写到 redo log 中,并更新内存中的数据页,然后在空闲的时候或者是按照设定的更新策略将 redo log 中的内容异步更新到磁盘中的数据页(脏页刷盘)。

  • 当需要更新的数据页在内存中时,就会直接更新内存中的数据页
  • 不在内存中时,将更新操作记录在change buffer中(这时不需要从磁盘中读出数据页),并将操作记录到redo log中,防止机器意外关闭导致数据丢失。这时已经可以返回给客户端更新成功了,因为即使机器意外重启,也可以通过redo log找回数据
    • 当下一次查询命中这个数据页的时候,会先从磁盘中读取数据页到内存中,然后先执行change buffer的merge操作,保证数据逻辑的正确性
    • 除了查询操作外,系统有后台线程会定期merge
    • 数据库正常关闭(shutdown)的时候,也会进行merge操作

  • InnoDB在 buffer pool(innodb存储引擎带的一个缓存池)中变更数据时,首先会将相关变更写入 redo log buffer 中,后续某个时间点再一次性将多个操作记录写到 redo log file
  • 当重做日志写入磁盘后,缓冲池中的变更数据才会依据 checkpoint 机制择时写入到磁盘中
    • checkpoint:把脏页刷到磁盘的时间点,这个时间点之前的数据都已经保存到了持久存储


redo log buffer 写入 redo log file 实际上是先写入 OS Buffer,然后操作系统调用 fsync() 函数将日志刷到磁盘。

redo log 也是在磁盘上, InnoDB 引擎把日志记录写到 redo log 中也是一个写磁盘的过程,但是与更新过程不一样的是,更新过程是在磁盘上随机 IO,而写 redo log 是在磁盘上顺序 IO,效率要高。

redo log 的存在把全局的随机写变为了局部的顺序写,从而提高效率。

什么是 crash-safe?
crash-safe 指在 InnoDB 存储引擎中,事务提交过程中任何阶段,MySQL 突然奔溃,重启后都能保证事务的完整性,已提交的数据不会丢失,未提交完整的数据会自动进行回滚。

crash-safe 依赖的就是 redo log 和 undo log 两个日志。

比如:重启 innodb 时,首先会检查磁盘中数据页的 LSN ,如果数据页的 LSN 小于日志中 check point 的 LSN ,则会从 checkpoint 开始恢复。

  • LSN:InnoDB 使用的一个版本标记的计数,它是一个单调递增的值。数据页和 redo log 都有各自的 LSN。每次把 redo log 中的内容写入到实际的数据页之后,就会把 LSN 也同步过去。如果发生了宕机,我们可以根据数据页中的 LSN 值和 redo log 中 LSN 的值判断需要恢复的 redo log 的位置和大小

二阶段提交

  1. 在 mysql 里每当执行一个事务时,并不会时时的将数据修改同步到硬盘上,而是优先从 buffer pool 操作,数据不存在则会从磁盘加载后再操作;
  2. 当有事务执行操作后,会将刚刚对数据页的修改结果先记录到 redo log,然后才提交事务;
    当系统崩溃时,虽然buffer pool中的数据丢失,数据没有持久化。但是系统可以根据redo log的内容,将所有数据恢复到最新的状态
  3. 写入 redo log 时,事务标记为 prepare 状态;
  4. 当 binlog 也写入成功后才会真正的 commit。
    因为 binlog 用于从数据库的恢复,如果 redo log 写入成功,binlog 没有写入成功,那么就会导致主数据库有此操作结果的数据,而从数据库同步时缺失数据

5.2 回滚日志(undo log)

undo log 和bin log 一样也是逻辑日志,记录的是一种相反操作的记录,比如在回滚时,如果是 insert 操作时,则会逆向为 delete,delete 操作时,逆向为 insert 操作,更新则恢复到当时的版本数据。

回滚日志主要有两个作用:

  • 回滚
  • 多版本控制(MVCC),保证事务的原子性

6. DDL 日志

DDL 是数据定义语句,像 create,drop,alter语句。

DDL 日志记录了数据库里元数据的变更信息。它存放在数据目录下的ddl_log.log 文件中。

DDL 日志是一个二进制文件,不被人为的阅读修改,也没有其他配置项可以配置它,而且在成功启动 mysqld 后会被删除,只有在记录元数据时才会被重新创建。

应用

保证一致性

从数据库层面,数据库通过原子性、隔离性、持久性来保证一致性。

在ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是数据库为了保证一致性提供的手段。

MySQL通过两阶段提交来保证redo log和binlog的数据是一致的:

  • 阶段1:InnoDB redo log 写盘,InnoDB 事务进入 prepare 状态
  • 阶段2:binlog 写盘,InnoDB 事务进入 commit 状态

每个事务binlog的末尾,会记录一个 XID event,标志着事务是否提交成功。在恢复过程中,binlog 最后一个 XID event 之后的内容都应该被清除。

保证原子性

MySQL利用 Innodb 的 undo log 保证原子性。

当事务回滚时能够撤销所有已经成功执行的sql语句,记录了要回滚的相应日志信息。

保证持久性

MySQL利用 Innodb 的 redo log 保证持久性。

数据如果写入内存成功,但数据还没真正刷到磁盘,如果此时的数据库挂了,可以靠 redo log 来恢复内存的数据,这就实现了持久性。

将redo log进行刷盘比用数据页刷盘效率高,这也是采用redo log的优势。

  • redo log 只记录了哪一页修改,所以体积小,刷盘快。
  • redo log 是一直往末尾进行追加,属于顺序IO,效率显然比随机IO来的快。

总结

MySQL有两层,一层是Server层,一层是存储引擎层。

  • binary log 和 relay log 是 Server层的日志
    • binlog + relaylog 用于主从复制,会出现 case-unsave
    • binlog 是逻辑日志
  • redo log 和 undo log 是 InnoDB引擎特有的日志
    • redolog + undolog 解决buffer-pool数据不持久化,实现crash-safe
    • redolog 的两阶段提交,保证了redolog 和 binlog 的数据一致性
    • redolog 是物理日志
    • redolog 不具有像 binlog 一样归档的作用
posted @ 2022-03-23 21:01  当康  阅读(455)  评论(0编辑  收藏  举报