起因
使用完电脑后,我习惯将电脑设置成睡眠模式,以便下次更快地启动。之前就注意到一个问题,如果睡眠之前不将MySQL完全退出,那么下次MySQL下一次启动时,有极大概率导致无法启动。
此后便养成了电脑睡眠前将开发环境完全退出地习惯。就在刚才,开发环境XAMMP上的MySQL又双叒启动失败了,电脑设置睡眠前我确实完全退出开发环境了啊!我无法理解。
XAMPP的错误日志
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错误日志
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_logfile0
和ib_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程序的流程是这样的:
- 查找名为
mysqld.exe
的程序 - 获取其
PID
- 使用
taskkill.exe
命令通过PID
强制终止进程。
这是一个非常简单粗暴的方法!我认为这个不太正确的关闭做法,导致了问题发生。
MySQL DOCUMENTATION给出了一个关闭MySQL的过程:5.1.19 The Server Shutdown Process:
- 使用mysqladmin shutdown命令请求关闭服务
- 创建一个关闭线程(可选)
- 停止接受新的连接
- 终止当前活动
- 关闭存储引擎
- 退出服务
不正确的关闭方式,可能会导致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_logfile0
和 ib_logfile1
,所以只需在mysql
-> data
目录中删除这两个文件,重启MySQL即可。
参考资料:
Comments NOTHING