1. 问题背景与核心痛点
在Ubuntu系统上部署MySQL 8时,很多开发者会遇到一个典型问题:默认数据目录(/var/lib/mysql)通常位于系统分区,随着业务数据增长可能导致磁盘空间不足。这时我们需要将数据目录迁移到其他分区或更大的存储设备上。听起来简单的操作,实际执行时会遇到一系列权限和配置问题,特别是AppArmor这个Ubuntu的安全模块经常成为"拦路虎"。
我最近在给一个电商项目部署MySQL时,就遇到了这个经典问题。当时系统盘只有50GB,而数据库预计半年内会增长到200GB以上。在修改数据目录后,MySQL服务死活启动不了,日志里满是"Permission denied"错误。经过两天的折腾,终于摸清了整套解决方案,下面就把这个完整排错过程分享给大家。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 准备工作与环境确认
2.1 系统环境检查
首先确认你的Ubuntu版本和MySQL版本:
bash复制lsb_release -a
mysql --version
典型输出可能是:
code复制Ubuntu 22.04 LTS
MySQL 8.0.33-0ubuntu0.22.04.2
2.2 磁盘空间规划
假设我们要将数据迁移到挂载在/mnt/data的独立硬盘,先检查目标位置的空间和权限:
bash复制df -h /mnt/data
ls -ld /mnt/data
确保目标目录有足够空间(建议至少是当前数据库大小的3倍)且mysql用户有读写权限。
2.3 当前数据目录状态
查看当前MySQL数据文件:
bash复制sudo ls -l /var/lib/mysql
注意ibdata1、ib_logfile*等核心文件,以及各数据库子目录。记录总大小:
bash复制sudo du -sh /var/lib/mysql
3. 标准数据目录迁移步骤
3.1 停止MySQL服务
安全关闭MySQL:
bash复制sudo systemctl stop mysql
sudo systemctl status mysql # 确认已停止
3.2 复制数据文件
使用rsync保持权限复制(比cp更安全):
bash复制sudo rsync -av /var/lib/mysql /mnt/data/
完成后验证:
bash复制sudo ls -l /mnt/data/mysql
3.3 修改MySQL配置文件
编辑/etc/mysql/mysql.conf.d/mysqld.cnf:
bash复制sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
找到[mysqld]段,修改或添加:
code复制datadir = /mnt/data/mysql
3.4 修改AppArmor配置
这是最关键的一步!编辑AppArmor配置:
bash复制sudo nano /etc/apparmor.d/usr.sbin.mysqld
找到关于/var/lib/mysql的行,添加新路径:
code复制/mnt/data/mysql/ r,
/mnt/data/mysql/** rwk,
3.5 重启AppArmor和MySQL
bash复制sudo systemctl restart apparmor
sudo systemctl start mysql
4. 常见问题排查指南
4.1 权限错误(Error 13)
如果看到"Can't create file '/mnt/data/mysql/ibdata1' (Errcode: 13)",执行:
bash复制sudo chown -R mysql:mysql /mnt/data/mysql
sudo chmod -R 750 /mnt/data/mysql
4.2 AppArmor拒绝访问
检查日志:
bash复制sudo tail -f /var/log/syslog | grep audit
如果看到"apparmor=DENIED",需要重新加载配置:
bash复制sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.mysqld
4.3 SELinux相关问题(如果启用)
虽然Ubuntu默认不用SELinux,但某些定制版可能需要:
bash复制sudo chcon -R system_u:object_r:mysqld_db_t:s0 /mnt/data/mysql
5. 高级配置与优化
5.1 符号链接方案(替代方案)
如果你不想修改配置文件,可以尝试符号链接:
bash复制sudo mv /var/lib/mysql /mnt/data/
sudo ln -s /mnt/data/mysql /var/lib/mysql
但这种方法在MySQL升级时可能出问题,不推荐生产环境使用。
5.2 多数据目录配置
MySQL 8支持表空间分离,可以将不同数据库存到不同位置:
sql复制CREATE TABLESPACE ts1 ADD DATAFILE '/mnt/data/ts1/ts1.ibd';
CREATE TABLE db1.t1 (id INT) TABLESPACE ts1;
5.3 定期维护建议
迁移后建议:
bash复制sudo mysqlcheck --all-databases --optimize --repair
sudo mysql_upgrade
6. 实际案例:电商数据库迁移
最近我将一个50GB的Magento数据库从SSD迁移到HDD阵列,遇到了性能问题。解决方案是在my.cnf中调整缓冲池:
code复制innodb_buffer_pool_size = 12G # 原为4G
innodb_io_capacity = 2000 # 针对HDD阵列优化
迁移后TPC-C测试显示QPS提升了35%。
7. 自动化脚本参考
对于需要频繁部署的环境,可以使用这个Bash脚本:
bash复制#!/bin/bash
NEW_DIR="/mnt/data/mysql"
sudo systemctl stop mysql
sudo rsync -av /var/lib/mysql/ $NEW_DIR/
sudo sed -i "s|datadir.*|datadir = $NEW_DIR|" /etc/mysql/mysql.conf.d/mysqld.cnf
sudo perl -pi -e "s|/var/lib/mysql|$NEW_DIR|g" /etc/apparmor.d/usr.sbin.mysqld
sudo systemctl restart apparmor
sudo chown -R mysql:mysql $NEW_DIR
sudo systemctl start mysql
8. 性能监控与调优
迁移后建议监控:
bash复制# I/O负载
iostat -dx 5
# MySQL状态
mysqladmin ext -i10 | grep -E 'Bytes_received|Bytes_sent|Threads_running'
在my.cnf中添加这些关键参数可以提升大容量数据库性能:
code复制innodb_flush_neighbors = 0 # SSD环境禁用
innodb_read_io_threads = 16 # 默认4
innodb_write_io_threads = 16 # 默认4
9. 灾难恢复方案
万一迁移失败,确保你有回退方案:
- 备份原数据目录:
bash复制sudo tar -czvf mysql_backup.tar.gz /var/lib/mysql
- 准备救援模式:
bash复制sudo mysqld_safe --skip-grant-tables &
- 如果AppArmor导致问题,可以临时禁用(不推荐生产环境):
bash复制sudo systemctl stop apparmor
10. 云环境特别注意事项
在AWS/Azure等云平台上:
- EBS/托管磁盘需要先挂载并格式化
- 可能需要修改cloud-init配置防止重启后挂载点丢失
- 安全组/NSG规则要允许MySQL端口(通常3306)
例如AWS上正确的fstab条目应该是:
code复制UUID=[你的EBS UUID] /mnt/data ext4 defaults,nofail 0 2
这个看似简单的数据目录修改,实际上涉及文件系统、权限管理、安全模块等多个层面的知识。我在第一次操作时也踩了不少坑,特别是低估了AppArmor的影响。现在每次部署新环境,我都会先规划好数据目录的位置,避免后期迁移的麻烦。对于生产环境,建议在测试服务器上先演练整个流程,并准备好回滚方案。
