起因

使用完电脑后,我习惯将电脑设置成睡眠模式,以便下次更快地启动。之前就注意到一个问题,如果睡眠之前不将MySQL完全退出,那么下次MySQL下一次启动时,有极大概率导致无法启动。

此后便养成了电脑睡眠前将开发环境完全退出地习惯。就在刚才,开发环境XAMMP上的MySQL又双叒启动失败了,电脑设置睡眠前我确实完全退出开发环境了啊!我无法理解。

XAMPP的错误日志

image-20231113152613578

14:57:19  [mysql]   Attempting to start MySQL app...
14:57:20  [mysql]   Status change detected: running
14:57:20  [mysql]   Status change detected: stopped
14:57:20  [mysql]   Error: MySQL shutdown unexpectedly.
14:57:20  [mysql]   This may be due to a blocked port, missing dependencies, 
14:57:20  [mysql]   improper privileges, a crash, or a shutdown by another method.
14:57:20  [mysql]   Press the Logs button to view error logs and check
14:57:20  [mysql]   the Windows Event Viewer for more clues
14:57:20  [mysql]   If you need more help, copy and post this
14:57:20  [mysql]   entire log window on the forums

看看XAMPP的错误日志,基本上说了和没说一样,没有任何错误相关的信息,只是说去看看MySQL日志。

MySQL错误日志

image-20231113153234898

2023-11-13 14:57:19 0 [Note] Starting MariaDB 10.4.28-MariaDB source revision c8f2e9a5c0ac5905f28b050b7df5a9ffd914b7e7 as process 22296
2023-11-13 14:57:19 0 [Note] InnoDB: Mutexes and rw_locks use Windows interlocked functions
2023-11-13 14:57:19 0 [Note] InnoDB: Uses event mutexes
2023-11-13 14:57:19 0 [Note] InnoDB: Compressed tables use zlib 1.2.12
2023-11-13 14:57:19 0 [Note] InnoDB: Number of pools: 1
2023-11-13 14:57:19 0 [Note] InnoDB: Using SSE2 crc32 instructions
2023-11-13 14:57:19 0 [Note] InnoDB: Initializing buffer pool, total size = 16M, instances = 1, chunk size = 16M
2023-11-13 14:57:19 0 [Note] InnoDB: Completed initialization of buffer pool
2023-11-13 14:57:19 0 [ERROR] InnoDB: Missing MLOG_CHECKPOINT at 28622527 between the checkpoint 28622527 and the end 28622336.
2023-11-13 14:57:19 0 [ERROR] InnoDB: Plugin initialization aborted with error Generic error
2023-11-13 14:57:20 0 [Note] InnoDB: Starting shutdown...
2023-11-13 14:57:20 0 [ERROR] Plugin 'InnoDB' init function returned error.
2023-11-13 14:57:20 0 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
2023-11-13 14:57:20 0 [Note] Plugin 'FEEDBACK' is disabled.
2023-11-13 14:57:20 0 [ERROR] Unknown/unsupported storage engine: InnoDB
2023-11-13 14:57:20 0 [ERROR] Aborting

看到了关键信息:

[ERROR] InnoDB: Missing MLOG_CHECKPOINT at 28622527 between the checkpoint 28622527 and the end 28622336

InnoDB:检查点 28622527 和末尾 28622336 之间的 28622527 处缺少 MLOG_CHECKPOINT

MLOG_CHECKPOINT

MySQL 8.0.30之前,InnoDB默认在数据目录中创建两个重做日志(Redo Log)文件,名为 ib_logfile0ib_logfile1,并以循环方式写入这些文件。

重做日志是一种基于磁盘的数据结构,在崩溃恢复期间用于纠正不完整事务写入的数据。在正常操作期间,重做日志会对由 SQL 语句或低级 API 调用产生的更改表数据的请求进行编码。在初始化期间和接受连接之前,会自动重播在意外关闭之前未完成更新数据文件的修改。

重做日志在磁盘上由重做日志文件物理表示。写入重做日志文件的数据根据受影响的记录进行编码,这些数据统称为重做。通过重做日志文件的数据传递由不断增加的LSN值表示。当数据修改发生时,重做日志数据会被附加,并且随着检查点的进展,最旧的数据会被截断。——摘录自MySQL DOCUMENTATION 15.6.5 Redo Log

MLOG_CHECKPOINT是什么

在MySQL中,MLOG_CHECKPOINT是用于记录和管理事务日志(transaction log)的一种机制。事务日志是MySQL中的一种重要的持久化机制,用于记录数据库引擎执行的所有修改操作,以确保数据的一致性和持久性。

