本篇文章主要讲解排查问题的思路, 涉及 Linux 删除文件的原理, 实例误删数据恢复, MySQL 实例初始化参数优先级别等, 虽然涉及知识点比较浅, 但是个人觉得挺有意思的, 所以翻出笔记发布出来.
1 备份出错咯
测试环境测试 xtrabackup 相关性能的时候, 备份失败!
备份指令如下:
1 innobackupex --defaults-file=/apps/conf/MySQL/mysql5_3306.cnf --socket=/tmp/mysql3306.sock --user=[*]--password=[*] --no-timestamp /apps/mysql_backup/test_backup_1> /apps/mysql_backup/backup.log 2>&1
备份错误日志如下:
- xtrabackup: The latest check point (for incremental): '1209962'
- xtrabackup: Stopping log copying thread.
- .xtrabackup: warning: Log block checksum mismatch (block no 2364 at lsn 1209856):
- expected 1161267116, calculated checksum 1312610971
- xtrabackup: warning: this is possible when the log block has not been fully written by the server, will retry later.
- 180912 15:30:11>> log scanned up to (1209856)
- 180912 15:30:11 Executing UNLOCK TABLES
- 180912 15:30:11 All tables unlocked
- 180912 15:30:11 Backup created in directory '/apps/mysql_backup/test_backup_1/'
- MySQL binlog position: filename 'MySQL-bin.000001', position '26708128'
- 180912 15:30:11 [00] Writing /apps/mysql_backup/test_backup_1/backup-my.cnf
- 180912 15:30:11 [00] ...done
- 180912 15:30:11 [00] Writing /apps/mysql_backup/test_backup_1/xtrabackup_info
- 180912 15:30:11 [00] ...done
- xtrabackup: Transaction log of lsn (1209962) to (1209856) was copied.
- xtrabackup: error: last checkpoint LSN (1209962) is larger than last copied LSN (1209856).
- Tips:
数据库实例运行正常的情况, 在各个 log buffer 中, 会存有 各个 LSN, 可以通过 show engine innodb status 查看, 但是注意, 这个 lsn 并非是直接从磁盘文件获取, 而是从 buffer 中获取. 说明如下:
- dba@localhost : (none) 17:00:21> show engine innodb status \G
- ......
- ---
- LOG
- ---
- Log sequence number 4158179984
- Log flushed up to 4158179984
- Pages flushed up to 4158179984
- Last checkpoint at 4158179975
- 0 pending log flushes, 0 pending chkp writes
- 128445 log i/o's done, 0.00 log i/o's/second
- ......
- # Log sequence number: 当前系统最大的 LSN 号
- # log flushed up to: 当前已经写入 redo 日志文件的 LSN
- # pages flushed up to: 已经将更改写入脏页的 lsn 号
- # Last checkpoint at: 系统最后一次刷新 buffer pool 脏中页数据到磁盘的 checkpoint
- # LSN1 = Log sequence number
- # LSN2 = log flushed up to
- # LSN3 = pages flushed up to
- # LSN4 = Last checkpoint at
- # LSN1>= LSN2>= LSN3>= LSN4
2 备份错误解读
xtrabackup: error: last checkpoint LSN (1209962) is larger than last copied LSN (1209856).
说明 xtrabckup 同步 redo log 到完成的时候, 自身的 redo log 最后的 lsn 跟 当前数据库的 ib_logfile 文件最后的 lsn 对比, 发现不匹配.
先检查备份失败实例内的 ib_logfile 文件:
正常 ib_logfile 文件的 change time 应该是跟数据库的初始化时间一致, 因为没有修改 文件的权限, 但是显示的 change time 明显晚与实例的运行时间, 怀疑是否这几个 ib_logfile[*] 文件被覆盖!
为验证 ib_logfile[*] 文件被覆盖, 检查 MySQL 进程下是否有 deleted 句柄, 截图如下, 发现 ib_logfile[*] 文件确实被覆盖.
3 排查问题
哇, 原来 文件被删除, 这里有几个疑问 :
为何文件被删除, 不影响 3306 实例 mysqld 的运行, 3306 仍支持正常的 dml 及 ddl 操作?
306 的 redo log 是写入到哪些地方?
3306 实例丢失的文件能否恢复, 能否正常使用?
为何 只有 redo log 文件被删除, 是什么操作导致?
3.1 文件被删, 实例为何能正常运行
在 Linux 中, 每个文件都有两个 link 计数器:
i_count: 文件使用者或者被调用的数量, 理解为内存引用的计数器. 文件被进程打开使用的时候, 自增 + 1.
i_nlink: 硬链接的数量, 理解为磁盘引用计数器. 创建文件的硬链接的时候, 自增 + 1.
当我们执行 rm 操作的时候, 实际是 i_nlink-1, 不一定真正删除文件, 只有当 i_nlink=0 & i_count=0 时, 文件才会被真正删除. 案例中的 ib_logfile[*] 由于没有新创建硬链接, 所以 i_nlink = 1, 加上此时 3306 实例处于运行中, 需要调用 到 ib_logfile[*] 文件, 所以 i_count = 1( 当前无其他进程使用到 ib_logfile[*] ), 当文件被删除的时候, i_nlink =0 但是 i_count=0, 故文件不会被真正删除, 仅删除 inode 连接, 并没有删除 磁盘的数据块.
那么被进程调用的文件, 遭遇 rm 操作, 那么它将何去何从呢?
首先, 该文件的 i_nlink 被删除, 剩下 i_count, 故仅删除磁盘硬链接, 内容未删除. 可以通过 proc 文件系统查找文件. 每个进程都有进程 id, 可以通过 proc 文件系统查找到该进程打开及调用的文件的链接.
测试 1: 在运行的新实例 3006 上, 删除 3 个文件后检查
3.2 数据实际写入到哪里, 原被覆盖文件? proc 文件系统文件?
测试 2: 删除表格文件, 查看句柄 size 是否变化?
测试内容: 删除 ib_logfile0 文件及 tbb.ibd 文件, 往 tbb 表格插入数据, 查看 ib_logfile0 大小及 tbb.ibd 大小
测试说明: 为何是查看大小, 而不是查看 change time 呢? 因为 node 链接存储了文件的属性, 删除了该链接, 则无法查看文件属性, 仅能通过 lsof 查看文件大小来判断写入情况./proc 文件系统中的 deleted 软连接, stat 查看也是查看软连接的属性, 并非 真实文件数据块. 无论是 root 还是其他用户删除, 这个文件都能写入.
测试过程: 见截图
测试结果: 因为 ib_logfile 是固定大小 1G, 无法查看到 change 时间, 故不能验证; 但是可以从 tbb.ibd 文件大小得知, 实际是 redo log 是写入到 被删除的文件的.
3.3 恢复被删除实例数据文件
若是主库文件被删
检查从库是否正常, 是否无删除文件
从库正常
方案 1
主从切换
旧主上, 根据 deleted 句柄重定向被删除的数据块到原先的磁盘位置, 例: cat /proc/16979/fd/36> /apps/dbdat/mysql5_data3306/sophia/tba.ibd
方案 2
重做 旧主库, 重做后恢复从库使用
从库不正常, 文件也被删
从库, 停止复制
从库, 根据 deleted 句柄重定向被删除的数据块到原先的磁盘位置, 例: cat /proc/16979/fd/36> /apps/dbdat/mysql5_data3306/sophia/tba.ibd
从库, 恢复复制
检查从库追上主库
主从切换
旧主新从, 停止复制, 重定向文件内容 或者重做 从库
若是从库文件被删
方案 1
从库停止复制
从库上, 根据 deleted 句柄重定向被删除的数据块到原先的磁盘位置, 例: cat /proc/16979/fd/36> /apps/dbdat/mysql5_data3306/sophia/tba.ibd
方案 2
重做 旧主库, 重做后恢复从库使用
3.4 为何仅 ib_logfile[*] 文件被删除, 为何被删
查看 MySQL 3306 的 error 看看有啥苗头:
- 180912 15:09:51 [Note] Plugin 'FEDERATED' is disabled.
- 180912 15:09:51 InnoDB: The InnoDB memory heap is disabled
- 180912 15:09:51 InnoDB: Mutexes and rw_locks use GCC atomic builtins
- 180912 15:09:51 InnoDB: Compressed tables use zlib 1.2.3
- 180912 15:09:51 InnoDB: Using Linux native AIO
- 180912 15:09:51 InnoDB: Initializing buffer pool, size = 1.0G
- 180912 15:09:51 InnoDB: Completed initialization of buffer pool
- InnoDB: The first specified data file /apps/dbdat/mysql5_data3306/ibdata1 did not exist:
- InnoDB: a new database to be created!
- 180912 15:09:51 InnoDB: Setting file /apps/dbdat/mysql5_data3306/ibdata1 size to 1000 MB
- InnoDB: Database physically writes the file full: wait...
- InnoDB: Progress in MB: 100 200 300 400 500 600 700 800 900 1000
- 180912 15:09:55 InnoDB: Log file /apps/dbdat/mysql5_data3306/ib_logfile0 did not exist: new to be created
- InnoDB: Setting log file /apps/dbdat/mysql5_data3306/ib_logfile0 size to 1000 MB
- InnoDB: Database physically writes the file full: wait...
- InnoDB: Progress in MB: 100 200 300 400 500 600 700 800 900 1000
- 180912 15:09:58 InnoDB: Log file /apps/dbdat/mysql5_data3306/ib_logfile1 did not exist: new to be created
- InnoDB: Setting log file /apps/dbdat/mysql5_data3306/ib_logfile1 size to 1000 MB
- InnoDB: Database physically writes the file full: wait...
- InnoDB: Progress in MB: 100 200 300 400 500 600 700 800 900 1000
- 180912 15:10:01 InnoDB: Log file /apps/dbdat/mysql5_data3306/ib_logfile2 did not exist: new to be created
- InnoDB: Setting log file /apps/dbdat/mysql5_data3306/ib_logfile2 size to 1000 MB
看 log, 是 新建实例的时候, 覆盖了 3306 实例的 ib_logfile 跟 ibdata1, 但是实际上为何只有 ib_logfile 被覆盖呢? 查看历史 操作命令, 找到了 初始化 实例的指令如下:
- history | grep MySQL
- 368 /apps/svr/mysql57/bin/mysqld --initialize --datadir=/apps/dbdat/mysql57_data3307 --user=apps --innodb-data-file-path=ibdata1:1000M:autoextend --innodb-data-home-dir=/apps/dbdat/mysql57_data3307 --innodb-log-file-size=1000M --innodb-log-files-in-group=4
3.5 为何使用命令指定参数执行新建实例, 会覆盖其他实例的的文件呢?
查看 history 操作时, 发现 之前执行了了 初始化实例的指令, 没有指定 配置文件, 而是指定了 datadir, 另起服务器, 执行该命令, 是正常, 截图如下, 那么这里又 引申了 第五个 问题: 为何使用 命令执行新建实例, 会覆盖其他实例的的文件呢?
灵机一动的我, 联想到了配置文件的默认读取顺序, MySQL 安装的时候, 读取配置文件的优先顺序: . 查看 /etc/my.cnf , 发现确实是 有文件存在, 并且这个文件是 3306 的配置文件内容.
File Name | Purpose |
/etc/my.cnf | m |
/etc/MySQL/my.cnf | Global options |
SYSCONFDIR/my.cnf | Global options |
$MYSQL_HOME/my.cnf | Server-specific options (server only) |
defaults-extra-file | |
~/.my.cnf | User-specific options |
~/.mylogin.cnf | User-specific login path options (clients only) |
这个时候, 可以初步确认, MySQL 3307 初始化的时候, 除了使用 指定的指令外, 其他需要的启动参数会从 默认的配置文件路径依次读取. 那么, MySQL 初始化需要读取哪些 必备参数呢 ?
MySQL 5.7 , 无默认配置文件, 仅指定参数 datadir basedir 初始化后生成 :MySQL\sys\performance_schema 3 个数据库目录及表格文件, 同时生成 ibdata1,ib_logfile[*] 等文件, 是否可以判断, 初始化过程中, 需要指定跟 ibdata1,ib_logfile[*] 相关参数, error log 位置, user 则正常呢?
/apps/svr/mysql57/bin/mysqld --initialize --datadir=/apps/dbdat/mysql57_data3307 --user=apps --innodb-data-file-path=ibdata1:1000M:autoextend --innodb-data-home-dir=/apps/dbdat/mysql57_data3307 --innodb-log-file-size=1000M --innodb-log-files-in-group=4 --basedir=/apps/svr/mysql57 --innodb_log_group_home_dir=/apps/dbdat/mysql57_data3307 --innodb-log-files-in-group=4 --log-error=/apps/logs/MySQL/error3307.log
你以为这样, 就可以安装了吗?
并不, 当存在默认路径的配置文件时, 除了指定参数外, 还是会去读 默认路径 上的配置文件 为主.
来源: https://www.cnblogs.com/xinysu/p/9655759.html