
我去年接了一个内部小项目的维护业务数据三个月涨了快 40G系统盘眼看着就飘红了。当时最直接的办法就是把 MariaDB 的数据目录从 /var/lib/mysql 迁到挂载在 /data 下的独立数据盘顺便把安装配置也重新梳理了一遍。这类“Linux 安装 MariaDB 修改存储路径”的需求在运维日常里太典型了不管是自学 Linux 数据库部署、给公司服务器做数据盘扩容还是准备面试实操都能用得上。这篇文章我就把完整流程拆开讲清楚从环境准备、源的选择、安装初始化再到数据目录迁移、权限处理和安全策略调整每一步都给出能直接复现的操作也会把背后的原理和踩过的坑一并交代。1. 迁移存储路径之前先搞清楚场景和风险1.1 什么情况下需要改存储路径很多朋友第一次接触“修改 MariaDB 存储路径”这个需求第一反应是默认装在 /var/lib/mysql 不就行了确实小项目、测试环境随便跑都没问题。但真实生产环境里改存储路径几乎是业务增长后绕不开的一步。最常见的情况就是系统盘空间不够。云服务器默认系统盘一般就 40G 到 80GMariaDB 的数据文件、binlog 日志、redo log 全堆在根分区业务一旦跑起来膨胀速度远超预期。其次是数据盘隔离需求很多业务要求数据库必须落在独立的数据盘或者专门的 SSD 上不能跟系统盘抢 IO。再有就是迁移场景比如要把数据库从旧机器搬到新机器或者从机械盘换到 NVMe 盘这时候也需要重新规划数据目录。不过有一点要提醒改存储路径不是简单的 mv 一下就行它牵扯到 datadir 参数、socket 路径、pid 文件、日志位置、SELinux/AppArmor 安全策略还有整个数据文件的一致性。如果只改一半最常见的结果就是服务起不来或者起来后客户端连不上。1.2 MariaDB 在 Linux 上的默认目录布局动手之前先把 MariaDB 在 Linux 上的默认布局搞清楚后面迁移的时候才知道哪些东西要跟着动哪些可以保持不变。不同发行版会略有差异但整体上差不多目录/文件默认位置说明数据目录/var/lib/mysql所有库表文件、ibdata1、undo 日志、redo log主配置文件/etc/my.cnf或 /etc/my.cnf.d/ 下的多个配置片段错误日志/var/log/mariadb/mariadb.log启动和运行时的错误记录socket 文件Debian系/run/mysqld/mysqld.sock本地客户端连接用socket 文件RHEL系/var/lib/mysql/mysql.sock有些版本默认在这里pid 文件/var/run/mariadb/mariadb.pid记录服务进程号数据目录里存放的内容很多人会有误解以为只有库名对应的文件夹。实际上 /var/lib/mysql 下面除了 mysql、performance_schema、sys 这些系统库目录还有 ibdata1、ib_logfile0/1 这类 InnoDB 核心文件以及临时的 ibtmp1。修改存储路径时整个目录都要搬走不能只挪某个库。1.3 迁移前必须完成的三件事我见过太多人上来就改配置结果数据搬了一半发现回不去。这里先列三个必做项顺序不能乱第一确认全量备份存在。不管用什么方式mysqldump 导出来的 SQL 也好物理目录打包也好必须有一份能用的备份。迁移过程中如果数据文件损坏至少能还原。我的习惯是迁移前做一次逻辑备份放系统盘以外的地方双保险。第二确认服务可以正常停止。迁移期间数据库要处于完全关闭状态不能有连接还在写。建议选业务低峰期操作提前跟业务方打好招呼避免出现停止服务后还有应用在重连的尴尬。第三确认目标磁盘已经挂载好而且在 /etc/fstab 中有持久化配置。否则服务器重启后新目录变成空目录数据库会因为找不到 datadir 而启动失败。这也是新手最容易忽略的df -h 看盘明明是满的实际是没挂载。2. Linux 环境准备与 MariaDB 安装2.1 环境确认与安装源选型安装第一步不是敲 yum install 或者 apt install而是确认系统环境和架构。先跑一下cat /etc/os-release uname -m常见的发行版分两大系RHEL 系CentOS、Rocky Linux、AlmaLinux、Oracle Linux和 Debian 系Ubuntu、Debian。两个系的包管理器和初始化方式不同安装源也不同。安装源这块我强烈建议用官方软件仓库而不是直接用发行版自带的源。原因很简单系统自带源里的 MariaDB 版本往往偏旧比如 Ubuntu 22.04 自带的还是 10.6 系列而官方源能直接装到 10.11 LTS 甚至 11.x 的稳定版性能和 bug 修复都更好。另外官方源对 ARM 架构也很友好这在国产化服务器场景里很实用。2.2 RHEL 系安装步骤以 Rocky Linux 9 为例先配置官方 MariaDB 源。可以访问官网的仓库生成工具也可以手动写一个源文件。手动配置的方式是新建 /etc/yum.repos.d/mariadb.repo[mariadb] name MariaDB baseurl https://mirror.mariadb.org/yum/11.4/rhel/9/x86_64/ gpgkey https://mirror.mariadb.org/yum/RPM-GPG-KEY-MariaDB gpgcheck 1注意 baseurl 里的版本号11.4 是目前企业级用得比较多的稳定分支。装完源之后sudo dnf install -y mariadb-server mariadb-client sudo systemctl enable --now mariadb这里要解释一个细节为什么用 dnf 而不用 yum。在 Rocky 9、AlmaLinux 9 这类新版本里yum 只是个兼容别名底层还是 dnf直接敲 dnf 体验更好报错信息也更清晰。2.3 Debian 系安装步骤Debian 系稍微麻烦一点因为官方源需要手动导入签名密钥。以 Ubuntu 22.04 为例sudo apt update sudo apt install -y wget sudo apt install -y mariadb-server mariadb-client如果你想装官方源里的新版本可以这样操作sudo apt install -y software-properties-common sudo mkdir -p /etc/apt/keyrings sudo wget -qO /etc/apt/keyrings/mariadb-keyring.pgp https://mirror.mariadb.org/yum/RPM-GPG-KEY-MariaDB然后在 /etc/apt/sources.list.d/ 下添加源文件写入deb [signed-by/etc/apt/keyrings/mariadb-keyring.pgp] https://mirror.mariadb.org/repo/11.4/ubuntu jammy main更新源后安装sudo apt update sudo apt install -y mariadb-server mariadb-client2.4 安装后的初始化与连接验证安装完成后第一件要做的事就是初始化安全配置。执行sudo mariadb-secure-installation这个命令会引导你设置 root 密码、删除匿名用户、禁止 root 远程登录、删除 test 库。中间的选项全部按推荐值选就行生产环境建议全选 yes。然后验证服务状态和默认数据目录systemctl status mariadb mysql -u root -p -e SHOW VARIABLES LIKE datadir;正常情况下你会看到后面小节里要改的 datadir 还是默认的 /var/lib/mysql。这时候整个数据库已经能跑了可以建个测试库插几条数据为了后面迁移验证做准备mysql -u root -p -e CREATE DATABASE test_migrate; USE test_migrate; CREATE TABLE t1 (id INT PRIMARY KEY, name VARCHAR(50)); INSERT INTO t1 VALUES (1, hello);3. 修改 MariaDB 存储路径的完整实操3.1 目录规划与磁盘挂载检查这一步是迁移的核心。首先确定你要把数据放到哪个目录我建议用 /data/mysql而不是直接使用 /data。原因很简单/data 是数据盘的挂载点里面可能还有其他业务目录提前建一个独立子目录权限隔离和后续运维都更清晰。先看一下磁盘挂载情况lsblk df -h cat /etc/fstab如果目标数据盘还没格式化挂载可以用下面的流程sudo mkfs.xfs /dev/sdb # 新盘注意确认盘符别把自己的系统盘格式化了 sudo mkdir -p /data echo /dev/sdb /data xfs defaults 0 0 | sudo tee -a /etc/fstab sudo mount -a df -h这里有个容易被忽略的坑如果数据盘之前已经挂过但 fstab 没写重启后会掉盘。掉盘之后 MariaDB 启动时会发现 datadir 指向的 /data/mysql 不存在直接报错起不来。所以每次改完 fstab 后建议用 mount -a 先验证一遍再重启验证一遍。3.2 数据目录迁移的详细步骤现在进入核心环节。整个过程按以下步骤操作每一步都有它的理由不要跳步。第一步备份。既然已经跑了一个测试库先导出一份逻辑备份mysqldump -u root -p --all-databases --single-transaction --routines --events --triggers /tmp/mariadb_backup_$(date %Y%m%d).sql加 --single-transaction 是为了保证备份数据的一致性不会因为并发写入导致逻辑不一致。加了 --routines、--events、--triggers 是为了把存储过程、定时事件、触发器都包含进去。很多人只导出表数据导入后才发现存储过程没了就是这个原因。第二步停止服务sudo systemctl stop mariadb停止后确认没有残留进程ps aux | grep mariadbd | grep -v grep这一步很关键。systemctl stop 之后如果还有残留的 mariadbd 进程数据文件可能还在被写入这时候复制目录拿到的文件就是不一致的。第三步创建目标目录并调整属主sudo mkdir -p /data/mysql sudo chown mysql:mysql /data/mysql sudo chmod 751 /data/mysql属主一定要是 mysql:mysql否则启动时进程没有权限写数据目录。权限设 751 是为了让 mysql 用户能读写目录其他用户只能进入这个权限组合在生产环境比较常见。第四步复制数据文件。这里我推荐用 rsync而不是 mv。为什么因为 rsync 能在保留权限、属主、软链接的同时进行增量复制万一中途失败可以重跑而 mv 是把整个目录直接搬走原目录就没有了回滚会变得很麻烦。命令如下sudo rsync -avp /var/lib/mysql/ /data/mysql/-a 是归档模式保留权限、属主、时间戳等属性-v 是显示进度-p 是保留文件权限。复制完成后可以对比一下两个目录的内容sudo diff -r /var/lib/mysql /data/mysql没有输出说明两边文件一致。复制完成后原目录先别急着删把它改名留作回滚sudo mv /var/lib/mysql /var/lib/mysql.bak3.3 修改配置文件datadir、socket 与日志联动数据文件搬完接下来就是改配置。MariaDB 的配置加载顺序比较特殊/etc/my.cnf 里会有 include 指令把 /etc/my.cnf.d/ 目录下的文件全部加载进来。我的习惯是在 /etc/my.cnf.d/ 下新建一个专门的文件比如 mariadb-datadir.cnf这样改动集中、方便排查。[mysqld] datadir/data/mysql socket/var/lib/mysql/mysql.sock pid-file/var/run/mariadb/mariadb.pid log-error/var/log/mariadb/mariadb.log这里解释几个关键点datadir 不用说这是数据目录的核心参数。socket 这里我特意保留成原来的 /var/lib/mysql/mysql.sock但如果原目录不存在了有些客户端连接会出问题。稳妥的做法是创建一个软链接或者直接用默认的系统 socket 路径比如 Debian 系用 /run/mysqld/mysqld.sockRHEL 系用 /var/lib/mysql/mysql.sock。如果你把 socket 改了客户端连接时也要指定新的 socketmysql -u root -p -S /tmp/mysql.sock其实更简单的通用做法是把 socket 放到 /tmp 下几乎所有 PHP、Python 配置都能识别但要注意安全管理/tmp 权限本身要锁好。这个看各人偏好。pid-file 和 log-error 通常不需要改因为这两个文件是运行时动态创建的不存在“数据文件”的概念。但如果你遇到权限问题可以把 pid-file 保持默认log-error 保持默认这个不影响存储路径的修改。改完配置之后还有一件事不能漏如果原目录被改名成了 /var/lib/mysql.bak而 socket 还是指向 /var/lib/mysql/mysql.sock那就必须保证这个目录存在。我建议sudo mkdir -p /var/lib/mysql sudo ln -s /data/mysql/mysql.sock /var/lib/mysql/mysql.sock这样原来走 socket 连接的程序不用改配置还是按旧路径去找 socket 文件实际上会被 LN 指向新目录下的 socket。3.4 SELinux 与 AppArmor 安全策略调整这一步是绝大多数教程不会重点讲但实际最容易翻车的环节。很多人在修改目录后启动失败日志里一堆 Permission denied查了半天发现是 SELinux 或 AppArmor 拦着。RHEL 系默认 SELinux 是 enforcing 模式。新数据目录 /data/mysql 没有被标上 mysqld_db_t 类型MariaDB 进程就无法访问。解决办法有两个一是直接把 SELinux 关掉这是我最不推荐的做法等于把服务器的安全防线拆了。二是给新目录打上正确的 SELinux 上下文sudo dnf install -y policycoreutils-python-utils sudo semanage fcontext -a -t mysqld_db_t /data/mysql(/.*)? sudo restorecon -Rv /data/mysqlsemanage 命令的作用是告诉 SELinux/data/mysql 这个目录未来要作为 MariaDB 数据目录使用预先登记它的文件类型。restorecon 则是把当前目录里已有的文件全部刷新成这个类型。做完之后可以用 ls -Z /data/mysql 确认ls -Z /data/mysqlDebian/Ubuntu 系是 AppArmor情况类似。配置文件通常在 /etc/apparmor.d/usr.sbin.mariadbd默认只允许访问 /var/lib/mysql 下的文件。修改后要在文件末尾加入/data/mysql/ rwk,保存后重载 AppArmorsudo systemctl reload apparmor不放心可以查看当前状态sudo aa-status | grep mariadbd这一步不发愁的话启动后大概率会报Failed to open log (file ./ib_logfile0, errno 13)这就是权限被拦截的典型表现。3.5 启动验证与数据完整性检查配置和权限都处理完之后可以启动服务了sudo systemctl start mariadb systemctl status mariadb如果启动失败第一时间看错误日志sudo tail -50 /var/log/mariadb/mariadb.log启动成功之后逐个验证mysql -u root -p -e SELECT datadir; mysql -u root -p -e SHOW DATABASES; mysql -u root -p -e USE test_migrate; SELECT * FROM t1;这里有一个小的经验迁移完成后不要急着删 /var/lib/mysql.bak。我通常的做法是让它保留 24 小时经过一个完整的业务周期、确认新目录读写都正常之后再清掉这份备份。虽然占点磁盘空间但能让迁移风险降到最低。4. 常见问题与排查技巧实录4.1 启动失败案例速查表迁移过程中最容易遇到的几个问题我整理成一张表方便直接对照排查。报错现象可能原因排查思路常用解决命令Cant create/write to file /data/mysql/xxx目录属主或权限不对检查目录属主和权限chown -R mysql:mysql /data/mysqlFailed to open log (file ./ib_logfile0, errno 13)SELinux 或 AppArmor 拦截查看 SELinux/AppArmor 配置semanage fcontext / restoreconCant connect to local MySQL server through socketsocket 路径不匹配检查 client 配置和 socket 文件是否真实存在mysql -S /tmp/mysql.sockDirectory xxx is not owned by mysql属主不正确检查目录 ownerchown -R mysql:mysql[ERROR] InnoDB: Operating system error number 13权限或安全策略查看文件权限、上下文ls -Z 确认 SELinux 类型[ERROR] MariaDB: File /var/lib/mysql/mysql.sock not found原目录被改名但 socket 还在旧路径创建符号链接或修改配置ln -s我这里再强调一个实际场景如果你恰好用的是一台开启了 SELinux 的国产化服务器比如麒麟、统信 UOS 这些基于 RHEL 体系的系统semanage 命令有可能不在 PATH 里需要先安装 policycoreutils-python-utils。有些精简版系统连 policycoreutils 都没装就得先dnf install policycoreutils-python-utils。4.2 客户端无法通过 socket 连接时的处理迁移后很容易出现一个奇怪的现象服务进程明明起来了systemctl status mariadb也显示 active但本地mysql -u root -p就是报“Cant connect to local MySQL server through socket”。这个问题的根因在于配置优先级。MariaDB 读取 [client] 和 [mysqld] 两段配置socket 参数在这两段中都要存在。如果你只改了 [mysqld] 里的 socket但 [client] 段还指向旧路径客户端就会用旧路径去找 socket 文件。最简单的排查方式mysql -u root -p -S /实际socket路径能连上就说明问题出在客户端连接的 socket 路径配置。这时候在 my.cnf.d 的配置文件里补一段[client] socket/var/lib/mysql/mysql.sock或者统一把所有 socket 都指向一个新路径关键是 [mysqld] 和 [client] 必须一致。另外还有一个容易被忽略的点mysql.sock 文件是服务启动时创建的如果你把原 /var/lib/mysql 目录整个改名了而 socket 路径还指向里面服务会在启动时尝试创建 socket 文件但因为目录不存在就会一直报错。所以我在前面建议做软链接本质原因就在这里。4.3 迁移后发现数据不完整的排查这种情况最吓人但通常不是数据真的丢了而是迁移操作方式不对导致漏掉了文件。有一次我帮朋友排查迁移后一个库少了三张表看了半天才发现他操作命令是cp /var/lib/mysql/* /data/mysql/但当时服务没停还有应用在写入部分新写入的文件没复制到新目录。这种问题的防范办法有两点复制前必须确认服务完全停止这是硬指标复制后用 diff 或 rsync 校验两边目录是否完全一致。如果不放心可以直接用 rsync 再跑一次校验模式sudo rsync -avp --checksum /var/lib/mysql/ /data/mysql/不输出差异内容说明两边一致。如果发现有遗漏就用这条命令自动补上然后重启服务。另外要提醒表空间类型的问题如果某些表使用了 InnoDB 独立表空间file-per-table默认开启每个表对应一个 .ibd 文件如果早期表是共享表空间数据全在 ibdata1 里。复制目录时这些文件都会被正常复制不会出现逻辑遗漏。但如果你的表既有 InnoDB 又有 MyISAMMyISAM 的三件套.frm、.MYD、.MYI也必须完整任何一个缺失都会在查询时报错。4.4 保留回滚方案避免迁移翻车无论准备得多充分总有意外可能发生。所以我每次迁移都有一个固定动作保留原目录标记为不可被自动清理的文件。如果迁移后出现完全不可恢复的问题比如误删了某个重要目录、误改了配置导致反复启动失败回滚流程只有四步sudo systemctl stop mariadb sudo mv /data/mysql /data/mysql.failed sudo mv /var/lib/mysql.bak /var/lib/mysql sudo systemctl start mariadb改回配置里的 datadir 为 /var/lib/mysql再启动服务。这样整个环境就恢复到迁移前状态。这套回滚方案我建议你在真正迁移之前先用测试库完整演练一遍。演练几次之后你会对整个过程每个环节可能出的问题更有感觉真正上生产的时候心里就有底。5. 迁移后的日常维护与经验补充5.1 磁盘空间监控与备份策略迁移最大的收益就是给数据盘扩容腾出了空间但空间是省出来了监控不能停。我一般会在服务器上部署简单的磁盘告警脚本或者直接用系统自带的 crontab 加一条0 * * * * df -h | awk NR1 || /\/data/ {print} /var/log/disk_usage.log每天看一眼 /data 的占用趋势心里就有数。备份策略上我建议至少保留两套一套是 mysqldump 的逻辑备份适合数据量不大、需要整库恢复的场景另一套是物理备份工具MariaDB 自带的 mariadb-backup 可以做在线物理备份适合数据量大、恢复时间要求高的场景。很多人在迁移完成后就不管备份了这是要不得的。迁移节点本身是数据结构最容易出问题的时机如果新目录里数据文件存在 bug几天后才发现再想恢复就难了。5.2 几个提升效率的 MariaDB 运维命令这里分享几个我平时经常用的巡检命令适合在迁移完成后的几天里反复检查# 查看数据目录实际占用 sudo du -sh /data/mysql # 查看当前连接数连接数异常时最先要看这个 mysql -u root -p -e SHOW PROCESSLIST; # 查看 InnoDB 状态关注 buffer pool 命中率等关键信息 mysql -u root -p -e SHOW ENGINE INNODB STATUS\G # 巡检所有库表完整性 mysqlcheck -u root -p --all-databases这里说明一下mysqlcheck 对 MyISAM 表可以做 check 和 repair对 InnoDB 表主要是检查逻辑一致性。迁移完成后的第一次巡检建议把所有库都 check 一遍有问题尽早暴露。还有一个实用的监控指标datadir 所在分区的剩余空间。MariaDB 运行时redo log、undo log、临时文件都会写在这个分区如果空间满了数据库会直接进入只读模式甚至崩溃。日常巡检时df -h 里 /data 的使用率要保持在 80% 以下这个目标要提前规划。5.3 关于迁移路径的几点个人体会最后说几个我自己折腾了几次才总结出来的体会。第一次做数据目录迁移的时候我犯过一个说大不大、说小不小的错只改了 datadir没管 socket结果启动倒是成功了但所有原本走 socket 连接的应用全部连不上。当时还怀疑是端口问题查了半天才发现路径不匹配。所以后来我总结出一个原则凡是涉及目录位置的改动一定要把 client 端和 server 端的路径全部过一遍不能只盯着 mysqld。还有一个体会是关于参数调整的。迁移之后很多人会顺手调 innodb_buffer_pool_size因为换了新盘想提升性能。这个思路没错但要注意顺序先把迁移做稳定再调参数。两个风险叠加在一起出了问题你根本不知道是路径配置引出来的还是参数设得不对引出来的。我的做法是迁移后保持原参数跑一天确认一切正常后再根据监控数据逐步调整。另外如果你是在虚拟化环境里做这个操作记得迁移前给整机做一个快照。快照回滚比任何备份都快是最后一根救命稻草。虽然云平台的快照会占空间、收费但比起数据库数据的安全性这点成本非常值得。这套流程走完你得到的不仅是一个改过路径的 MariaDB还有一套对目录结构、权限、安全策略的完整认知。以后再遇到类似的迁移需求比如把 MySQL 数据目录迁走、把 binlog 单独放一个盘思路都是通用的。