MLOG_CHECKPOINT是事务日志中的一种特殊记录类型,用于标记事务日志中的检查点(checkpoint)。检查点是事务日志中的一个关键点,表示在该点之前的所有操作已经成功地写入到磁盘中。

当MySQL执行一次检查点操作时,它会将一个MLOG_CHECKPOINT记录写入事务日志。这个记录包含了一些元数据,如检查点的位置和相关的信息。写入MLOG_CHECKPOINT记录的目的是为了在数据库崩溃或重启后,MySQL可以根据检查点的位置来确定需要恢复的事务日志的起始点,以确保数据的一致性。

重做日志(Redo Log)是事务日志的一部分,它是InnoDB存储引擎特有的日志文件。重做日志记录了对数据库进行的物理修改操作,例如对数据页的修改。MLOG_CHECKPOINT记录则是被写进了重做日志(Redo Log)文件里。

MLOG_CHECKPOINT为何丢失

其中一个主要原因是MySQL异常崩溃或非正常关闭。如果MySQL在执行检查点操作之前发生异常崩溃或非正常关闭,可能会导致MLOG_CHECKPOINT记录没有被写入事务日志。这种情况下,检查点可能会缺失MLOG_CHECKPOINT记录。

这让我不禁怀疑XAMPP关闭MySQL的逻辑,是通过MySQL命令退出,还是粗暴地结束MySQL的进程?

在XAMPP目录下,找到了一个用于停止MySQL的批处理文件mysql_start.bat

@echo off
cd /D %~dp0
echo Mysql shutdowm ...
cmd.exe /C start "" /MIN call "@@BITROCK_INSTALLDIR@@\killprocess.bat" "mysqld.exe"

if not exist mysql\data\%computername%.pid GOTO exit
echo Delete %computername%.pid ...
del mysql\data\%computername%.pid

:exit

这里的脚本指向另外一个批处理文件killprocess.bat,看看里面是什么内容。

@echo off 
SetLocal EnableDelayedExpansion
set "PID_LIST="

for /f "tokens=2" %%V in ('tasklist.exe ^| findstr /i "%1" 2^>NUL') do @set "PID_LIST=!PID_LIST! /PID %%V"

if defined PID_LIST (
  taskkill.exe /F %PID_LIST%
) else (
  echo Process %1 not running
)

SetLocal DisableDelayedExpansion
exit

总体而言,通过上面两个批处理文件可以看出来,整个关闭MySQL程序的流程是这样的:

  1. 查找名为mysqld.exe的程序
  2. 获取其PID
  3. 使用taskkill.exe命令通过PID强制终止进程。

这是一个非常简单粗暴的方法!我认为这个不太正确的关闭做法,导致了问题发生。

MySQL DOCUMENTATION给出了一个关闭MySQL的过程:5.1.19 The Server Shutdown Process

  1. 使用mysqladmin shutdown命令请求关闭服务
  2. 创建一个关闭线程(可选)
  3. 停止接受新的连接
  4. 终止当前活动
  5. 关闭存储引擎
  6. 退出服务

不正确的关闭方式,可能会导致MySQL 服务启动后出现任何不可预见的崩溃。

解决办法

错误的根源在于MySQL不正确的关闭,最佳的解决办法是改变XAMPP关闭MySQL的逻辑,但是我没有这么做。

我在Stack Exchange找到了一个解决办法:删除Redo Log,即删除所有MLOG_CHECKPOINT记录。具体而言,检查点缺失MLOG_CHECKPOINT记录并不一定意味着数据的完整性受到了影响,因为MySQL有其他机制来保证数据的一致性和可恢复性。例如,MySQL使用redo log和undo log来记录和恢复事务操作。当MySQL启动时,它会根据redo log的内容进行恢复,而不仅仅依赖于MLOG_CHECKPOINT记录。

有必要说明的一点是,这是在这是一个开发环境的前提下才能做的。删除Redo Log有可能会导致数据丢失,即便是万分之一的概率,这在生产环境中也是不可接受的。如果你在生产环境中遇到了和我一样的错误,请务必不要像我说的一样去做,删除Redo Log文件。

前文也提及过,Redo Log文件的文件名为ib_logfile0ib_logfile1,所以只需在mysql -> data目录中删除这两个文件,重启MySQL即可。

参考资料:

  1. MySQL or MariaDB is faulted due a startup error
  2. InnoDB Redo Log
  3. InnoDB: Ignoring the redo log due to missing MLOG_CHECKPOINT
  4. MySQL Documentation
この素晴らしい世界に祝福を!
最后更新于 2023-11-14