ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Linux下安装MariaDB与修改存储路径的实践指南

Linux下安装MariaDB与修改存储路径的实践指南 先说一个我自己的经历。有一年公司一台生产服务器莫名其妙报警我登上去一看根分区 usage 97%再仔细一查罪魁祸首就是 MariaDB。当初图省事安装时全部用的默认路径数据文件、binlog、慢查询日志全都堆在 /var/lib/mysql 里而系统盘总共只有 60G跑了小半年直接撑爆。那天我花了整整一个下午才在不停机的情况下把数据目录挪到了另一块 2T 的数据盘上过程相当刺激。所以这篇东西我想把Linux 安装 MariaDB和修改 MariaDB 存储路径这两件事放到一起讲清楚。前者很多人已经会了但后者才是真正容易出问题的地方。适合这几类人看刚买服务器准备装数据库的、已经装好但发现数据放在系统盘想搬迁的、以及每次迁移数据目录都会遇到各种奇怪报错的。我会把每一步的为什么也讲明白这样你就算换一个发行版、换一套路径也能自己判断问题出在哪。1. 装前必看MariaDB 默认路径为什么总在系统盘上搞事情1.1 系统盘写满的三个常见场景我在帮别人处理数据库故障时发现机器出问题往往不是数据库进程挂了而是磁盘满了导致数据库无法写入。最常见的场景就三种。第一种云服务器默认系统盘只有 40G 或 60G而数据盘是单独买的。很多人安装数据库的时候根本没想过把数据目录指向数据盘结果所有数据都落在系统盘上。数据量一涨系统盘很快告急。第二种测试环境里随便装了一台跑着跑着 binlog、慢查询日志、临时文件把磁盘吃满了。这种测试环境通常没有单独的日志盘日志和数据混在一个目录里很难单独清理。第三种根分区和 /var 分区没有单独划分整个系统只有一个 / 分区。这种分区方案下数据库文件、系统日志、软件包缓存全部挤在一起任何一个组件异常增长都会拖垮整台机器。我自己的那次事故就是第一种和第三种叠加。后来我把数据目录迁走之后同时把 binlog 的保留时间也改短了系统盘占用直接从 97% 降到了 30% 左右。所以建议各位在安装之前先把路径规划好不要等出事了再补救。1.2 一个被忽略的事实存储路径不只是 datadir很多人一说修改 MariaDB 存储路径下意识就觉得只要把 datadir 改掉就行。其实 MariaDB 涉及磁盘存储的路径有好几类只不过绝大多数情况下最占空间的是 datadir。路径项默认位置常见发行版作用是否建议迁移datadir/var/lib/mysql存放库表数据文件最核心的目录必须重点规划binlogdatadir 下的 binlog.00000x二进制日志用于主从复制和时间点恢复生产环境建议迁移错误日志 log_error/var/log/mysql/ 或 datadir记录启动、报错、警告信息建议指定到固定位置慢查询日志datadir 下如需开启记录慢 SQL方便排查性能问题建议与 datadir 分离socket 文件/run/mysqld/mysqld.sock 或 /tmp/mysql.sock本地客户端连接用迁移 datadir 时注意保持pid 文件/run/mysqld/mysqld.pid记录进程 ID供 systemd 等管理随配置调整即可tmpdir/tmp排序、临时表、DDL 等产生的临时文件大内存机器可指向 tmpfs强调一下binlog 和临时文件膨胀同样会写满磁盘。binlog 如果不设过期时间会一直保留直到磁盘爆炸。我见过不少案例数据本身不大但是 binlog 积累了几十 G把系统盘塞满了。所以修改存储路径的时候最好把这些日志类路径一起考虑进去。1.3 五分钟环境检查清单无论你是全新安装还是准备迁移花五分钟做一次环境检查后面能省很多事。以下命令基本在常见的 Linux 发行版上都能用。# 查看系统发行版信息 cat /etc/os-release # 查看磁盘分区和挂载情况 df -h # 查看块设备确认是否有未挂载的数据盘 lsblk -f # 查看 SELinux 状态 getenforce # 查看防火墙状态Ubuntu/Debian ufw status # 或 CentOS/RHEL firewall-cmd --state检查完你心里应该有数了系统是什么版本、有没有单独的数据盘、数据盘挂载到哪个目录、SELinux 是不是 enforcing 状态。尤其是 SELinux如果你要迁移数据目录却忽略它后面启动服务器多半会报权限错误。这个我后面专门讲因为新手常在这一步卡住。2. 安装 MariaDBUbuntu 和 CentOS 系分别怎么装踩过什么坑2.1 Ubuntu/Debian 系一条 apt 命令之后root 认证方式变了在 Ubuntu 上安装 MariaDB 非常简单sudo apt update sudo apt install mariadb-server -y sudo systemctl status mariadb安装完服务通常会自动启动。但这里有一个很多人第一次接触时懵掉的坑用mysql -u root -p输密码怎么输都是错误的但用sudo mysql却能直接进去。原因在于 MariaDB 在 Debian/Ubuntu 上默认启用了unix_socket认证插件。它的逻辑是只要当前 Linux 系统用户是 root 或者具有 sudo 权限就能通过系统身份直接认证进入数据库不需要密码。这种设计在一定程度上提高了本地管理的安全性但也让很多从 CentOS 过来的人很不适应。如果你确实需要给 root 设置密码并且允许用密码登录可以这样执行ALTER USER rootlocalhost IDENTIFIED VIA mysql_native_password USING PASSWORD(你的新密码);或者保留 socket 认证和密码认证两种方式共存ALTER USER rootlocalhost IDENTIFIED VIA unix_socket OR mysql_native_password USING PASSWORD(你的新密码);Ubuntu 上 MariaDB 的配置文件分散在 /etc/mysql 下核心片段一般在 /etc/mysql/mariadb.conf.d/50-server.cnf。我后面讲修改 datadir 时主要改的就是这个文件。还有一点Debian 系默认有 AppArmor 在管着 mysqld改路径时别忘了这层限制具体操作在第三章。2.2 CentOS/RHEL 系仓库选择、配置目录与 systemd 管理CentOS 系传统的安装命令是sudo yum install mariadb-server -y sudo systemctl enable --now mariadb不过 CentOS 7 自带的 MariaDB 版本比较老如果你需要新一点的功能建议使用 MariaDB 官方仓库。配置官方仓库的步骤在 MariaDB 官网有对应工具这里不展开只提醒一句yum 源配置完成之后用yum list mariadb-server先确认一下你将要安装的版本再执行安装。CentOS/RHEL 系配置文件的路径和 Ubuntu 不一样主配置在 /etc/my.cnf而 /etc/my.cnf.d/ 目录下会有很多片段MariaDB 服务端配置在 /etc/my.cnf.d/mariadb-server.cnf。修改这个文件时记得要先看目录下有没有其他片段可能影响变量。启动和管理方面现代版本基本都用 systemdsudo systemctl start mariadb sudo systemctl enable mariadb systemctl status mariadb和 Ubuntu 系不同CentOS 系默认的 SELinux 状态通常为 enforcing所以改路径的时候要先处理 SELinux 上下文。如果只是临时测试可以setenforce 0关掉但生产环境我不建议这么做正确做法是给新目录打上 mysqld_db_t 标签稍后详述。2.3 安装完成后的安全初始化与读写验证不管哪个发行版装完之后推荐跑一遍安全初始化脚本sudo mysql_secure_installation这个脚本会一步步问你是否设置 root 密码、是否删除匿名用户、是否禁止 root 远程登录、是否删除 test 库、是否刷新权限表。生产环境建议都选是。如果是在 Ubuntu 上root 认证方式是 unix_socket脚本可能会提示无法设置密码那也没关系保持 socket 认证反而更安全。初始化完成之后做一次读写验证确认数据库真的能正常用mysql -uroot -p -e CREATE DATABASE IF NOT EXISTS test_db; USE test_db; CREATE TABLE IF NOT EXISTS test_table (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50)); INSERT INTO test_table (name) VALUES (hello); SELECT * FROM test_table; DROP DATABASE test_db; 这段 SQL 会创建库、建表、插入数据、查询数据然后删掉测试库。如果能顺利执行完说明安装基本没问题。到这里数据库已经能跑了下一步才是最关键的——修改存储路径。3. 搬迁数据目录实操从停机到修改配置一步步说清楚3.1 停机、创建新目录、设置权限先强调一句改 datadir 这种操作必须在数据库停干净的状态下做。不要想着在线热迁移除非你用专业的备份恢复工具否则直接在运行状态下拷贝数据文件很容易导致文件不一致尤其是 InnoDB 的 redo log 和表空间文件。sudo systemctl stop mariadb sudo systemctl status mariadb # 确认服务确实已停止看到 inactive (dead) 后再继续接下来创建新目录。假设你的数据盘挂在 /data 下那我建议目录结构清晰一点比如sudo mkdir -p /data/mysql sudo chown -R mysql:mysql /data/mysql sudo chmod 750 /data/mysql这里解释一下权限。MariaDB 服务会以 mysql 用户运行所以从 /data 到 /data/mysql 每一级目录都要允许 mysql 用户进入。如果 /data 本身权限是 700 且属于某个普通用户那 mysql 是走不进去的启动时会直接报 Permission denied。我之前排查过一个案例就是创建 /data/mysql 后忘了检查 /data 的权限折腾了好久。生产环境如果你有强迫症可以用namei -l /data/mysql查看每一级目录的权限一目了然。3.2 rsync 拷贝为什么我不用 mv 或 cp目录权限设置好之后把旧数据整体拷贝过去。我最常用的是 rsyncsudo rsync -av --progress /var/lib/mysql/ /data/mysql/注意源目录末尾的斜杠它表示拷贝目录内的所有内容而不是把 /var/lib/mysql 这个目录本身再套一层。目标目录的权限和属主如果不是 mysql:mysql拷贝之后再统一修正一次sudo chown -R mysql:mysql /data/mysql为什么不用 mv因为 mv 在同一文件系统下只是改个名字瞬间完成但跨文件系统时会先拷贝再删除。更关键的是mv 在拷贝完成后如果出问题旧数据可能已经被删掉了恢复起来很麻烦。rsync 则不会动源目录你可以反复执行等确认一切正常再清理。为什么不用普通 cpcp 不保留权限、属主、时间戳等元信息之后你很可能要手动逐个修正。而 rsync -a 是归档模式可以完整保留这些信息。拷贝过程中如果中断rsync 还能断点续传cp 就只能从头再来。考虑到数据量大时这个区别几小时和几分钟的差别我强烈建议 rsync。拷贝完成后做一轮快速比对du -sh /var/lib/mysql /data/mysql ls /var/lib/mysql | wc -l ls /data/mysql | wc -l如果文件数量一致再继续下一步。3.3 修改配置文件datadir 之外还有什么需要一起改配置文件的位置Ubuntu 在 /etc/mysql/mariadb.conf.d/50-server.cnfCentOS/RHEL 在 /etc/my.cnf.d/mariadb-server.cnf。打开文件找到[mysqld]段把 datadir 改掉。我一般也会顺手把日志、socket、pid 相关路径一起调整避免后续麻烦。下面是一个参考配置路径换成你自己的实际路径[mysqld] datadir/data/mysql socket/run/mysqld/mysqld.sock pid-file/run/mysqld/mysqld.pid # 错误日志和慢查询日志建议单独指定 log_error/data/mysql/logs/mysql-error.log slow_query_log1 slow_query_log_file/data/mysql/logs/mysql-slow.log long_query_time2 # binlog 建议放在数据盘避免溢出系统盘 log_bin/data/mysql/logs/mysql-bin binlog_expire_logs_seconds604800 max_binlog_size512M如果 log_error、slow_query_log、binlog 这些文件路径里的目录不存在MariaDB 启动时通常会自动创建文件但不会自动创建多层目录。所以保险起见先手动建目录并设置权限sudo mkdir -p /data/mysql/logs sudo chown -R mysql:mysql /data/mysql/logs还有一种做法是保持 socket 和 pid 路径不动因为它们默认就在 /run 或 /tmp 下和 datadir 没太大关系只改 datadir。这样做可以省掉很多客户端 socket 连接的问题。如果把 socket 改到新目录请确保该目录的权限 mysql 用户能写否则后面本地连接会报 Cant connect through socket。3.4 AppArmor 和 SELinux大多数人启动失败都卡在这一步这里是最容易出问题的地方我单独拿出来讲。如果是 Ubuntu/Debian系统自带的 AppArmor 会限制 mysqld 访问的目录。默认策略只允许访问 /var/lib/mysql 和 /var/log/mysql 等目录。你把 datadir 改成 /data/mysql 之后如果不修改 AppArmor 配置服务会启动失败日志里会出现Permission denied之类的字样。最简单的做法是给 /etc/apparmor.d/tunables/alias 添加一行路径别名alias /var/lib/mysql/ - /data/mysql/,然后重新加载 AppArmorsudo systemctl reload apparmor如果你想更精确地控制可以编辑 /etc/apparmor.d/usr.sbin.mariadbd把原来规则里的路径替换成新路径。或者保持 alias 这种方法它对系统里其他也需要读取数据库文件的工具最友好。CentOS/RHEL 这边是 SELinux。安装好策略管理工具后给新目录设置正确的上下文标签sudo yum install -y policycoreutils-python-utils sudo semanage fcontext -a -t mysqld_db_t /data/mysql(/.*)? sudo restorecon -Rv /data/mysqlmysqld_db_t是数据库文件目录的 SELinux 类型要确保 /data/mysql 下所有内容都带上这个标签。设置完可以用ls -Z /data/mysql检查。如果你不想装 semanage也可以用 chcon 临时打标签sudo chcon -R -t mysqld_db_t /data/mysql但 chcon 是临时性的如果文件系统重新标记或者执行了 restorecon标签会被重置。生产环境还是用 semanage 加 fcontext 更规范。3.5 启动验证与旧目录的保留策略配置改完SELinux 或 AppArmor 也处理好了就可以尝试启动sudo systemctl start mariadb sudo systemctl status mariadb然后进数据库执行SELECT datadir, socket, pid_file;如果结果是 /data/mysql说明 datadir 已经改成功。再验证一下最基本的读写mysql -uroot -p -e CREATE DATABASE test_after_migrate; DROP DATABASE test_after_migrate;一切正常之后先把旧目录改个名字保留一段时间不要急着删除sudo mv /var/lib/mysql /var/lib/mysql.bak.$(date %F)我建议保留一到两周确认线上跑得完全正常之后再手动清理。这样做最坏情况下你还能把旧目录改回来回滚比删掉之后后悔强多了。我自己见过太多人迁移完直接 rm -rf 旧目录过几天发现某个自定义函数没备份只能干瞪眼。4. 迁移后的故障排查我把常见报错和解决方式都整理出来了4.1 服务起不来先查这三处路径迁移后最典型的问题就是服务起不来。遇到这种问题先不要慌按顺序查三处。第一处是 systemd 状态和日志systemctl status mariadb -l journalctl -u mariadb -n 50第二处是数据库错误日志明确指定过 log_error 的就去那个路径看没指定过的就去系统默认位置。报错里如果出现Permission denied大概率是权限或 SELinux/AppArmor 问题。报错里如果出现Cant open file ./mysql/...大概率是文件拷贝不完整或权限不对。报错里如果出现InnoDB: Unable to lock ./ibdata1说明有残留的 mysqld 进程还在跑或者错误日志文件被别的进程占用。第三处是目录权限和数据目录归属。执行namei -l /data/mysql ls -laZ /data/mysql | head看到目录属主不是 mysql:mysql或者 SELinux 标签不对基本就能定位了。我自己遇到得最多的是迁移后忘了把新目录的 SELinux 上下文设置成 mysqld_db_tsystemctl status 显示的错又不是那么直白害得我绕了好大一个圈子。后来我学乖了凡是迁移完启动失败第一反应就去看ls -Z十次有八次是标签问题。4.2 socket 路径漂移导致的客户端连不上如果你修改了 socket 路径客户端连接时很容易出现这个错误ERROR 2002 (HY000): Cant connect to local MySQL server through socket /run/mysqld/mysqld.sock (2)原因很直白客户端默认去找配置里的 socket服务端却把 socket 生成到了另一个地方。解决办法有两个方向。方向一在配置文件的[client]和[mysqld]段里统一 socket 路径。例如[client] socket/run/mysqld/mysqld.sock [mysqld] socket/run/mysqld/mysqld.sock方向二连接时显式指定 socketmysql -uroot -p -S /run/mysqld/mysqld.sock如果是通过 TCP 连接远程数据库就写-h 127.0.0.1 -P 3306强制走 TCP。这里建议保持默认 socket 位置不要随意挪因为它放在 /run 或 /tmp 下没有体积问题而且本地客户端也默认去那里找没必要给自己找麻烦。4.3 root 密码遗忘或认证方式不对怎么办这是一个高频问题尤其是 Ubuntu 系默认 unix_socket 认证有些朋友折腾密码时越改越乱最后干脆登不进去了。找回密码的思路是跳过授权表启动数据库。sudo systemctl stop mariadb sudo -u mysql /usr/sbin/mariadbd --skip-grant-tables --skip-networking 添加--skip-networking很重要不然跳过授权表期间数据库会不设防地监听在 3306 端口等于裸奔。然后连接数据库mysql -uroot进去后先刷新权限再修改 root 的认证方式FLUSH PRIVILEGES; ALTER USER rootlocalhost IDENTIFIED VIA mysql_native_password USING PASSWORD(新的强密码);修改完成后退出把刚才手动启动的 mariadbd 进程杀掉再用 systemd 正常启动sudo pkill mariadbd sudo systemctl start mariadb这里要提醒一句别再一股脑把 root 的认证插件改成mysql_native_password而完全去掉 unix_socket。在 Ubuntu 系上保留unix_socket OR mysql_native_password这种组合最实用既能机房内 sudo 免密管理也能远程密码登录两边都不误。4.4 binlog 和日志文件继续膨胀路径迁移不够彻底很多人以为把 datadir 迁走就万事大吉了结果过段时间发现系统盘又满了。一查问题出在 binlog、undo log 或慢查询日志还在原地写。如果 binlog 默认写在旧 datadir而你没改配置那么搬迁之后新数据可能因为配置里 log_bin 设置的路径仍然写到旧目录。所以迁移路径后一定要检查一下mysql -uroot -p -e SHOW VARIABLES LIKE log_bin%; mysql -uroot -p -e SHOW VARIABLES LIKE slow_query_log_file; mysql -uroot -p -e SHOW VARIABLES LIKE log_error;然后把需要放到数据盘上的路径统一改到新目录。binlog 的膨胀问题建议同时设置过期时间参数作用参考值expire_logs_days老版本 binlog 保留天数7binlog_expire_logs_seconds新版本 binlog 保留秒数6048007 天max_binlog_size单个 binlog 文件大小上限512Mmax_binlog_cache_size大事务的 binlog 缓存上限视事务大小而定这样设置之后binlog 会自动清理不会再无限积累。慢查询日志也可以加个轮转比如用 logrotate 或者系统自带的维护任务。5. 迁移完不是终点后续运维检查和参数调整建议5.1 数据校验与重启演练路径迁移完、客户端也能连上先别急着宣布大功告成。我会建议再做一轮数据完整性校验。最简单的方法是查看到所有库和表的数量确认没有在拷贝过程中丢文件SELECT COUNT(*) FROM information_schema.tables WHERE table_schema NOT IN (mysql, performance_schema, information_schema);然后跑一遍 mysqlcheckmysqlcheck -uroot -p --all-databases --check这个命令会对所有表做检查。表多的话耗时会长一点但迁移之后值得做一次。如果你用了 InnoDB其实它自身有崩溃恢复机制只要错误日志没有报错一般问题不大。但 MyISAM 引擎的表迁移后检查一下就重要得多因为它是非事务引擎对文件一致性要求更敏感。再做一个重启演练systemctl restart mariadb然后确认服务自动起来、客户端能正常连接。这一步能发现很多设置好了但经不起重启的隐患。比如有些配置路径虽然在启动时被自动创建了但目录的属主不对重启到一半就挂了。提前演练一次总比半夜出故障再动手强。5.2 备份脚本和监控项记得同步路径路径迁移后最容易忽视的就是备份脚本。原来备份脚本里写的是/var/lib/mysql迁移后如果不改备份的不是空目录就是直接备份失败。我建议迁移完成后全盘搜一遍旧路径grep -r /var/lib/mysql /etc/cron* /opt/scripts 2/dev/null凡是发现旧路径统一替换成新路径。特别是 mysqldump 或 xtrabackup 这类备份工具它们不一定只看配置文件有些脚本会直接写死路径。监控方面也要同步调整。原来只监控系统盘空间的建议把数据盘的使用率也加进去。可以写一个简单的定时任务或者直接在已有的监控系统里新增一条数据盘磁盘使用率监控。别小看这一步路径迁移的本质通常就是把数据从一块盘挪到另一块盘如果新盘的容量也不充裕那只是把故障时间往后推迟了而已。5.3 和存储路径相关的几个性能参数数据目录迁到独立数据盘之后顺手把性能参数也调一调效果通常比在系统盘上一直跑默认值要好。首先是 innodb_buffer_pool_size这是 InnoDB 最关键的内存参数建议设置为物理内存的 50%-70%。如果你原来不敢设太高是因为系统盘和业务程序共用内存那么现在数据库独占一台或跑在独立节点上这个参数就可以放开一些。其次是 tmpdir如果机器内存比较大可以把 tmpdir 指向 tmpfs[mysqld] tmpdir/dev/shm这样临时表和排序操作直接在内存中完成性能提升非常明显。但需要注意 /dev/shm 默认大小是物理内存的一半如果临时文件特别大容易撑爆内存。设置前评估好你的实际负载。还有 innodb_log_file_size。MariaDB 10.6 之后引入了 innodb_redo_log_capacity自动管理 redo log 大小旧的手工设置方式在新版本里不再推荐。如果你想调建议优先用新参数。参数调整后重启数据库再用SHOW VARIABLES和SHOW ENGINE INNODB STATUS\G确认实际生效情况。不要一次调太多每次改一两个参数观察几天再继续。5.4 更省事的方案新装环境里直接指定 datadir如果你现在还没安装 MariaDB想从根本上避免搬迁的麻烦可以在安装之前就规划好。比如数据盘已经挂载到了 /data那就先创建好目录sudo mkdir -p /data/mysql sudo chown -R mysql:mysql /data/mysql然后把配置文件的 datadir 预先写好再启动服务。如果是全新初始化可以这样sudo mariadb-install-db --usermysql --datadir/data/mysql sudo systemctl start mariadb这种方式比先装默认路径、再搬迁省事得多也避免了拷贝大文件的时间消耗和潜在风险。尤其是大数据量的场景一次初始化比几十 G 的 rsync 快太多了。所以我的习惯是新机器装数据库第一步挂载数据盘第二步创建 mysql 目录第三步写配置第四步初始化第五步启动。把路径规划做在前面后面就不用再折腾那套停机、rsync、改配置、处理 SELinux的流程了。最后说一点自己的体会我迁移过不少次数据目录也帮朋友处理过各种稀奇古怪的启动失败。总结下来路径本身不复杂复杂的是各个 Linux 发行版自带的那些安全机制。Ubuntu 的 AppArmor、CentOS 的 SELinux这些平时不起眼的东西一旦动了默认数据目录就会变成第一道拦路虎。所以每次迁移前我都习惯先把getenforce和/etc/apparmor.d里的规则看一遍心里有数再动手。还有一个经验是改配置任何时候都要留回滚余地。旧目录保留两周配置文件用cp复制一份备份甚至把修改前的my.cnf原样存下来。这些操作看起来多余但真的能救命。你要明白数据库这种组件宁可动作慢一点也不要因为没有退路而把自己逼到墙角。按这个流程来Linux 上改 MariaDB 存储路径是一件非常稳妥的事情。
返回列表