写这篇内容前,先说一下背景。我在实际运维中遇到过太多次“MySQL装好了,但跑起来全是毛病”的情况,尤其是在自定义安装目录这件事上。默认路径安装确实省事,但等你真正要把数据盘独立出来、或者给数据库单独划分一块空间的时候,默认方案就会让你很被动。这篇文章就是把我在生产环境里用RPM方式安装MySQL 8.4.7、并且自定义目录的完整流程和经验整理出来,给同样需要做这类操作的同行参考。
1. 整体设计思路:为什么选RPM加自定义目录
1.1 安装方式选型:RPM方案的优势
MySQL在Linux下的安装方式大致有几种:yum/apt源在线安装、RPM包手动安装、tar.gz免编译解压安装,以及源码编译。很多人图省事直接yum install,也有一部分人喜欢tar.gz包解压后自己初始化,两种方案我都实际用过,说说我的选择。
RPM方式的核心优势其实是它和系统的自然集成。装完之后systemd服务脚本、mysql用户、默认配置文件、/var/lib/mysql等目录结构都会自动生成,这意味着后续用systemctl管理、用semodule做SELinux适配、用rpm -q去校验安装完整性都很顺手,不会出现“文件不知道装在哪个目录,卸载像手拆炸弹”的局面。
相比tar.gz方式,RPM省去了大量手工步骤:不需要自己创建mysql用户、不需要手写systemd服务文件、不需要纠结PATH环境变量。但代价就是你得接受RPM预设的很多默认路径。而我们这次要做的自定义目录,恰恰是在RPM方案的基础上,把它们配置文件中预设的datadir、socket、log等路径替换成自己规划的目录。
再说下版本,8.4.7这个版本值得装。MySQL 8.4是一个LTS版本,意味着它有长期的补丁支持和稳定的行为表现。
1.2 为什么要自定义目录
直接回答最核心的问题:默认安装不好吗?也不是不好,默认路径适合测试环境和标准化的内部云环境。但如果你遇到下面这几种情况,自定义目录几乎是必选项:
第一,数据盘独立。生产环境的服务器几乎都会单独挂载数据盘,系统盘往往只有几十G或者容量紧张,而数据库的数据只要跑起来就是几十上百G。
第二,备份恢复时需要路径可控。你知道数据在哪个目录,做物理备份、冷拷贝、迁移的时候,操作成本会低很多。
第三,公司内部有目录规范。很多公司会对中间件目录做统一规定,数据目录、日志目录、临时目录要各归其位。
第四,避免系统盘和数据库互相影响。系统日志疯狂写入导致磁盘满了,结果把MySQL的数据写崩——这类事故很多人应该都见过。数据放在独立目录,至少能隔离风险。
1.3 目录规划建议
自定义目录不是随便改一个路径就完事,而是要把几个核心目录分开规划:
- 数据目录(datadir):用来存放ibdata、ibd、undo等核心数据文件
- 日志目录:error log、慢查询日志、binlog分开
- socket和pid目录
- 临时目录(tmpdir)
我自己常用的规划方案是这样的:
/mysql/app # 程序安装目录(RPM默认装在/usr,这里指自定义的软链或扩展目录可选) /mysql/data # 数据文件目录 /mysql/logs # 错误日志、慢日志 /mysql/binlog # 二进制日志独立存放 /mysql/tmp # 临时目录 /mysql/run # socket和pid文件目录当然上面的/mysql可以改成任何符合你们规范的路径。分隔思考是核心:数据是数据、日志是日志,别混在一起。后面配置的时候,你会发现这些目录会一一对应到my.cnf里的具体参数,这就是“先规划再做配置,再动手装”的节奏。
2. 核心细节拆解:RPM包的构成与默认路径陷阱
2.1 RPM包组成说明
MySQL 8.4.7通过RPM方式安装时,实际上分成好几个包,每个包都有自己的职责。生产环境安装时建议全部装,不要只装server。常见的包包括:
- mysql-community-server:服务端核心
- mysql-community-client:命令行客户端
- mysql-community-common:公共文件,包括字符集等
- mysql-community-libs:客户端共享库
- mysql-community-icu-data-files:ICU数据文件,涉及字符集比较和排序
- mysql-community-client-plugins:客户端插件(在8.0.33版之后加入的拆分)
我在某个模拟项目中遇到过只装server和client,结果后来程序连接数据库时报找不到libmysqlclient.so的错误,就是因为libs包没装。所以最简单的处理方式是下载缺一不可的官方rpm包全部安装。
2.2 必须知道的默认路径陷阱
RPM方式虽然省心,但它会默认把各种文件放到系统目录,这是两个“坑”的源头。先看RPM安装会生成哪些关键路径:
| 路径 | 默认内容 |
|---|---|
| /usr/bin/mysql | 客户端程序 |
| /usr/sbin/mysqld | 服务端程序 |
| /usr/lib/systemd/system/mysqld.service | systemd服务脚本 |
| /etc/my.cnf 以及 /etc/my.cnf.d/ | 配置目录 |
| /var/lib/mysql | 默认数据目录 |
| /var/log/mysql/mysqld.log | 默认日志(不同发行版略不同) |
| /var/run/mysqld | 默认socket/pid目录 |
| /tmp | 默认临时目录 |
这里有两个非常容易踩的坑。第一,在你还没修改配置之前,mysqld在初始化时会把数据写到/var/lib/mysql。如果安装前没有规划,又会多一步数据迁移。所以正确的顺序是:安装RPM包之后,初始化之前,就把配置文件改好。第二,即使你改了my.cnf中的datadir,systemd服务脚本里的一些参数可能还默认指向/var/lib/mysql,所以你需要确认服务脚本是否会覆盖配置。
从MySQL 8.0开始,mysqld的systemd服务里加载配置时也是读取/etc/my.cnf,然后根据其中的datadir启动。只要配置文件写对,服务脚本本身一般没问题,但要注意SELinux的上下文,后面排查部分我会细说。
2.3 配置文件改写的关键思维
8.4版本的MySQL,配置文件和以前版本的一个重大区别是:部分变量只支持放在[mysqld]段里,例如datadir、basedir、socket这些核心路径参数,你放在[mysqld_safe]或者[client]段里虽然不会直接报错,但可能不生效。
改写配置的要点是“先找默认,再改默认”。装完RPM之后,首先查看cat /etc/my.cnf里面的基础内容,把需要的自定义项加上去。通常这个文件里内容很少,真正的配置在/etc/my.cnf.d/目录下的多个文件里(比如mysql-server.cnf)。改配置的时候要注意各配置文件的加载顺序,后加载的同名参数会覆盖先加载的同名参数。
在后续配置里我会给出一个完整的my.cnf配置示例,直接把自定义目录都写好。
3. 实操全流程:从安装到验证
3.1 环境准备与基础依赖
本次演示的环境是Rocky Linux 9.x(CentOS 7也可以参考),x86_64架构。开始之前先做三件准备工作。
确认系统版本和架构:
cat /etc/os-release arch如果是8.4版本,还需要确认glibc版本,我下面演示的版本在glibc 2.28以上环境均可运行。接下来安装MySQL仓库的RPM包,把官方仓库加入yum源(这里用社区仓库的通用地址,建议直接到官网下载RPM包来装,避免网络源变化影响):
yum install -y https://repo.mysql.com/mysql84-community-release-el9.rpm然后检查可用的MySQL版本列表:
yum list available | grep mysql-community看到列出了mysql-community-server、client等包,就说明仓库生效了。但这里我要特别提醒一句,生产环境我建议直接下载RPM包到本地安装,不要用yum install mysql-community-server这种在线方式。原因是:在线方式会拉取仓库里当前最新的版本,虽然理论上8.4仓库只会给8.4.x系列,但你自己下载固定版本的RPM包才能保证可复现性。我自己在验证某个项目时就是固定下载8.4.7的这几个包,放到一个目录,再统一安装。
安装之前需要处理两个依赖:libaio和perl。MySQL初始化过程中会用到libaio库,缺少时会报“error while loading shared libraries: libaio.so.1”。
yum install -y libaio perl net-tools3.2 创建自定义目录并规划磁盘
这一步要在安装RPM包之前或者安装后立刻做,但绝对要在初始化之前。我们假设数据盘挂载在/mysql这个挂载点上,现在在这个挂载点下再创建子目录:
mkdir -p /mysql/data mkdir -p /mysql/logs mkdir -p /mysql/binlog mkdir -p /mysql/tmp mkdir -p /mysql/run然后把这些目录的属主和属组都改成mysql用户(RPM安装过程会自动创建mysql用户):
chown -R mysql:mysql /mysql这里有个关键细节:chown要在安装MySQL包之后执行,因为需要mysql用户存在。如果先创建目录再安装包,安装包时创建的mysql用户不会自动获得目录所有权,还得再执行一次chown。如果你在安装RPM包之前就把目录建好了,等RPM安装完成后,确认mysql用户存在,然后执行一次chown即可。
另外一个容易被忽略的点:磁盘挂载。如果/mysql是一个独立的逻辑卷或磁盘分区,建议在fstab里加上noatime参数,减少文件访问时间更新带来的额外IO开销。对于数据库这种IO敏感型应用,noatime确实能带来细微但可感知的性能提升。
3.3 安装RPM包并确认安装结果
下载好官方RPM包之后,把包放到统一目录,然后用rpm命令逐个安装。这里我推荐一种做法:先install common、libs、client依赖,最后install server。不过实际做的时候往往一条命令搞定:
rpm -ivh mysql-community-common-8.4.7-1.el9.x86_64.rpm \ mysql-community-client-plugins-8.4.7-1.el9.x86_64.rpm \ mysql-community-libs-8.4.7-1.el9.x86_64.rpm \ mysql-community-client-8.4.7-1.el9.x86_64.rpm \ mysql-community-icu-data-files-8.4.7-1.el9.x86_64.rpm \ mysql-community-server-8.4.7-1.el9.x86_64.rpm用rpm -ivh而不是yum install的好处是,它不会自动帮你解决依赖。如果系统缺依赖会直接报错,你反而可以清楚地知道缺什么再补什么。而yum install会自动处理依赖,有时候处理得“过度”,把自己不想要的包也带进来。
安装完成后,验证一下包是否完整:
rpm -qa | grep mysql应该看到mysql-community-server-8.4.7-1.el9.x86_64等几个包都在。同时确认mysql用户已经创建:
id mysql到这里程序就装好了,但数据库还不能启动,因为没有初始化数据。这时候系统里其实已经有一个未初始化过的mysqld服务了,我们先不要启动它,先把配置文件写对。
3.4 配置文件修改:把路径全面指向自定义目录
MySQL 8.4的配置目录结构是/etc/my.cnf加/etc/my.cnf.d/。在RPM安装后,/etc/my.cnf通常只包含includedir指令,真正的配置在/etc/my.cnf.d/mysql-server.cnf里。
如果你使用的是MySQL 8.4官方仓库的RPM包,编辑/etc/my.cnf.d/mysql-server.cnf,写入如下配置:
[mysqld] basedir=/usr datadir=/mysql/data socket=/mysql/run/mysql.sock pid-file=/mysql/run/mysql.pid log-error=/mysql/logs/error.log slow_query_log=ON slow_query_log_file=/mysql/logs/slow.log long_query_time=2 tmpdir=/mysql/tmp binlog_format=ROW server-id=1 log_bin=/mysql/binlog/mysql-bin expire_logs_days=7 [client] socket=/mysql/run/mysql.sock [mysql] socket=/mysql/run/mysql.sock这里面对路径的每一项改动,我解释一下为什么这么写:
- datadir改到/mysql/data后,后续mysqld --initialize时会把系统库文件生成在这里
- socket和pid-file放在/mysql/run下,避免/var/run这个tmpfs在系统重启时被清除后出现socket文件丢失的问题。当然这个目录如果也在tmpfs上,需要在系统启动时自动创建并赋予mysql权限。
- log-error单独出来,因为错误日志对排查数据库问题至关重要,建议和慢日志分开存
- binlog放在/mysql/binlog里,这是生产环境里值得单独分离的,因为binlog增长非常快,不控制好很容易把磁盘写满,单独放一个目录可以单独做容量监控和清理策略
之前提过的log_bin默认情况下8.4版本是开启的,这里显式写出来是为了让binlog路径也自定义。server-id=1是开启binlog的必要条件,如果你不写server-id,log_bin配置不会生效。
注意一个容易踩的坑:[mysqld]段落里有些参数在8.4版本中不允许被忽略,如果配置写在了[mysqld_safe]段,8.4可能根本不会读取。所以所有路径参数务必放在[mysqld]段。
确认配置无误后,可以用下面命令检查mysqld能否识别这些配置:
mysqld --verbose --help | grep -A 1 '^datadir'配置正确的话会输出/mysql/data。
3.5 初始化数据库:mysqld --initialize
配置文件改好之后,下一步是初始化数据目录。8.0系列开始用mysqld --initialize代替了老版本的mysql_install_db。这一步会创建系统表空间、系统库(mysql、performance_schema、sys等),并生成一个临时root密码。
执行初始化命令:
mysqld --initialize --user=mysql --datadir=/mysql/data注意这里有几个细节。第一,--datadir要和my.cnf里的datadir一致,否则会出现初始化目录和运行目录不一致的问题。第二,--user=mysql会让初始化过程中的文件操作以mysql用户身份执行,如果你之前没有chown目录,这里就会报权限错误。第三,初始化数据库前确保/mysql/data目录是空的,不能有残留文件。
初始化过程没有输出就是成功,可以查看错误日志确认:
tail -n 30 /mysql/logs/error.log日志中会有类似这样一行:
[Note] A temporary password is generated for root@localhost: xxxxxxxx这个临时密码我们要保存好,第一次登录必须用它。如果哪次初始化后没有找到临时密码,排查方向通常是error log路径不对,或者error log的权限问题导致写不进去。
3.6 启动服务并验证所有目录生效
初始化完成后,启动服务:
systemctl start mysqld systemctl status mysqld服务状态显示active (running)就说明启动成功。但是启动成功不等于我们自定义目录生效了,必须要做双重验证。
先用systemd看进程状态:
systemctl status mysqld -l再用客户端连接验证:
mysql -uroot -p --socket=/mysql/run/mysql.sock输入之前日志中记录的临时密码,登录成功后第一件事就是改密码。8.4默认启用了密码校验插件,临时密码一般是强密码,新密码也需要符合复杂度要求。
ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourStrongPassw0rd!';然后刷新权限:
FLUSH PRIVILEGES;这时候可以确认一下所有关键路径变量是不是已经指向自定义目录:
SHOW VARIABLES WHERE Variable_name IN ('datadir','socket','pid_file','log_error','tmpdir','log_bin');如果每一项都对应着你规划的路径,说明安装成功,并且自定义目录已经全部生效。
顺便说一下,连接时可以不必每次指定socket。默认情况下客户端会去/var/run/mysqld/mysql.sock找socket文件,而我们改到了/mysql/run/mysql.sock,如果不加--socket参数就会报“Can't connect to local MySQL server through socket”。建议在/etc/my.cnf的[client]段写上socket路径,或者使用TCP方式连接。
3.7 设置开机自启与基础安全加固
RPM安装的MySQL在systemd下,一般默认就是enabled状态,但最好确认一下:
systemctl enable mysqld然后做几项安全加固:
mysql_secure_installation这个脚本会引导你完成:设置root密码强度、删除匿名用户、禁止root远程登录、删除test数据库。生产环境我建议都选yes。但要注意,如果你的业务确实需要root远程访问,建议单独创建一个管理员账号,而不是开root的远程权限。
另外把3306端口检查一下:
ss -tlnp | grep 3306如果只想本机访问,把bind-address设置成127.0.0.1。如果需要局域网访问,就在my.cnf的[mysqld]段加bind-address=0.0.0.0,同时通过防火墙放行3306端口,并在MySQL里创建允许对应主机访问的账号。
4. 常见问题与排查技巧实录
自定义目录安装过程中,我实际遇到过不少问题,挑几个典型场景列出来,对照排查可以少走很多弯路。
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 启动报错 Failed to start mysqld.service | 目录权限不足 | chown -R mysql:mysql /mysql 后重启 |
| 连接时 Can't connect through socket | socket路径不一致 | 确认my.cnf中[client]和[mysqld]的socket一致 |
| 初始化日志提示 Error opening file /mysql/data/ibdata1 | 目录属主错误 | 检查/mysql/data属主是否为mysql |
| 修改密码报 ERROR 1819 | 密码复杂度不够 | 设置包含大小写、数字、特殊字符的强密码 |
| 启动后访问非常慢 | SELinux context异常 | 执行restorecon -R /mysql 或调整SELinux策略 |
| binlog没有生成 | server-id未配置 | 在[mysqld]段增加server-id=1 |
4.1 目录权限导致的初始化失败
这个问题出现频率最高。症状就是initialize执行完马上报错,日志显示无法创建文件。我在一个模拟项目里就遇到过:系统里预先创建好了/mysql目录但是忘了chown,mysqld初始化时以mysql用户身份操作,结果没有写权限。
排查思路如下:执行ls -ld /mysql/data,查看属主是否为mysql。如果不是,执行chown -R mysql:mysql /mysql,然后清空/mysql/data下残留的文件(注意:没有成功初始化的残留文件是可以直接清掉的),再重新执行mysqld --initialize。
4.2 SELinux导致服务启动失败
对于RHEL系发行版,这一点必须单独讲一讲。RPM安装方式下,SELinux对默认的/var/lib/mysql目录有预设的策略,但自定义目录不在默认安全上下文里,会直接被SELinux拦掉。遇到的现象是systemctl start mysqld后服务状态是failed,但journal日志里看不到明确报错。
最简单的解决方式有两个。一是恢复目录的SELinux上下文:
restorecon -R /mysql这样/mysql会被标记为mysqld需要的上下文(如mysqld_db_t)。第二个方式是临时关闭SELinux验证是否SELinux导致(不推荐长期关闭):
setenforce 0如果是SELinux的问题,setenforce 0后服务就能正常启动。生产环境建议用restorecon加自定义策略的方式,而不是直接关闭SELinux。
4.3 密码策略导致的修改失败
8.4版本的validate_password组件默认是激活的,初始化之后临时密码是强密码,但你想改成“123456”这种弱密码时就会被拒绝。这在测试环境很常见。
如果你确定这台机器的密码策略不需要太高,可以在登录后先降低策略等级:
SET GLOBAL validate_password.policy = LOW; SET GLOBAL validate_password.length = 6;然后再执行:
ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';但生产环境我又得提醒一句:不要随意降低密码策略。数据库密码是底线安全,建议保留默认的强度策略,宁可让开发同学把密码设置的复杂一点。
4.4 systemd服务里残留默认路径的排查
还有一种情况:my.cnf里配置全都改好了,但systemd服务脚本里把某些参数硬顶掉了。比如mysqld.service里ExecStart启动命令行中带了--datadir=/var/lib/mysql,那你改my.cnf就无效了。
排查方法:
systemctl cat mysqld如果在ExecStart行看到明确的--datadir参数,就说明配置文件被命令行参数覆盖了。解决方法是创建systemd的override文件:
systemctl edit mysqld然后写入:
[Service] ExecStart= ExecStart=/usr/sbin/mysqld --datadir=/mysql/data注意第一行ExecStart=是清空原来指令,第二行写新指令。这种问题在RPM方式下不常见,但如果你的系统之前有其他MySQL残留,就会遇到。另外,一定要确认MySQL 8.4的RPM默认服务脚本没有额外指定datadir参数,目前官方RPM不做这个事,配置完全交给my.cnf,所以正常情况下我们可以不用override。
4.5 tmpdir空间不足导致SQL执行失败
自定义目录后还有一个隐藏坑:tmpdir指向了/mysql/tmp,这个目录如果容量较小,执行大排序或者大事务时可能报错“No space left on device”。排查方法:
df -h /mysql/tmp生产环境建议把tmpdir放在和datadir不同的磁盘上,避免数据目录IO竞争。如果你的服务器内存够大,也可以把tmpdir指向dev/shm这种内存盘,但要注意内存溢出风险。我一般不太建议直接把tmpdir放内存盘,除非你非常清楚业务模型。
5. 最后分享一点经验
在我接触的案例里,很多人装MySQL都能装成功,但过了一个月就开始出问题:日志写满系统盘、binlog找不到、迁移时不知道数据到底在哪。这些问题本质上都是安装时没有在“目录规划”这件事上想清楚。RPM方式本身没有错,它反而是最容易被标准化的安装方式,关键在于你有没有在初始化之前把配置文件的路径定制到位。
另一个我在多台机器上验证过的经验是:安装文档化。建议把安装步骤、配置文件、目录规划整理成一份清单,保存到公司的知识库。别小看这件事,半年后你要在新服务器上再部署一台同版本实例时,这份清单能让你从半天时间压缩到半小时。
如果你当前面对的是已有数据的旧MySQL环境,要做目录迁移,那流程会稍有不同:需要停机、拷贝数据文件、修改配置、启动验证。后续如果有机会,我再用一篇文章把数据目录迁移的细节整理出来。那篇文章里我会重点写“怎么安全地把/var/lib/mysql的数据搬到新目录而不损坏binlog和undo文件”,到时候欢迎继续交流。