概述
在 mysql 数据库中,binlog 可是个相当重要的存在,它的全称为 binary log,也就是二进制日志。它就像是数据库的 “记忆本”,记录了所有的 DDL(数据定义语言)和 DML(数据操作语言)操作,像常见的 CREATE(创建)、INSERT(插入)、UPDATE(更新)以及 DELETE(删除)等操作都会被它一一记录下来。
binlog 有着多方面的重要作用,首先在数据恢复方面,当数据库不幸发生故障,或者出现数据丢失的情况时,我们就可以依靠 binlog 把数据恢复过来,以此确保数据的完整性和一致性。其次,在主从复制的场景里,它更是起到了关键作用,从库能够通过读取主库的 binlog 来实现数据同步,让主从库的数据时刻保持一致。而且,通过分析 binlog,我们还可以审计数据库的操作历史,便于监控数据库的运行状态和性能呢。另外,利用 binlog 进行增量备份也是个不错的选择,这样能提高备份效率,同时减少备份所花费的时间。
若想在 MySQL 中启用 binlog,那就需要在 MySQL 的配置文件(通常是 my.cnf 或 my.ini)里进行相应的参数设置,然后重启 MySQL 服务使其生效。具体操作如下:
先打开 MySQL 配置文件,一般它位于 /etc/mysql/my.cnf 或 /etc/my.cnf 这个路径下。接着找到或添加以下配置项:
[mysqld]server-id=1log-bin=/var/log/mysql/mysql-bin.logbinlog-format=row
这里面每个参数都有着独特的含义与作用哦。server-id 是每个 MySQL 实例的唯一标识符,在主从复制环境中,主库和从库的 server-id 必须不一样才行,这样才能准确区分不同的实例。log-bin 参数则是用来指定 Binlog 文件的存放位置以及文件名前缀,像上述配置里设置为 /var/log/mysql/mysql-bin.log,不过实际应用中,大家可以根据自己的需求自行指定路径和文件名前缀哦。而 binlog-format 是指定 Binlog 的格式,常见的格式有 STATEMENT(语句模式)、ROW(行模式)和 MIXED(混合模式),比较推荐使用 ROW 模式,因为它能够记录下更详细的改动信息呢。
除了上述这些启用 binlog 的基本配置外,还有一些其他的配置参数,可以进一步对 Binlog 的性能和行为进行优化。比如说 max_binlog_size,它是用来指定单个 Binlog 文件的最大大小的,例如设置 “max_binlog_size=100M”,那就意味着当 Binlog 文件达到 100MB 这个大小时,系统会自动创建新的 Binlog 文件啦。再比如 expire_logs_days,它可以指定 Binlog 文件的过期时间,像设置 “expire_logs_days=7”,就是说 Binlog 文件在 7 天后会自动被清理掉,避免占用过多磁盘空间。还有 sync_binlog 参数,它指定了每次事务提交后,是否要立即将 Binlog 同步到磁盘,当设置 “sync_binlog=1” 时,每次事务提交后就会马上同步 Binlog 到磁盘,这样虽然提高了数据的安全性,但相应地也会降低一定的性能哦。另外,binlog_do_db 可以指定需要记录 Binlog 的数据库名,例如 “binlog_do_db=my_database”,那就只会记录这个特定数据库的操作了;而 binlog_ignore_db 则是指定不记录 Binlog 的数据库名,像 “binlog_ignore_db=information_schema”,就表示不会记录这个数据库的操作啦。
查看 Binlog 文件有相应的方法哦。如果想列出所有 Binlog 文件,可以使用 “mysql -uroot -p -e "SHOW BINARY LOGS;"” 这个命令,执行后它会列出当前数据库中所有的 Binlog 文件及其大小,就像下面这样的展示形式:
+------------------+-----------+|Log_name |File_size |+------------------+-----------+| mysql-bin.000001 |1073741824|| mysql-bin.000002 |1073741824|+------------------+-----------+
要是想查看某个 Binlog 文件的具体内容,那就可以使用 “mysqlbinlog /var/log/mysql/mysql-bin.000001” 这个命令啦(这里的文件路径根据实际情况来定哦),执行后就能看到指定 Binlog 文件里详细记录的内容了。
对于 Binlog 文件的清理操作,通常可以通过设置 expire_logs_days 参数来让系统自动完成清理工作。不过要是需要手动清理的话,也有对应的命令哦。比如先查看当前清理策略,可以使用 “mysql -uroot -p -e "SHOW VARIABLES LIKE 'expire_logs_days';"” 这个命令,它会显示当前设定的清理策略情况。要是想手动清除所有比指定 Binlog 文件更早的文件,可以使用 “mysql -uroot -p -e "PURGE BINARY LOGS TO 'mysql-bin.000001';"” 命令,举个例子,假设执行命令前的 Binlog 文件状态是有 mysql-bin.000001、mysql-bin.000002、mysql-bin.000003 这些文件,执行完这个命令后,就只剩下 mysql-bin.000001 这个文件了哦。还有一种情况,如果想清除指定时间之前的 Binlog 文件,可以使用 “mysql -uroot -p -e "PURGE BINARY LOGS BEFORE '2023-01-01 00:00:00';"” 命令,这里的时间格式要按照 YYYY-MM-DD HH:MM:SS 来写哦。
在数据恢复与备份这个场景中,binlog 有着很大的用处。当数据库发生故障或者出现数据丢失的糟糕情况时,我们可以借助 binlog 来恢复数据哦。比如说,假设数据库在某一天凌晨发生故障了,那可以按照以下步骤进行恢复操作:
第一步,先停止 MySQL 服务,使用 “mysqladmin -u root -p shutdown” 命令来实现。
第二步,恢复数据文件,这一步通常需要从之前准备好的备份文件中把数据文件恢复出来哦。
第三步,使用 Binlog 恢复数据,通过 “mysqlbinlog /var/log/mysql/mysql-bin.000001| mysql -u root -p” 这样的命令,就能把指定 Binlog 文件中的操作应用到数据库里,从而实现数据的恢复啦。
在主从复制环境里,binlog 同样起着关键作用呢。主库会把所有的操作都记录到 Binlog 当中,然后从库通过读取主库的 Binlog 来实现数据同步。具体的配置操作如下:
在主库这边,配置如下:
[mysqld]server-id=1log-bin=/var/log/mysql/mysql-bin.logbinlog-do-db=my_database
这里的 server-id 设为主库的唯一标识符,log-bin 指定了 Binlog 的存放位置,而 binlog-do-db 则指定了需要同步的数据库哦。
在从库这边,配置如下:
[mysqld]server-id=2log-slave-updatesread-only=1
这里的 server-id 设为从库的唯一标识符,log-slave-updates 表示从库会将接收到的 Binlog 记录到自己的 Binlog 中,read-only 表示从库设置为只读状态,不允许进行写操作哦。
配置好之后,先通过相应命令配置从库的主库信息,并指定开始同步的位置,最后使用 “START SLAVE;” 命令来启动从库的复制进程,这样一来,从库就能顺利地和主库进行数据同步啦。
概念
在 MySQL 中,redolog(重做日志)可是保障数据持久性与完整性的关键所在,它属于物理日志哦。简单来说,它主要记录的是数据页的物理修改情况呢。大家都知道,MySQL 在运行时,对数据的增删改等操作往往是先在内存中进行的,要是这时候系统出现异常,像突然断电或者宕机了,那内存里修改的数据可就丢失啦。而 redolog 就是为了解决这个问题诞生的,它会把对数据页做的修改记录下来,然后在合适的时机刷入磁盘,就算遇到意外情况,后续也能依靠这些记录把数据恢复过来,确保数据不会因为系统异常而丢失呢。
redolog 的数据存储涉及两部分,一部分是在内存中的日志缓存,叫做 redo log buffer,另一部分则是位于磁盘中的重做日志文件,也就是 redo log file 啦。
redo log buffer 作为内存中的缓存区域,会先接收数据修改时产生的日志记录哦。当执行 SQL 语句对数据进行修改时,这些修改内容会先写入 redo log buffer 里暂存起来呢。
而 redo log file 是实实在在存储在磁盘上的啦,它由多个 redo log block 组成,这些 redo log block 每个大小通常是固定的哦,例如有的是 512KB 大小呢。同时,还有 log group 这样的概念,它其实是多个 redo log file 的逻辑组合,一般默认会有 2 个 redo log file(像命名为 ib_logfile0、ib_logfile1 等)共同组成一个 log group 呀。它们相互配合,为 redolog 的存储和后续刷盘等操作提供了基础架构呢。
redolog 从内存刷入磁盘可是有多种时机的哦。
首先,有个重要的参数 innodb_flush_log_at_trx_commit 可以控制刷盘策略呢。它有三个取值,不同取值对应不同刷盘情况哦。当取值为 0 时,表示每次事务提交时将 redo log 留在 redo log buffer 中,该模式下在事务提交时不会主动触发写入磁盘的操作,而是由 MySQL 的后台线程每隔 1 秒,把缓存在 redo log buffer 中的 redo log,通过调用 write () 写到操作系统的 Page Cache,然后调用 fsync () 持久化到磁盘,不过这种情况下要是 MySQL 进程崩溃了,就会导致上一秒钟所有事务数据的丢失呢;当取值为 1 时,每次事务提交时,都会直接把缓存在 redo log buffer 里的 redo log 持久化到磁盘,这样哪怕 MySQL 异常重启之后,数据也不会丢失哦;当取值为 2 时,每次事务提交时,只是把缓存在 redo log buffer 里的 redo log 写到 redo log 文件,也就是写到操作系统的 Page Cache,后续再通过调用 fsync 将其持久化到磁盘,相较于取值为 0 的情况,这种方式更安全些,只有在操作系统崩溃或者系统断电的情况下,上一秒钟所有事务数据才可能丢失呀。
其次,当 redo log buffer 中记录的写入量大于 redo log buffer 内存空间的一半时,也会触发刷盘落盘哦。这是因为 redo log buffer 是一个环状的内存结构,会被反复利用呀,当使用量达到一半就得把里面的数据往磁盘送送啦,腾出空间来继续接收新的日志记录呢。
再者,innodb 的后台线程每隔 1 秒,也会将 redo log buffer 持久化到磁盘哦,让数据能及时保存下来呢。
还有哦,当发生 checkpoint 动作时,也会涉及 redolog 的刷盘呢,这主要是为了保证数据的一致性和可恢复性啦。
最后,当 MySQL 正常关机时,系统也会把 redo log buffer 里还没来得及刷盘的数据刷入磁盘,确保数据完整地保存下来哦。
在 MySQL 中,undolog(回滚日志)是一种用于撤销回退的日志。当我们对数据库执行事务操作时,在事务没提交之前,MySQL 会先记录更新前的数据到 undolog 日志文件里面。这样一来,要是事务需要回滚,或者在数据库崩溃等意外情况发生时,就可以利用 undolog 进行数据回退,将数据恢复到事务开始之前的状态。比如说,执行一条 “delete from user where id = 1;” 的删除语句时,undolog 就会记录一条对应的 “insert into user (id) values (1);” 这样的反向插入语句,以保证在事务回滚时,能够把数据还原回去。再比如执行 “update user set name = ' 李四 ' where id = 1;”(假设修改之前 name = ' 张三 ')这条更新语句时,undolog 会记录一条相反的 “update user set name = ' 张三 ' where id = 1;” 语句,要是这个修改出现异常,就可以通过 undolog 日志来实现回滚操作,从而保证事务的一致性。
undolog 在 MySQL 数据库中有着两个非常重要的作用。
其一,提供回滚操作,以此实现事务的原子性。在进行数据更新操作时,MySQL 不仅会记录 redo log,还会记录 undolog。倘若因为某些原因,比如出现错误或者用户执行了 ROLLBACK 语句,导致事务需要回滚,那么 MySQL 就会利用 undolog 将数据恢复到事务开始之前的状态,确保事务中的所有操作要么全部成功执行,要么全部回滚,满足事务原子性的要求。
其二,在 InnoDB 存储引擎中实现多版本并发控制(MVCC)。当读取的某一行数据被其他事务所锁定时,通过 undolog 可以分析出该行记录以前的数据版本情况,进而让用户能够读取到当前事务操作之前的数据,也就是实现了 “快照读”(普通的 SELECT 语句就是快照读)。这一功能在提升数据库并发性能方面起着关键作用,能让不同事务在一定程度上互不干扰地读取数据。
undolog 的存储是由 InnoDB 存储引擎来实现的,并且数据保存在 InnoDB 的数据文件中。它采用分段(segment)的方式进行存储,其中 rollback segment(回滚段)是重要的组成部分。
每个回滚段中包含 1024 个 undo log segment。在 MySQL 5.5 之前,系统只支持 1 个 rollback segment,也就是最多只能记录 1024 个 undo 操作;而在 MySQL 5.5 之后,可以支持 128 个 rollback segment,分别从 resg slot0 - resg slot127,每一个 resg slot(即每一个回滚段)内部由 1024 个 undo segment 组成,总共能够记录 128 * 1024 个 undo 操作。
在 undolog 日志里面,不仅存放着数据更新前的记录,还会记录 RowID(行标识)、事务 ID(每次递增)、回滚指针等信息。回滚指针的作用很关键,例如第一次如果是 insert 语句的话,回滚指针为 NULL,第二次 update 之后的 undolog 的回滚指针就会指向刚刚那一条 undolog 日志,依此类推,便会形成一条 undolog 的回滚链,方便后续查找该条记录的历史版本。
在更新数据之前,MySQL 会提前生成 undolog 日志。当事务提交的时候,并不会立即删除 undolog,这是因为后面可能还需要进行回滚操作。当执行回滚(rollback)操作时,会从缓存中读取数据。而 undolog 日志的删除是通过后台 purge 线程进行回收处理的。
举个例子来说明其工作过程,假设有事务 A 执行 update 操作,此时事务还没提交,会先将数据备份到对应的 undo buffer,然后由 undo buffer 持久化到磁盘中的 undolog 文件中,这时 undolog 就保存了未提交之前的操作日志,接着将操作的数据(比如 Teacher 表的数据)持久保存到 InnoDB 的数据文件 IBD 中。要是此时事务 B 进行查询操作,会直接从 undo buffer 缓存中进行读取,因为这时事务 A 还没提交事务,如果要回滚事务,也是先直接从 undo buffer 缓存读取数据。
再简化来看整个利用 undolog 实现原子性和持久化的事务过程:假设有 A、B 两个数据,初始值分别为 1、2。首先事务开始,接着记录 A = 1 到 undolog 中,然后修改 A = 3,再记录 B = 2 到 undolog 中,之后修改 B = 4,随后将 undolog 写到磁盘(实现 undolog 持久化),再把数据写到磁盘(实现数据持久化),最后事务提交。之所以这样能同时保证原子性和持久化,是因为更新数据前会记录 undolog,并且为保证持久性,数据在事务提交前要写到磁盘,而且 undolog 必须先于数据持久化到磁盘。如果在数据和 undolog 都持久化完成后、事务提交前发生系统崩溃,undolog 是完整的,可以用来回滚;要是在数据还没持久化到磁盘时就发生系统崩溃,那磁盘上的数据还是保持在事务开始前的状态。
有几个和 undolog 相关的重要参数值得关注。
首先是 innodb_undo_directory 参数,它用于指定 undolog 日志的存储目录,默认值为 “./”,我们可以根据实际的存储规划和需求,通过修改这个参数来调整 undolog 的存储位置。
还有 innodb_undo_logs 参数(在不同版本中可能有细微差异),它与可以支持的 undo log 数量等相关,像前面提到的回滚段数量等配置,一定程度上会和这个参数共同作用来管理 undolog 的相关资源分配情况。
另外,innodb_undo_tablespaces 参数也很关键,它可以设置 undo 独立表空间个数,范围为 0 - 128,默认值为 0。当设置为 0 时,表示不开启独立 undo 表空间,且 undo 日志存储在 ibdata 文件中;而如果设置了合适的数值开启独立表空间,能在一定程度上更灵活地管理 undolog 的存储以及相关性能优化等情况,不过这个参数只能在最开始初始化 MySQL 实例的时候指定,如果实例已创建,通常是不能变动的,如果在数据库配置文件中指定的值大于实例创建时的指定个数,数据库启动会失败并提示该参数设置有误。
合理地配置这些参数,能够帮助我们更好地管理和优化 undolog 在 MySQL 中的使用,使其更好地发挥作用,满足不同业务场景下对于事务回滚、数据一致性以及性能等方面的要求。
在 MySQL 的实际运行过程中,binlog、redolog、undolog 这三大日志相互配合,共同保障数据库操作的准确性、完整性以及各类功能的顺利实现。
比如在数据更新操作时,当执行一条如 “UPDATE user SET age = 25 WHERE id = 1;” 这样的语句,首先 InnoDB 存储引擎层会介入。在真正对数据进行变更前,会先将当前的数据状态保存到 undolog 中,形成一个可以用于回滚的历史版本,就像把原来 id 为 1 的用户年龄记录下来,方便后续万一需要回滚能恢复到这个状态。
接着,会把这次数据修改对应的物理变化记录到 redolog 里,不过这时的 redolog 处于 prepare 状态,例如记录了对应数据页上关于这个用户年龄修改的相关物理变动情况。而且这个记录并不是直接就落盘到磁盘的,会先暂存在 redo log buffer 中,其落盘时机受到 innodb_flush_log_at_trx_commit 参数控制,像当参数为 1 时,每次事务提交就会直接把 redo log buffer 里的日志持久化到磁盘;为 0 时,则由后台线程每隔 1 秒去处理刷盘;为 2 时,是先写到操作系统的 Page Cache 后续再持久化到磁盘等情况。
之后,MySQL 的 server 层会生成这条更新操作对应的 binlog,记录这个语句逻辑上的更新情况,比如记录了执行了这么一条把 id 为 1 的用户年龄更新为 25 的语句,它同样先是写到 binlog cache 中,然后由 sync_binlog 参数决定何时刷盘到磁盘,像 sync_binlog 为 1 时,提交事务时就会立马刷新到磁盘。
最后,当事务要正式提交时,会把 redolog 的状态从 prepare 改为 commit 状态,至此整个事务才算真正完成提交。如果在这个过程中出现意外,比如数据库突然宕机了,重启后恢复的流程中,MySQL 会先检查 redolog 的状态,如果发现是 commit 状态,就认为事务是成功的;要是处于 prepare 状态,则会去查看对应事务的 binlog 是否完整,完整的话就对还未提交的事务进行提交,不完整就回滚事务,以此保证数据的一致性和持久性。
再来看事务回滚的场景,假设执行了一系列更新操作后,由于某些原因执行了 ROLLBACK 语句来回滚事务。这时,就会利用之前记录的 undolog 来进行数据回退,把数据恢复到事务开始之前的状态,比如之前修改过的用户信息等都会还原回去,而 redolog 和 binlog 在这种回滚情况下,就不会继续按照提交的流程去进行刷盘等操作了,保证了数据能正确地回滚到之前的版本。
在主从复制的环境里,主库在执行各种操作时,会同时记录 binlog、redolog 等日志。主库将操作记录到 Binlog 当中后,从库通过读取主库的 Binlog 来实现数据同步,在这个过程中,从库接收到 Binlog 后也会涉及自身相关的日志记录以及数据更新操作,同样依赖于类似的日志协作机制来保证数据准确地同步过来并且保持一致性,一旦出现数据不一致等问题,也可以依靠这些日志来进行排查和恢复。
下面从多个角度对 binlog、redolog、undolog 这三大日志进行对比总结:
通过这样的对比总结,能更清晰地区分这三大日志的不同特点