ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

CentOS Stream 9 卸载重装 MySQL 8.4.7 并迁移数据到指定盘

CentOS Stream 9 卸载重装 MySQL 8.4.7 并迁移数据到指定盘 Linux CentOS Stream 9 一键卸载 MySQL 8.4.7 并重装到指定盘干了这么多年 Linux 运维我几乎每个月都能碰到这种场景服务器刚到手时图省事MySQL 直接用默认方式装上去数据一路往/var/lib/mysql里堆。等系统盘告警、df -h一敲发现/已经 100% 的时候才想起来当初怎么没把数据库放在数据盘上。我这次处理的就是一台 CentOS Stream 9 机器上面跑着 MySQL 8.4.7系统盘就剩 800M而另外挂载了一块 1TB 的数据盘一直闲置。与其用软链接把/var/lib/mysql挪过去不如直接卸载干净、重装到指定盘一步到位。这篇文章就把这套“卸载 重装 数据迁移到指定盘”的流程完整记录一下并且整理成脚本下次再遇到同类服务器可以直接抄作业。不管你是在学 Linux 安装 MySQL 的新手还是天天跟数据库目录、磁盘布局打交道的运维老手这篇都值得看完。新手的收获是知道 MySQL 到底怎么卸载才干净、怎么指定数据目录才不出错老手的收获是我把踩过的坑尤其是 SELinux 和 socket 路径这两个隐形杀手全部摊开来讲清楚避免你重走弯路。1. 为什么要把 MySQL 重装到指定盘方案选型与设计思路1.1 默认安装路径的问题根源MySQL 在 Linux 上用 RPM 包装完之后数据目录被固定在/var/lib/mysql日志写到/var/log/mysqlsocket 文件放在/tmp/mysql.sock或者/var/lib/mysql/mysql.sock。这本身没有毛病问题的关键在于/var属于根分区。很多云主机根分区给的容量就 40G 到 50G系统本身吃掉一部分nginx 日志、业务备份再占掉一部分留给数据库的余量非常有限。数据库一涨起来根分区直接被打爆。我见过最典型的故障现场就是/var/lib/mysql下面的 binlog 和临时文件把根分区写满MySQL 直接拒绝写入业务侧大量报错。此时数据库本身没有坏纯粹是磁盘空间耗尽。运维要做的无非两条路——扩容根分区或者把数据目录挪到独立的数据盘上。扩容根分区要动云盘、扩分区、扩文件系统中间停机窗口很大而把 MySQL 的数据目录搬到一块干净的数据盘不动系统盘、不扩容、不影响已经挂载的其他服务明显更划算。1.2 “重装到指定盘”而不是“软链接挪目录”把 MySQL 的数据目录搬家业界常见的做法有三种。第一种是直接软链接停库把/var/lib/mysql整体拷贝到新盘然后mv /var/lib/mysql /var/lib/mysql.bak再ln -s /data/mysql /var/lib/mysql。这招在部分系统上确实能跑但隐患不小MySQL 升级时 RPM 包里的脚本有时会删掉软链接重建目录SELinux 对软链接上下文的识别也常有异常另外 systemd 的ProtectSystem等安全选项在某些版本下会阻止对软链接目录的正常访问。第二种是 Mount Bind 挂载把数据盘挂载到/var/lib/mysql目录看起来路径不变底层其实是新盘。这个方法不用改配置文件适用性很广但如果数据库需要漂移、或者存在多个实例要分目录部署时bind 挂载管理起来就比较繁琐。第三种就是我采用的“真重装”卸载旧 MySQL重新安装时将数据目录通过datadir配置项显式指向数据盘比如/data/mysql。这是最干净、最符合官方推荐的方式不存在任何路径层面的兼容性问题。代价是要停机、要重装、要重新初始化数据目录。但配合自动化脚本整个流程可以在十分钟内完成风险完全可控。1.3 一键脚本的三个设计原则把整个流程脚本化最忌讳的就是写一个“看起来能用其实一跑就崩”的脚本。我在设计这个一键脚本时给自己定了三条死规矩。第一备份永远先于破坏。任何情况下脚本执行到卸载步骤之前必须先做数据备份没有备份直接拒绝继续执行。第二幂等性。脚本不管是第一次跑、中途失败重新跑、还是对一台已经重装过 MySQL 的机器跑都不应该产生副作用。比如重复添加官方仓库要能跳过目标数据目录已存在时不能无脑覆盖。第三分段可见。一键脚本不代表黑盒每个大步骤都要打印清晰的状态提示并且要求每一步的执行结果都被检查失败即中断不能带着错误往下走。基于这三条原则整体流程划分为环境检查、数据备份、卸载清理、仓库配置、安装、目录初始化、配置写入、启动验证八个阶段。下面按实际执行顺序逐步展开。2. 动手之前的准备备份、磁盘评估与依赖确认2.1 盘点现有数据体量卸载之前必须先搞清楚一个问题这台机器上的 MySQL 到底有多少数据删了之后还能不能恢复。我处理的那台机器上跑着好几套应用库最大的库有 47GB里面还有一堆统计表。直接在系统里敲du -sh /var/lib/mysql mysql -u root -p -e SELECT table_schema, ROUND(SUM(data_lengthindex_length)/1024/1024, 2) AS total_mb FROM information_schema.tables GROUP BY table_schema ORDER BY total_mb DESC;第一句看总目录占用第二句从库里按 schema 统计各库容量。两个都能跑的话数据体量基本就有数了。我遇到的现象是第二句因为库太大、查询慢差点以为自己连不上数据库实际是 information_schema 统计时锁表现象等了十几秒才出结果不用慌。2.2 mysqldump 逻辑备份与物理备份双保险我强烈建议逻辑备份和物理备份各做一份不要嫌麻烦。逻辑备份用mysqldump导出 SQL 文件方便重装之后直接导入物理备份直接把整个/var/lib/mysql拷贝走防止 mysqldump 在导出过程中遇到个别损坏的表或权限问题导致漏数据。逻辑备份命令如下mkdir -p /backup/mysql_backup mysqldump -u root -p --all-databases --single-transaction --routines --triggers --events --set-gtid-purgedOFF /backup/mysql_backup/all_databases_$(date %F).sql这里说几个容易被忽略的关键参数。--single-transaction配合 InnoDB 可以做一致性快照备份不锁表在线执行时不影响业务写入--routines和--triggers必须加否则存储过程、触发器全部丢失--events备份事件调度器--set-gtid-purgedOFF是 MySQL 8.0/8.4 环境下从非复制实例导出时必须要加的不然导入时会把 GTID 历史带上可能引发主从复制冲突。物理备份更简单也更快systemctl stop mysqld cp -rp /var/lib/mysql /backup/mysql_backup/var_lib_mysql_physical_$(date %F) systemctl start mysqld停库再拷贝能保证数据文件处于一致状态。如果业务允许长时间只读先停库备份完再启动是最稳的。如果不允许停机那就用rsync做两次同步第一次在线同步第二次短暂锁定写入再同步增量。2.3 确认目标数据盘的挂载状态备份做完接下来要看数据盘。目标盘的挂载点、文件系统类型、剩余空间都需要确认lsblk df -hT fdisk -l我这边的情况是数据盘/dev/sdb已经格式化成了 XFS挂载在/data可用空间 980GB。如果你的新盘还没有挂载那就需要先分区、格式化、写进/etc/fstab做持久挂载再继续往下走。这里提醒一句目标挂载目录最好是单独的新目录比如/data不要直接挂在/home或/root下面MySQL 会拒绝把数据目录放到 home 目录路径下报错信息是Datadir is inside home directory这个坑在手动初始化时非常常见。2.4 清理 CentOS Stream 9 的包管理依赖CentOS Stream 9 默认用 dnf 作为包管理器官方仓库里就叫mysql-server。我机器上的 MySQL 8.4.7 是通过 MySQL 官方 Yum 仓库装的所以卸载前确认一下来源rpm -qa | grep -i mysql在有官方仓库的机器上结果通常包含mysql-server、mysql84-libs、mysql84-common、mysql84-icu-data-files等。卸载时直接dnf remove mysql-server会把服务端去掉但留下的mysql84-libs等库文件未必会一起移除需要手动再清理。如果你是通过dnf install mysql-server从 AppStream 装的那包名可能略有差异但卸载思路一样。还有一点非常重要卸载记录一定要确认不能漏掉配置目录。RPM 卸载时默认不会删除/etc/my.cnf和/var/lib/mysql数据这是设计上防止误删的安全机制。但我们的诉求是“重装到指定盘”必须主动清理这些残留。3. 卸载脚本的执行逻辑与清理细节3.1 卸载阶段脚本实现我把卸载阶段的核心代码提出来这段可以直接单独跑也可以合并进一键脚本#!/bin/bash # mysql_uninstall.sh # 用途在 CentOS Stream 9 上彻底卸载 MySQL 8.4.7 set -euo pipefail echo [1/4] 停止 MySQL 服务 systemctl stop mysqld 2/dev/null || true systemctl disable mysqld 2/dev/null || true pkill -9 mysqld 2/dev/null || true echo [2/4] 确认备份完成 if [ ! -f /backup/mysql_backup/all_databases_*.sql ] [ ! -d /backup/mysql_backup/var_lib_mysql_physical_* ]; then echo 错误未检测到任何备份文件拒绝卸载 exit 1 fi echo [3/4] 卸载 RPM 包 dnf remove -y mysql-server mysql84-server mysql84 2/dev/null || true dnf remove -y $(rpm -qa | grep -i mysql) 2/dev/null || true echo [4/4] 清理残留文件 rm -rf /var/lib/mysql.old_$(date %s) 2/dev/null || true mv /var/lib/mysql /var/lib/mysql.old_$(date %s) rm -f /var/log/mysqld.log /var/log/mysql.log 2/dev/null || true rm -rf /etc/my.cnf /etc/my.cnf.d 2/dev/null || true echo 卸载完成。残留文件已重命名为 /var/lib/mysql.old_*如需找回数据可以从此目录恢复。这段脚本里有几个小细节值得展开讲讲。停服务时我加了一个pkill -9 mysqld这是防止 mysqld 进程异常驻留。正常systemctl stop可以优雅停机但一旦遇到卡死的连接或者磁盘 IO 阻塞stop 会一直卡住这时候强杀是无奈但有效的办法。考虑到脚本的自动化属性这一行保留但日常手动操作时还是建议先systemctl stop多等几秒不要一上来就 pkill。备份检查这步我用的是通配符判断文件是否存在。你可能觉得set -euo pipefail下如果没有任何备份文件会直接退出不需要额外判断。但实际上 bash 的set -e对if判断内部命令是豁免的所以需要显式写这个检查。备份检查这段绝非形式主义我吃了太多教训卸载脚本里如果没有这层防线一台忘记备份的机器跑了卸载命令后果就是彻底凉凉。dnf remove -y $(rpm -qa | grep -i mysql)这行的威力很大会把所有含 mysql 字样的包全部移除。如果不加过滤条件有可能把mysql-connector-odbc等客户端相关包也一并删掉所以在实际使用时建议把这一行保留但提前用rpm -qa | grep -i mysql确认一下列表内容。3.2 为什么用“重命名”而不是“直接删除”数据目录脚本里把/var/lib/mysql重命名成带时间戳的旧目录而不是直接rm -rf。这个设计是因为数据目录里可能有你还没意识到的价值。比如 binlog 中可能有某些时间点的增量数据或者某个库的某些表是 MyISAM 引擎mysqldump 不一定能完整导出。保留旧目录等于多一个后悔药而且重命名操作比删除快得多、安全得多。等重装完成、数据验证通过之后再手动清理这个旧目录也不迟。清理/etc/my.cnf和/etc/my.cnf.d也是必须的因为重装之后如果旧配置里还有指向旧 socket 或旧 datadir 的路径新实例很容易起不来。特别是 CentOS Stream 9 上我遇到过/etc/my.cnf.d/mysql-server.cnf这类分段配置不删干净的话改了主配置却总被下面的分段配置覆盖排查半天才知道原因。3.3 卸载结果的验证方法脚本跑完不要直接进入安装阶段。先验证卸载是否真的干净rpm -qa | grep -i mysql # 应无任何输出 which mysql # 应提示命令不存在 ls /var/lib/mysql # 目录已不存在或只剩重命名后的目录这一步如果发现rpm -qa还有残留需要重新执行dnf remove。如果which mysql还能找到说明系统的 PATH 里还有残留的二进制可能是手动编译安装留下的这类分布在路径层面不容易清干净但不影响重装安装官方 RPM 时会把新版二进制覆盖到/usr/bin/mysql。4. 重装 MySQL 8.4.7 并指向数据盘关键配置与初始化4.1 安装 MySQL 官方仓库CentOS Stream 9 的 AppStream 仓库自带的 MySQL 版本通常是 8.0 系列而标题里指定要 MySQL 8.4.7这个是 8.4 LTS 系列必须用 MySQL 官方 Yum 仓库来装。rpm -ivh https://dev.mysql.com/get/mysql84-community-release-el9-1.noarch.rpm执行完可以用下面的命令确认仓库是否生效dnf repolist | grep mysql正常能看到mysql84-community和mysql84-community-source两个仓库。接下来安装服务端dnf install -y mysql-server等待安装完成。安装过程会把mysqld服务放到/usr/sbin/mysqld同时生成默认的/etc/my.cnf。这里有个细节MySQL 8.4 默认的/etc/my.cnf是空配置绝大多数配置项都依赖默认值所以后续我们要往里面写入自定义的 datadir 和 socket 路径。4.2 创建目标数据目录并处理权限数据盘挂载在/data那就先建目录、改权限mkdir -p /data/mysql chown -R mysql:mysql /data/mysql chmod 750 /data/mysqlchown改成mysql:mysql这一点不用多说MySQL 的 systemd 服务默认以 mysql 用户运行。但chmod 750可能会被一些人忽略。如果目录权限是 755其他用户也能进入目录读元数据文件这不符合最小权限原则如果设成 700mysql 用户能访问但 MySQL 的目录内还会创建临时文件750 是折中且稳妥的方案。4.3 修改 my.cnf 核心配置在/etc/my.cnf里写入[mysqld] ###### 基础配置 ###### datadir/data/mysql socket/data/mysql/mysql.sock pid-file/data/mysql/mysqld.pid log-error/var/log/mysqld.log ###### 字符集与时区 ###### character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci default-time-zone8:00 ###### 连接优化可选 ###### max_connections1000 skip-name-resolve1 innodb_buffer_pool_size2G [client] socket/data/mysql/mysql.sock这里最关键的三个变量是datadir、socket、pid-file。只要 datadir 变了socket 和 pid-file 建议也一起变否则会出现一个很隐蔽的故障mysqld 把 socket 文件放在了/data/mysql/mysql.sock但客户端默认去/var/lib/mysql/mysql.sock找结果报ERROR 2002 (HY000): Cant connect to local MySQL server through socket /var/lib/mysql/mysql.sock。网络热词里正好有这条报错见过的人绝对不少。所以[client]段里的 socket 也要同步指向新位置这样本地连接才能正常走通。skip-name-resolve1是我习惯加上的配置它让 MySQL 不再对客户端 IP 做反向 DNS 解析能减少连接延迟和 DNS 故障导致的连接问题。副作用是user表中的 host 字段必须用 IP 或用localhost不能用域名大多数场景都没问题。default-time-zone8:00这块要留意一下如果你用的是 UTC 时区服务器不显式指定可能会导致应用侧时间差 8 小时。判断系统时区用timedatectl和 SQL 里的SELECT NOW();对照一下即可。4.4 SELinux 策略最容易踩的大坑CentOS Stream 9 默认开启 SELinux而且处于 enforcing 模式。MySQL RPM 包的 SELinux 策略默认放行了/var/lib/mysql目录但一旦我们把 datadir 指到/data/mysqlmysqld 在启动时访问这个新目录就会触发 SELinux denial。日志里通常出现类似Jan 10 12:00:01 host mysqld[1234]: Cant open the mysql.plugin table.或者Jan 10 12:00:01 host kernel: audit: type1400 audit(...): avc: denied { write } for pid1234 commmysqld namemysql devsdb1 scontextsystem_u:system_r:mysqld_t:s0 tcontextsystem_u:object_r:unlabeled_t:s0 tclassdir我自己一开始没注意启动失败跑去看/var/log/mysqld.log里面啥都没有但是journalctl -u mysqld能看到 SELinux 的审计日志。这个问题有两种解决办法。第一种最正规给新目录配置 mysqld 类型的 SELinux 文件上下文然后 restorecon。semanage fcontext -a -t mysqld_db_t /data/mysql(/.*)? restorecon -Rv /data/mysql如果系统没有semanage命令先装工具包dnf install -y policycoreutils-python-utils第二种是临时测试时用chcon -R -t mysqld_db_t /data/mysqlchcon直接修改目录的 SELinux 标签但不会持久化文件系统重新标记后可能会被还原。日常运维建议老老实实用semanage fcontext加restorecon一次配置永久生效。如果你觉得 SELinux 太麻烦想直接关掉那就是另一条路。在/etc/selinux/config里把SELINUXenforcing改成permissive重启或执行setenforce 0。但我不建议你为 MySQL 单独关闭 SELinux因为生产环境开 SELinux 是基本的安全底线正确配置策略并没有想象中复杂花几分钟改上下文就能解决没必要降低整个系统的安全等级。4.5 初始化数据目录MySQL 8.4 安装完成之后不会像旧版本那样自动帮你初始化数据目录需要手动执行mysqld --initialize。有两种初始化模式。第一种自动生成临时随机密码mysqld --initialize --usermysql初始化完成后临时密码打印在错误日志里grep temporary password /var/log/mysqld.log拿到密码后要尽快登录并修改。第二种生成空密码的 root 账号mysqld --initialize-insecure --usermysql这适合自动化脚本场景不需要解析日志就能直接登录然后立即用 SQL 设置新密码。我在一键脚本里用的是--initialize-insecure因为自动化处理随机密码特别痛苦。这里必须强调初始化命令必须在/etc/my.cnf配置完成之后执行或者配合--datadir/data/mysql参数显式指定。如果初始化时 my.cnf 还没改mysqld 会在默认的/var/lib/mysql初始化你的 datadir 配置就白写了。我遇到过头疼的情况配置写好了但忘了清空/data/mysql下的残留文件执行初始化时报错[ERROR] InnoDB: The innodb_system data file ibdata1 must be writable原因就是旧文件权限不对或残留冲突。重新清空目录后跑就正常了。5. 启动服务、验证数据目录与恢复备份数据5.1 启动服务并设置开机自启配置和初始化都完成之后执行systemctl daemon-reload systemctl enable mysqld --now systemctl status mysqldsystemctl status输出里有Active: active (running)就说明启动成功。此时马上验证 datadir 是否生效mysql -uroot -p -e SHOW VARIABLES LIKE datadir; mysql -uroot -p -e SHOW VARIABLES LIKE socket;输出应该指向/data/mysql/和/data/mysql/mysql.sock。如果你用--initialize-insecure初始化此时 mysql 的 root 密码是空的立刻修改mysql -uroot -p --connect-expired-password -e ALTER USER rootlocalhost IDENTIFIED BY YourNewStrongPass123!;MySQL 8.4 默认的密码策略要求长度、大小写、数字、特殊字符太简单的密码会直接被拒绝。5.2 从备份导入数据重装后的 MySQL 是一张白纸业务库全部要重新导入。用逻辑备份恢复mysql -uroot -p /backup/mysql_backup/all_databases_20250110.sql如果备份文件比较大导入时可以把进度打到日志里mysql -uroot -p --force /backup/mysql_backup/all_databases_20250110.sql 2 /backup/mysql_backup/import_error.log导入完成之后务必做一次对比校验mysql -uroot -p -e SELECT table_schema, COUNT(*) AS table_count FROM information_schema.tables GROUP BY table_schema;再抽查几张业务大表的行数是否与原备份体现的数量一致。老话讲“没有验证的恢复等于没恢复”这一步不能省。5.3 一键整合脚本总览我把整个流程整合成了一个完整脚本放在/root/mysql_relocate.sh。结构和前面的分步讲解完全对应但有几点整合时需要注意检测是否重复执行、备份检查逻辑复用、日志输出到固定文件。核心框架如下#!/bin/bash set -euo pipefail DATA_DISK_MOUNT/data DATA_DIR/data/mysql BACKUP_DIR/backup/mysql_backup MYSQL_ROOT_PASSYourNewStrongPass123! log() { echo [$(date %Y-%m-%d %H:%M:%S)] $*; } fail() { echo [$(date %Y-%m-%d %H:%M:%S)] ERROR: $*; exit 1; } # 1. 检查是否 root 执行 [ $(id -u) -eq 0 ] || fail 请使用 root 执行 # 2. 检查目标盘的挂载情况 [ -d $DATA_DISK_MOUNT ] || fail 数据盘挂载目录不存在 $DATA_DISK_MOUNT # 3. 备份 log 开始备份所有数据库... systemctl stop mysqld 2/dev/null || true mkdir -p $BACKUP_DIR cp -rp /var/lib/mysql $BACKUP_DIR/var_lib_mysql_physical_$(date %F) || true systemctl start mysqld 2/dev/null || true mysqldump -u root -p$MYSQL_ROOT_PASS --all-databases --single-transaction --routines --triggers --events --set-gtid-purgedOFF $BACKUP_DIR/all_databases_$(date %F).sql || fail 备份失败 # 4. 卸载 log 卸载旧版 MySQL... systemctl stop mysqld 2/dev/null || true systemctl disable mysqld 2/dev/null || true dnf remove -y mysql-server mysql84-server mysql84 2/dev/null || true dnf remove -y $(rpm -qa | grep -i mysql) 2/dev/null || true [ -d /var/lib/mysql ] mv /var/lib/mysql /var/lib/mysql.old_$(date %s) # 5. 安装 log 安装 MySQL 8.4.7... rpm -ivh https://dev.mysql.com/get/mysql84-community-release-el9-1.noarch.rpm 2/dev/null || true dnf install -y mysql-server # 6. 写入配置 log 写入 my.cnf 配置... cat /etc/my.cnf EOF [mysqld] datadir$DATA_DIR socket$DATA_DIR/mysql.sock pid-file$DATA_DIR/mysqld.pid log-error/var/log/mysqld.log character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci default-time-zone8:00 max_connections1000 skip-name-resolve1 innodb_buffer_pool_size2G [client] socket$DATA_DIR/mysql.sock EOF # 7. 创建数据目录 SELinux 上下文 log 准备数据目录... mkdir -p $DATA_DIR chown -R mysql:mysql $DATA_DIR chmod 750 $DATA_DIR semanage fcontext -a -t mysqld_db_t $DATA_DIR(/.*)? 2/dev/null || true restorecon -Rv $DATA_DIR # 8. 初始化并启动 log 初始化数据目录... rm -rf $DATA_DIR/* mysqld --initialize-insecure --usermysql systemctl enable mysqld --now # 9. 修改 root 密码 log 设置 root 密码... mysql -uroot --connect-expired-password -e ALTER USER rootlocalhost IDENTIFIED BY $MYSQL_ROOT_PASS; # 10. 导入备份 log 导入备份数据... mysql -uroot -p$MYSQL_ROOT_PASS --force $BACKUP_DIR/all_databases_*.sql log 全部完成。建议执行 mysql -uroot -p 登录验证。这段脚本里有两个地方是刻意设计的。一个是第 3 步先做物理备份再做逻辑备份顺序不能反。如果机器上 mysqld 因为磁盘满起不来逻辑备份导出很可能失败但物理备份只要磁盘能读就能成功复制。另一个是第 8 步rm -rf $DATA_DIR/*这一步保证mysqld --initialize-insecure不会因旧文件冲突失败。配合前面“备份无论如何都要先完成”的防线这里的删除是安全的。6. 常见问题与排查技巧实录6.1 错误速查表以下这些错误在我处理过程中基本都会遇到按频率从高到低整理成一张表方便你对照排查。错误现象根本原因解决方法Cant connect to local MySQL server through socket /var/lib/mysql/mysql.socksocket 路径不匹配服务端与客户端配置不一致确认/etc/my.cnf的[mysqld]和[client]妥协同一个 socket 路径启动失败日志出现avc: denied { write } ... mysqld_t ... unlabeled_tSELinux 未放行新 datadirsemanage fcontext -a -t mysqld_db_t /data/mysql(/.*)?; restorecon -Rv /data/mysqlmysqld: Cant create/write to file /data/mysql/... (Errcode: 13 - Permission denied)目录权限不对或 mysql 用户无写权限chown -R mysql:mysql /data/mysql并确认父目录/data可被 mysql 用户穿越[ERROR] InnoDB: The innodb_system data file ibdata1 must be writable/data/mysql有残留旧文件或权限不正确清空数据目录内容后重新--initialize-insecureERROR 1045 (28000): Access denied for user rootlocalhost密码错误或者用了临时密码没修改初始化日志找临时密码或--initialize-insecure后用空密码登录再改密远程连接 MySQL 报Authentication plugin caching_sha2_password cannot be loaded客户端版本过旧不支持 MySQL 8.4 默认认证插件升级客户端驱动到新版本或在服务端为用户设置mysql_native_password6.2 四个值得写下来的排查心得第一个心得任何奇怪的启动失败先去journalctl -u mysqld看日志不要只看/var/log/mysqld.log。mysqld 的 error log 在配置阶段可能还没生效而 systemd journal 记录的是启动瞬间的完整输出包括 SELinux 审计信息。我以前经常一头扎进/var/log/mysqld.log里面只有半句话后来才发现关键信息全在 journal 里。第二个心得skip-name-resolve1加上之后如果应用原来用主机名连 MySQL会直接连不上因为 MySQL 不再做反向解析。报错是Host 192.168.1.10 is not allowed to connect to this MySQL server。解决方式是授权时直接用 IP 地址写 host。如果业务里有大量域名连接不建议开这个参数。第三个心得binlog 占空间的问题很容易被忽视。旧实例可能在/var/lib/mysql里堆积了很多 binlog 文件每个 1GB几十个下来就是几十 GB。重装之后如果不打算做基于 binlog 的主从复制可以在 my.cnf 里加expire_logs_days7MySQL 8.4 用binlog_expire_logs_seconds避免新实例没跑几天又把数据盘占满。第四个心得如果你要直接拿旧数据目录恢复就是物理备份那种方式千万注意 MySQL 8.4 的auto.cnf文件。这个文件记录了 server UUID直接复制旧数据目录到新机器后如果新机器原来的auto.cnf和旧的不一样需要删除/data/mysql/auto.cnf再启动否则主从场景下 UUID 冲突会导致复制建立失败。单机运行可能不报错但为了规范恢复物理备份时最好删掉自动生成的auto.cnf。6.3 数据验证时的关键一步安装、导入完成后还有一步容易被忽略验证旧目录中的库是否都已经出现在新实例中。我的做法是写了一个快速比对脚本mysql -uroot -p -N -e SHOW DATABASES; | grep -v -E ^(information_schema|performance_schema|mysql|sys)$ | sort /tmp/db_new.list然后再从备份目录的物理备份中直接读目录名称find /backup/mysql_backup/var_lib_mysql_physical_* -maxdepth 1 -type d | awk -F/ {print $NF} | grep -v -E ^\.|sys|undo|binlog|tmp | sort /tmp/db_old.list diff /tmp/db_new.list /tmp/db_old.list如果 diff 有输出说明有库没有恢复进来需要针对性处理。这个方法不复杂但能救命。有一次我导入时因为 SQL 文件里存在已删除表的误操作某个库竟然没被导进来如果没有这步比对业务上线半天后才会暴露问题。7. 最后再说一点实际体会这套流程我在生产环境上完整跑过不止一次每次都能顺利收尾但每次也都会因为环境差异多一些新的发现。比如有的机器挂载点是/mnt/data有的数据盘格式是 ext4有的系统里还残留着低版本的 MySQL 客户端这些都会让脚本的适配层不断变厚。我个人最大的体会是卸载和重装本身不难难的是做到“不丢数据、不破坏环境、出了问题能回滚”。所以就算你不需要一键脚本也请务必保留旧的/var/lib/mysql重命名目录至少保留一个月再清理。磁盘便宜数据无价在这个事情上多留一手永远不亏。另外想提醒的是脚本里的 root 密码、缓冲池大小、时区这些参数换成你自己的环境时一定不要照抄。密码策略、内存大小、业务时区都是高度个性化的直接套用默认值最容易在后续维护中踩坑。把这套脚本当成一个骨架根据实际场景调整血肉才是正确的用法。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表