尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

MySQL 8.4卸载重装到数据盘:从备份、挂载到SELinux配置的完整实战

MySQL 8.4卸载重装到数据盘:从备份、挂载到SELinux配置的完整实战 这阵子帮几个朋友收拾生产服务器发现一个特别高频的需求业务跑着跑着磁盘不够了或者新买的NVMe固态想给数据库用结果一看MySQL装在了系统盘数据文件直接堆满了根分区。最头疼的是很多人图省事当年就是用包管理器默认装的MySQL数据目录一直没动过现在想迁走绕来绕去发现不如干脆卸了重装来得干净。正好前段时间在一台CentOS Stream 9上把MySQL 8.4.7卸载并重装到了新挂载的数据盘上整个过程踩了不少坑也总结出一套比较稳的流程。这篇文章就把完整的操作思路、命令、避坑经验全部分享出来从为什么选择“卸载重装”而不是“直接迁移”到具体怎么卸、怎么挂盘、怎么重装最后怎么验证一次性讲清楚。1. 为什么选择“卸载重装”而不是“直接迁移”1.1 数据目录迁移的隐藏成本很多人第一反应是既然只是想换盘直接把datadir改掉、把数据拷贝过去不就完了吗理论上是这样但实际操作中你会发现几个很现实的问题。第一MySQL 8.4.7是一个LTS版本它的目录结构和权限要求比老版本严格得多。如果你用RPM方式安装的那么/etc/my.cnf、/var/lib/mysql、/var/log/mysqld.log这些路径和SELinux上下文都是安装时自动配置好的。强行改datadir会牵扯到SELinux标签、AppArmor配置虽然CentOS默认只开SELinux、日志文件路径、socket文件路径等等一堆联动项。我见过不止一个人只改了datadir结果服务起不来报错信息五花八门。第二你没法保证数据文件在拷贝过程中完全一致。MySQL的InnoDB引擎在运行期间会有大量的脏页和redo log如果你不是用xtrabackup这类物理备份工具做一致性快照直接用cp或rsync拷贝运行中的数据库目录拷出来的数据大概率是不完整的。轻则启动时要走崩溃恢复流程重则直接报Table doesnt exist甚至数据文件损坏。第三卸载重装带来的“副作用”恰恰是它的好处——干净的元数据、干净的配置、干净的日志。你不需要担心历史配置里残留了哪个搞不明白的参数也不需要排查是不是某个旧版本的遗留文件在捣乱。特别是一些服务器从5.7升级到8.4的配置文件里可能还留着sql_mode、default_authentication_plugin这些已经被废弃或改名的参数与其花时间清理不如直接卸掉重来。1.2 二进制包与RPM包在选择上的权衡在CentOS Stream 9上装MySQL 8.4主流有两种方式用MySQL官方的Yum仓库装RPM包或者用官方提供的Generic二进制包也就是tar.gz解压版。两种我都试过这里给出我的选型建议。如果你对磁盘路径有硬性要求比如必须把程序文件和数据文件分开、数据文件必须放在某块独立盘上那么二进制包压缩包其实是更好的选择。因为你解压到哪里、数据目录在哪里完全是自己说了算不受RPM包默认路径的限制。但你用RPM包也有好处systemd服务脚本、配置文件、目录权限、日志轮转这些全部自动帮你配好省心。而且8.4.7在CentOS Stream 9上用RPM安装只要配置好官方Yum源就是一个dnf install的事。我这次选择的是“RPM安装 手动指定数据目录”的方式。解释一下先正常通过Yum源安装RPM包让系统把MySQL相关的依赖、服务脚本、配置文件框架都准备好然后修改my.cnf里的datadir、socket等路径指向新盘再手动初始化数据目录。这样既享受了RPM包对系统集成的便利又满足了“把数据装到指定盘”的核心诉求。如果你希望程序文件也全部放到业务盘那二进制包会更好后面我会简单说下两条路线各自怎么走。2. 卸载前的准备工作这一步非常重要千万别跳过。很多人上来就是一个dnf remove mysql-server装了半天的数据直接灰飞烟灭事后哭都来不及。2.1 确认当前安装方式与数据体量动手之前先搞清楚这台机器上的MySQL到底是怎么来的。# 查看系统版本 cat /etc/os-release # 查看当前MySQL相关RPM包 rpm -qa | grep -i mysql如果rpm -qa能列出mysql-server、mysql-community-server之类的包名说明走的是RPM安装如果什么都查不到但/usr/local/mysql或/opt/mysql存在且里面有个bin/mysqld那大概率是二进制包。然后再看数据目录到底占用了多少空间确认一下有哪些数据库实例在跑# 查看数据目录大小 du -sh /var/lib/mysql # 确认当前运行的MySQL实例 systemctl status mysqld这一步的一个隐藏目的是让你心里有个数如果数据量是几十个GB用mysqldump逻辑导出再导入时间还能接受如果是几个TB那就要考虑物理备份或者干脆在业务低峰期操作。2.2 数据备份与导出既然要卸载重装备份是必须做的。根据我的经验最稳妥的方式是同时做两层备份物理文件备份和逻辑导出备份。物理备份就是把整个数据目录/var/lib/mysql原封不动打包拷贝到一个安全的地方。这个备份的好处是万一逻辑导出有问题你还能用物理文件把原实例恢复回来。# 创建备份目录建议放在另一块磁盘上 mkdir -p /backup/mysql_physical_$(date %F) cp -rp /var/lib/mysql/* /backup/mysql_physical_$(date %F)/ # 如果数据量很大直接打包 tar -czf /backup/mysql_physical_$(date %F).tar.gz -C /var/lib/mysql .逻辑备份用mysqldump注意加几个关键参数保证一致性mysqldump -uroot -p --single-transaction --routines --triggers --events --all-databases /backup/all_databases_$(date %F).sql--single-transaction很重要对InnoDB表来说它通过一致读快照来保证导出过程中数据不变化不需要锁表。--routines、--triggers、--events分别导出存储过程、触发器和事件调度器缺了任何一个恢复的时候都会出问题。备份完了还要把mysql.user表单独导一下。因为8.4版本MySQL新增了mysql.user表的变化直接全量导出有时候会把rootlocalhost的认证插件导出成旧格式导致恢复后无法登录。我的习惯是额外多导一份用户和权限信息mysqldump -uroot -p --no-tablespaces --single-transaction mysql user /backup/mysql_user_$(date %F).sql到这里备份就算做完了。我在实际操作中还会顺手做一次校验把导出的SQL文件压缩后计算MD5防止后续传输过程损坏。2.3 业务停机窗口与数据一致性确认备份不是只跑一条命令那么简单你还需要确认备份的有效性。我见过有人导完SQL文件恢复的时候发现文件只有几KB一看是磁盘满了导出命令实际上失败了但顶部的mysqldump没有返回非零退出码因为管道行为还得看pipefail一不留神就忽略了。所以导完之后建议看一眼每个SQL文件的大小至少得跟du -sh /var/lib/mysql的数据量成比例。然后再做个快速校验# 查看导出的SQL文件头部确认完整性 head -n 50 /backup/all_databases_$(date %F).sql tail -n 5 /backup/all_databases_$(date %F).sql如果文件末尾有-- Dump completed字样且没有mysqldump: Error相关的输出那这个备份基本是可靠的。同时务必在业务低峰期进行这个操作。如果你负责的是对外服务的数据库最好提前发公告明确维护时间窗口。我自己的经验是选在凌晨2点到6点之间把业务停掉跑完备份和卸载重装流程天亮之前正好能验证完恢复情况。3. 卸载MySQL 8.4.7的完整流程3.1 停止服务与关闭开机自启备份做好之后开始正式卸载。第一步永远是停服务这是安全底线。systemctl stop mysqld systemctl disable mysqld为什么要disable因为如果你不取消开机自启卸载到一半系统重启了服务管理器会尝试拉起一个已经被移除二进制文件的服务虽然最终必然失败但会留下一堆可疑的日志干扰你后续排查。再确认一下服务真的停了systemctl status mysqld # 看到 inactive (dead) 才继续3.2 移除RPM包与清理残留文件CentOS Stream 9默认的包管理器是dnf它和yum命令是兼容的所以下面两条命令效果一样。先查看完整的MySQL相关包列表有些依赖包单独列出来不叫mysql开头但确实是MySQL的依赖比如mysql-common、mysql-errmsg等rpm -qa | grep -i mysql然后逐个卸载。最简单的方式是直接卸载最上层的mysql-server让包管理器自动处理下层依赖dnf remove mysql-community-server mysql-community-client mysql-community-common mysql-community-libs如果提示依赖冲突可以加上--remove-leaves如果有这个选项或者老老实实一个个来。我的经验是在我操作的8.4.7环境中RPM包列表主要就是这四个server、client、common、libs。卸载完成后清理残留的文件和目录。RPM卸载不会自动删掉你的数据目录和配置文件这是好事但如果你确定不需要保留旧配置了还是手动删掉干净# 删除数据目录注意这里面的数据已经备份过了 rm -rf /var/lib/mysql # 删除配置文件 rm -f /etc/my.cnf rm -rf /etc/my.cnf.d/这样就把MySQL从纯RPM角度卸载掉了。3.3 清理用户、权限与日志等隐藏残留卸载RPM包后MySQL系统用户通常是mysql并不会自动删除。如果你后续要通过RPM重装留着这个用户其实不影响因为RPM安装会自己创建或复用该用户。如果你想彻底清理userdel mysql groupdel mysql但我不建议你这么做。为什么因为重装后系统会重新创建相同UID/GID的用户如果你已经创建过同名的业务用户且UID正好跟它冲突那才叫麻烦。保留mysql系统用户重装时能减少权限不一致引发的初始化问题。所以这里我的建议是除非你知道自己在做什么否则保留该用户。日志文件也容易遗漏。有的人习惯把日志改到自定义路径比如/var/log/mysql/这些RPM卸载时是不知道的需要你自己清理rm -rf /var/log/mysql清理完之后再验证一下系统里已经完全没有MySQL相关的东西了rpm -qa | grep -i mysql # 应无任何输出 find / -name mysqld -type f 2/dev/null # 应只返回空结果或者只有系统缓存到这里卸载完成系统回到了一个干净状态。4. 指定盘挂载与目录规划4.1 新盘分区与格式化在把MySQL安装到指定盘之前你得保证这块盘已经被正确分区、格式化、挂载。我这次用的是一块NVMe盘路径是/dev/nvme0n1。先看一下磁盘状况lsblk fdisk -l如果是一个全新的裸盘需要先分区。用parted或者fdisk都行我习惯用parted因为它对大容量盘支持更好parted /dev/nvme0n1 --script -- mklabel gpt parted /dev/nvme0n1 --script -- mkpart primary 0% 100%然后格式化。这里有个选型问题文件系统用xfs还是ext4CentOS Stream 9默认和推荐的是xfs它对大文件、高并发场景表现更好而且支持在线扩容。MySQL数据文件动辄几GB到几十GB用xfs不会出问题。mkfs.xfs /dev/nvme0n1p1格式化之前务必三思确认盘符没错。这种命令没有后悔药我以前就见过同事把旧的数据库盘给格式化了那叫一个惨烈。给磁盘建挂载点并挂载# 创建挂载点 mkdir -p /data # 挂载 mount /dev/nvme0n1p1 /data然后写进/etc/fstab保证重启后自动挂载。这一步千万别忘了不然重启后MySQL找不到数据目录直接起不来echo /dev/nvme0n1p1 /data xfs defaults 0 0 /etc/fstab # 验证fstab是否配置正确 mount -av4.2 MySQL目录规划与设计原则数据盘挂载好了接下来规划里面的目录结构。我见过有人直接把datadir指到/data根目录这是比较懒的做法不推荐。更好的做法是建立有层次的目录mkdir -p /data/mysql/{data,tmp,binlog,logs} chown -R mysql:mysql /data/mysql分目录的好处一目了然data核心数据文件对应datadirtmp临时表目录对应tmpdir把它单独放一块地方避免临时文件撑爆根分区binlog二进制日志目录方便独立备份和清理logs错误日志单独一个目录方便查看有人可能觉得一个盘没必要分这么细但我的经验是一旦业务量上来日志清理、binlog保留策略这些都是单独操作的。目录分好了后期做logrotate、定期清理、甚至迁移都方便得多。另外还有一点容易被忽略指定盘是纯数据盘的话没必要在盘上再放一套MySQL程序文件。程序文件放在系统盘数据文件放在数据盘这个组合最灵活。如果你非要连程序也放数据盘那就要做好系统盘坏了依赖数据盘启动MySQL的心理准备虽然也能行但复杂度会高不少。4.3 挂载目录的SELinux上下文准备CentOS Stream 9默认开启SELinux这是很多MySQL重装到指定盘后启动失败的根本原因。普通目录不会自动带有mysqld_db_t之类的类型标签需要手动设置。先确认SELinux当前状态getenforce如果输出是Enforcing那就必须处理SELinux标签。在/etc/my.cnf中指定了自定义datadir后对应目录的标签要改成MySQL能访问的类型# 用chcon临时修改类型重启后可能丢失但先让它跑起来 chcon -R -t mysqld_db_t /data/mysql # 用semanage添加规则持久化推荐 dnf install -y policycoreutils-python-utils semanage fcontext -a -t mysqld_db_t /data/mysql(/.*)? restorecon -Rv /data/mysqlrestorecon的作用是把目录上下文恢复成策略定义的默认值处理完以后即使SELinux处于Enforcing状态MySQL也能正常读写/data/mysql目录。5. 在指定盘上重装MySQL 8.4.7并初始化5.1 配置Yum源与安装RPM包卸载完之后配置文件已经没了Yum源也需要重新确认下。MySQL官方Yum源的配置方式在CentOS Stream 9上可以直接使用RPM包安装源文件先安装MySQL官方仓库RPMdnf install -y https://dev.mysql.com/get/mysql84-community-release-el9-1.noarch.rpm安装完后检查仓库是否生效dnf repolist | grep mysql正常情况下能看到mysql-8.4-lts-community这个仓库。如果提示找不到或网络超时可以手动编辑/etc/yum.repos.d/mysql-community.repo确保[mysql-8.4-lts-community]段的enabled1。然后安装dnf install -y mysql-server安装完以后/etc/my.cnf会被重新创建系统的mysqld服务也回来了。但此时数据目录还是空的RPM包的%post脚本在安装时会自动执行一次初始化把datadir初始化到默认的/var/lib/mysql。你需要做的是立刻修改配置指向你的数据盘。如果你不修改就直接启动那它在系统盘上生成了数据后面再改datadir又得多折腾一轮。所以安装完之后先不要启动服务先改配置文件。5.2 修改my.cnf指定数据目录与服务参数编辑/etc/my.cnf在[mysqld]段中添加或修改以下参数[mysqld] datadir/data/mysql/data socket/data/mysql/data/mysql.sock pid-file/var/run/mysqld/mysqld.pid log-error/data/mysql/logs/mysqld.log tmpdir/data/mysql/tmp binlog_expire_logs_seconds604800这里逐个解释datadir是核心数据目录必须指向你挂载盘中的目录。socket也建议改到数据盘上避免根分区空间的潜在占用问题。当然你如果后续用命令行连接时要指定socket路径稍微麻烦一点但生产环境这样做更合理。pid-file保持默认在/var/run/mysqld即可这是一个临时运行时文件不属于数据。log-error指向/data/mysql/logs方便和别的日志分开。tmpdir指向/data/mysql/tmp。MySQL在排序、建临时表时会写临时文件如果根分区空间紧张临时文件可能直接导致磁盘满。把它挪到数据盘上一劳永逸。修改完配置文件后再检查一下目录权限确保MySQL用户对这些目录有完全控制权chown -R mysql:mysql /data/mysql chmod 750 /data/mysql/data这里chmod 750有讲究mysql用户需要读写执行mysql组内的其他用户如果有的话可以读写执行但其他用户一律无权访问。数据安全性上比755好不少。5.3 执行初始化并启动MySQL配置文件改好了目录权限对了开始初始化数据目录。8.4版本的初始化命令是mysqld --initialize它会自动创建系统表空间和系统数据库。mysqld --initialize --usermysql --datadir/data/mysql/data初始化过程中系统会自动生成一个临时root密码输出在错误日志中。用命令查看grep temporary password /data/mysql/logs/mysqld.log初始化成功的话你会看到类似这样的内容[Note] A temporary password is generated for rootlocalhost: aB1cD2eF3...拿到临时密码后面第一次登录需要用到。初始化之后启动服务systemctl start mysqld systemctl enable mysqld启动后立即查看状态和日志systemctl status mysqld tail -n 20 /data/mysql/logs/mysqld.log正常启动的标志是日志中出现ready for connections。如果此处卡住或者报错了多半是SELinux标签没设置对、目录权限不对、或者配置文件中某个目录不存在。第一部分我已经给了排查思路后面第6节还会专门讲几个高频坑。5.4 首次登录与密码修改用刚才拿到的临时密码登录mysql -uroot -p --socket/data/mysql/data/mysql.sock登录后第一件事就是修改密码并强制使用更安全的认证插件ALTER USER rootlocalhost IDENTIFIED BY YourStrongPassw0rd!;8.4版本默认用的是caching_sha2_password认证插件这是好事支持更好的加密协议。如果你之前用老版本习惯了mysql_native_password想改成老的认证方式可以执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY YourStrongPassw0rd!;但我不太推荐为了兼容旧客户端而降级认证方式。如果你的旧程序连不上8.4的默认认证方式优先升级客户端驱动而不是降低服务端安全配置。修改完密码后确认基本查询没问题SELECT VERSION(); SELECT datadir; SHOW VARIABLES LIKE socket;这三个值全对证明MySQL确实在指定盘上跑起来了。5.5 用二进制包方式重装到指定盘的补充方案如果你选择了二进制Generic包而不是RPM包流程稍微不同但核心思路一致下载MySQL 8.4.7的Generic tarball后解压到指定目录tar -xzf mysql-8.4.7-linux-glibc2.28-x86_64.tar.gz -C /opt ln -s /opt/mysql-8.4.7-linux-glibc2.28-x86_64 /opt/mysql然后同样修改my.cnf指定basedir、datadir、socket再用mysqld --initialize初始化。唯一多出来的步骤是你需要手动创建mysql系统用户并自己编写systemd服务文件如果不想每次手动启动。RPM包装完以后systemd服务文件是自动生成的。二进制包方式需要你在/etc/systemd/system/mysqld.service写一段Unit配置把ExecStart指向/opt/mysql/bin/mysqld。新手建议走RPM方式省心。老手如果对目录控制有强迫症二进制包方式更自由。6. 常见问题排查与避坑经验6.1 初始化时报错无法写入目录 / 权限被拒绝这个报错信息我见过太多次了。表现是初始化命令执行后提示[ERROR] Could not open /data/mysql/data/mysql.ibd for writing: Permission denied排查步骤按顺序来先确认目录归属ls -ld /data/mysql /data/mysql/data如果显示的是root root那肯定是权限错了。执行chown -R mysql:mysql /data/mysql然后看SELinux这在CentOS Stream 9上尤其重要ls -Z /data/mysql/data如果文件的SELinux类型不是mysqld_db_t就按第4.3节的方法处理。还有一个隐藏问题如果/data本身不是根分区而是单独的挂载点那么/data/mysql这个路径在SELinux看来可能有不同的默认标签用restorecon或者semanage加上规则基本都能解决。6.2 启动后日志报错socket文件无法创建改了socket路径以后最常见的启动失败原因就是socket目录权限不对。[ERROR] Cant create Unix socket lock file /data/mysql/data/mysql.sock.lock这个错误的原因通常是/data/mysql/data目录的属主不是mysql或者父目录缺少写权限。解决办法chown mysql:mysql /data/mysql/data chmod 755 /data/mysql还有个小坑如果你把socket直接放在/data/mysql根目录而/data/mysql的父目录/data是root所有MySQL启动进程是mysql用户它没权限在这个目录下创建文件。所以我的目录规划里给了data子目录socket放里面就避开了权限问题。6.3 数据导入后表数据不完整 / 导入失败重装之后把之前备份的SQL文件导入mysql -uroot -p --socket/data/mysql/data/mysql.sock /backup/all_databases_$(date %F).sql导入完成后检查一下核心业务表的数据量和原库是否一致。我遇到过一个问题mysqldump导出时没有加--single-transaction导致导出过程中有大量并发写入数据快照不一致。所以再次强调导出时一定加这个参数。如果导入时报错Unknown table xxx in information_schema之类的问题多半是8.4版本里information_schema的结构变了重装时默认系统库是全新的业务库数据导入后只要没报错就没事。这种报错可以先忽略重点是业务库是否还原完整。还有一个容易忽略的坑你原来的数据库里可能有自定义函数、存储过程、定时事件。这些在导入时需要额外的权限。建议使用root账号导入导入完成后再按最小权限原则去调整业务账号。6.4 从系统盘误删了数据目录后的“后悔药”如果你在卸载的时候没备份或者备份文件损坏了先别慌。Linux下删除文件只要分区没有被大量覆盖数据是有可能恢复的。但MySQL的数据目录是很多个文件组成的恢复起来非常麻烦成功率也低。我的建议是把数据恢复工作委托给专业工具或专业团队不要自己反复尝试。更重要的是把这个教训记下来备份是一切操作的前提不要省那几分钟。6.5 根分区磁盘满导致MySQL无法启动重装到指定盘以后很多时候根分区的空间并没有因此变宽裕。如果你的datadir改了但tmpdir没改那么在执行大排序操作时临时文件一样会写到系统盘。查看当前MySQL使用的临时目录SHOW VARIABLES LIKE tmpdir;确保tmpdir指向/data/mysql/tmp。除此之外binlog也容易占空间。MySQL 8.4默认的binlog保留策略是binlog_expire_logs_seconds如果你不设置默认可能保留很久积少成多也会把数据盘塞满。我建议对binlog单独设置保留时间日常巡检时可以用du -sh查看。7. 验证整条链路与日常巡检建议重装完成后不要急着说“搞定了”一定要从头到尾验证一遍整条链路。第1步验证MySQL服务状态systemctl status mysqld -l第2步验证数据落盘位置ls -lh /data/mysql/data/*.ibd du -sh /data/mysql/data如果你的数据确实在变大说明数据正在写入指定盘而不是还在老地方偷偷写。第3步重启一次服务确认所有配置在系统重启后依然生效reboot systemctl status mysqld重启后如果MySQL自动启动且数据正常证明/etc/fstab挂载配置和MySQL服务自启都正常这才是真正的“一键完成”。第4步把日常巡检的几个命令固化下来我一般会写个简单的脚本定时检查磁盘空间、MySQL运行状态、关键日志错误#!/bin/bash df -h | grep /data systemctl is-active mysqld grep -i error /data/mysql/logs/mysqld.log | tail -n 10脚本不用复杂能提前告警就够了。在排障这件事上提前发现永远比事后补救好。对于这次重装涉及的指定盘我还建议同步关注一下它的SMART状态和IO负载。数据盘如果出现坏道或性能瓶颈MySQL再快也没用。iostat -x 1可以看到每块盘的利用率如果%util长期接近100%那说明盘本身已经是瓶颈了。最后分享一点个人体会把MySQL重装到指定盘技术上并不复杂但真正考验人的是对细节的把控。无论是备份校验、SELinux标签、目录权限、还是binlog保留策略任何一环漏掉都可能让你在半夜被报警电话吵醒。我自己的习惯是每次做这类操作都把步骤写成文档存起来下次再遇到同样需求直接照着跑同时把踩过的坑更新在文档里。运维这件事经验是最值钱的资产。希望这篇文章能帮你少踩几个坑顺利把MySQL搬到新盘上。如果你在操作中遇到了我这里没有提到的问题欢迎留言交流。
返回列表