如果你在 CentOS 上手动下载过 MySQL 的 tar.gz 包,大概率体会过这种感觉:解压十分钟,启动报错半小时,最后发现是缺了某个动态库。在 Linux 服务器上装 MySQL,从来不是缺教程,缺的是一套能一次跑通、出了问题也知道去哪查的流程。这篇东西是我从多次重装和帮同事救火里攒下来的实践记录,目标读者是刚接手 Linux 服务器、需要在 CentOS 上部署 MySQL 的运维和开发。看完你至少能搞明白三件事:装之前要确认什么,装的过程每一步在干什么,以及装完之后为什么别人还是连不上你的数据库。
MySQL 安装本身不算难,但你观察那些翻车案例会发现,绝大多数问题都出在装之前和装之后。装之前没确认系统版本和残留包,装完之后没处理权限、防火墙、SELinux。所以这篇文章不会只给你几条命令,而是把每一步背后的逻辑讲清楚,方便你举一反三。
1. 动手前先想清楚:装 MySQL 前必须确认的几件事
1.1 先看清你的 CentOS 属于哪个“流派”
很多人上来就执行安装命令,报了错才开始查系统版本,这是最常见的弯路。CentOS 7、CentOS 8、CentOS Stream 9 虽然都叫 CentOS,但软件包管理、默认行为和生命周期差异很大,尤其是用 MySQL 官方 Yum 源的时候,el7 和 el8 的 RPM 包不能混用。你在 CentOS 8 上强行装 el7 的包,大概率会遇到依赖冲突,或者装上之后服务起不来。
动手前先看清楚系统版本:
bash复制cat /etc/redhat-release
cat /etc/os-release
uname -m
第一个命令给你发行版名称和版本号,第二个命令能看到更详细的 ID、版本 ID,第三个命令告诉你架构是 x86_64 还是 aarch64。后面从官网下载 RPM 包时,el7 还是 el8、x86_64 还是 aarch64,都靠这几个信息判断。
这里多提一句生命周期问题。旧版 CentOS 停止维护后,默认镜像源地址会迁移到 vault 路径,如果你直接执行 yum install,可能遇到源 404 或者镜像列表为空。这也是为什么我建议你优先使用 MySQL 官方维护的 Yum 源,而不是依赖系统自带的 AppStream 源——官方源的生命周期和版本更新策略是独立管理的,长期看更稳定。
1.2 MySQL 8.0 还是 5.7,怎么选
现在新装 MySQL,除非有特殊兼容性要求,否则我建议直接上 8.0。8.0 和 5.7 相比,几个核心差异会影响你的日常使用:
- 默认字符集从 latin1 改成了 utf8mb4,对中文、Emoji 的支持明显更好,省去了一堆建表时的字符集设置。
- 身份认证插件默认是 caching_sha2_password,安全性更高,但老版本的客户端驱动可能不兼容,后面会专门讲这个问题。
- 窗口函数、公共表表达式(CTE)这些现代 SQL 特性终于补齐了,写复杂统计查询会舒服很多。
- 查询缓存(Query Cache)在 8.0 里被移除了。早期很多“优化建议”会让你开 query_cache,现在这套思路已经过时,网上看到相关配置直接忽略。
但如果你是在维护一个跑了好几年的老项目,代码里可能用了老版本驱动的连接串,甚至用了 5.7 才有的语法或行为,那这时候就不要盲目升级。稳妥做法是装 5.7,或者先装 8.0 做兼容性测试再决定。版本选择本质上是“业务需求优先”还是“技术先进优先”的权衡,没有绝对答案。
1.3 安装方式对比:为什么不推荐自己下载 tar.gz
MySQL 在 Linux 上的安装方式主要有三种:官方 Yum 源安装、RPM 包离线安装、源码编译安装。另外还有 tar.gz 二进制包解压的方式。
tar.gz 二进制包看起来最简单:解压、初始化、启动,三步走。但坑在于依赖。MySQL 运行需要 libaio、numactl 等系统库,tar.gz 包不会替你检查,也不会替你安装。等你执行 mysqld --initialize 发现报错找不到 libaio.so.1,只能再去 yum install,反而绕了一圈。更麻烦的是 systemd 服务文件要自己写,防火墙、SELinux 策略、开机自启这些都得手动配,对新手非常不友好。
源码编译安装适合对性能有极致要求或者需要自定义编译参数的高级用户,普通业务部署完全没有必要。RPM 离线安装适合内网环境或者无法访问外网的服务器,但你需要提前下载好 MySQL server、client、common、libs 等一系列 RPM 包,依赖关系要理清楚,比较繁琐。
所以对绝大多数 CentOS 服务器来说,最省心的方案就是用 MySQL 官方 Yum 源。它帮你处理依赖,提供 systemd 管理脚本,升级也方便,一条 yum update 就能完成小版本更新。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从仓库到服务:用 Yum 安装 MySQL 8.0 的完整操作
2.1 先把系统环境和残留包处理干净
安装 MySQL 之前,先花两分钟检查系统里有没有已经存在的 MySQL 或 MariaDB 相关组件。很多 CentOS 镜像默认装了 MariaDB 的客户端库,或者之前别人折腾过装了一半的 MySQL,这些残留包会占用端口、冲突文件,让新装的服务起不来。
bash复制rpm -qa | grep -E 'mysql|mariadb'
如果输出里有 mariadb-libs 这类包,先卸载。注意 mariadb-libs 可能被 postfix 等系统组件依赖,直接 yum remove 会连带删掉一堆东西,更安全的做法是:
bash复制yum remove mariadb-libs
卸载后如果 postfix 被连带删了,不影响 MySQL 安装,只是系统邮件功能会受影响,这通常不是问题。
接着确认 /var/lib/mysql 目录是否为空。如果之前初始化过,这个目录里会有数据文件,新装的 MySQL 启动时会认为数据库已经初始化过,从而跳过临时密码生成,导致你找不到初始密码。有旧数据就备份,没用的就清空:
bash复制ls -la /var/lib/mysql
干净环境下这个目录可能不存在,或者只有 lost+found,无需处理。
2.2 配置 MySQL 官方 Yum 源
前面提到系统自带的源不一定有 MySQL,而且版本可能不是你想要的。接下来下载 MySQL 官方的 Yum 仓库包。CentOS 7 用 el7 版本,CentOS 8 及 Stream 用 el8 版本。
bash复制# CentOS 7
rpm -ivh https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
# CentOS 8 / Stream 8
rpm -ivh https://dev.mysql.com/get/mysql80-community-release-el8-3.noarch.rpm
rpm -ivh 装完这个 release 包后,系统里会多出 /etc/yum.repos.d/mysql-community.repo 文件。里面包含了 MySQL 5.7、8.0 等多个子仓库,默认启用 8.0 仓库。你可以验证一下当前启用状态:
bash复制yum repolist enabled | grep mysql
看到 mysql80-community 就说明 OK。如果你想装 5.7,需要手动切换仓库。执行:
bash复制yum-config-manager --disable mysql80-community
yum-config-manager --enable mysql57-community
如果没有 yum-config-manager 命令,可以安装 yum-utils 工具包获得。切换后再执行 yum repolist 确认,避免你心里想装 5.7,结果装出来是 8.0。
2.3 安装、启动与开机自启
仓库源配好之后,安装过程就非常简单了:
bash复制yum install mysql-community-server -y
这一步会把 mysql-community-server、mysql-community-client、mysql-community-libs、mysql-community-common 等一串包都装好。依赖会自动处理,无需手动干预。下载量大概在几百 MB,取决于网络情况,耐心等就行。
安装完成后,先启动服务,再设置开机自启:
bash复制systemctl start mysqld
systemctl enable mysqld
systemctl status mysqld
如果看到 active (running) 的绿色状态,说明服务已经起来了。这一步偶尔会遇到 Failed to start mysqld.service: Unit not found 的情况,通常是因为安装过程没有正确生成 systemd 文件,可以确认一下 mysql-community-server 是否真的装上了,或者重启一次系统再试。
3. 初始化阶段的高发翻车点:临时密码、安全脚本与账号权限
3.1 日志里找不到临时密码怎么办
MySQL 8.0 安装后第一次启动时,会自动初始化数据目录,并为 root 用户生成一个临时密码。这个密码会写到错误日志里。找到它的命令是:
bash复制grep 'temporary password' /var/log/mysqld.log
正常情况下你能看到类似这样的输出:
code复制[Note] A temporary password is generated for root@localhost: xxxxxxxx
拉取密码后,用它登录:
bash复制mysql -uroot -p
但有很多人反馈日志里搜不到这行字,我排查过几次,原因基本是两种。
第一种,/var/lib/mysql 目录里有旧数据,MySQL 启动时跳过了初始化步骤,自然没有新密码生成。这种情况先备份旧数据,然后清空目录再重启服务。
bash复制systemctl stop mysqld
mv /var/lib/mysql /var/lib/mysql.bak.$(date +%F)
systemctl start mysqld
第二种,日志文件权限异常,导致 mysqld 没写进去。检查一下:
bash复制ls -l /var/log/mysqld.log
属主应该是 mysql:mysql,如果不是,chown mysql:mysql /var/log/mysqld.log 再重启。
3.2 mysql_secure_installation 每一步都在干什么
拿到密码登录后,第一件事是执行安全初始化脚本:
bash复制mysql_secure_installation
这个脚本会一步步问你几个问题。很多人嫌烦,一路按回车跳过,这就等于把数据库裸奔在服务器上。我帮你拆解一下每一步的实际作用:
第一个问题是是否安装密码强度校验组件。建议选择安装。MySQL 8.0 里密码策略默认是 MEDIUM,要求密码至少 8 位,且包含大写、小写、数字和特殊字符。这个策略会挡住很多弱密码,降低被爆破风险。
第二个问题是让你设置 root 新密码。如果你密码强度不够,会直接报错,需要重新输入。
第三个问题是是否删除匿名用户。默认安装后存在匿名用户,任何本地用户都可以用空密码登录,非常危险,选 Y 删除。
第四个问题是是否禁止 root 远程登录。这里要特别注意:如果选 Y,root 只能在 localhost 登录;如果选 N,root 可以从任意主机登录,安全性较差。我的建议是选 Y,然后单独创建业务账号并授权给指定 IP,而不是让 root 暴露在网络上。
第五个问题是是否删除 test 数据库。test 库是默认创建给所有人测试用的,没有实际价值,选 Y 删除。
最后一个是是否立即刷新权限表。选 Y,让刚才所有修改立即生效。
3.3 创建远程业务账号:为什么 root 不能直接连
安全脚本设置完后,你如果在本地用 mysql -uroot -p 能正常登录,但换一台机器用工具连数据库,会发现连接被拒绝。这其实是正常现象,因为 MySQL 的账号由“用户名 + 主机名”共同决定,默认的 root 账号只允许来自 localhost 的连接。
正确做法是创建一个专门给业务使用的账号,并只授权需要的数据库权限:
mysql复制CREATE USER 'app_user'@'192.168.1.%' IDENTIFIED BY 'YourStrongPass123!';
GRANT ALL PRIVILEGES ON yourdb.* TO 'app_user'@'192.168.1.%';
FLUSH PRIVILEGES;
这里 192.168.1.% 表示只允许 192.168.1 网段的主机连接,精确限制来源 IP 能有效减少攻击面。如果你确实需要任意主机访问(比如开发环境),可以用 '%',但生产环境强烈不建议。
一个容易踩的坑:MySQL 8.0 里,GRANT 语句不能隐式创建用户。旧版本你写 GRANT ALL ON ... TO 'user'@'host',如果用户不存在会自动创建;8.0 会直接报错。所以必须先 CREATE USER,再 GRANT,顺序不能颠倒。
还要注意 8.0 默认的 caching_sha2_password 认证插件。如果你的客户端是老版本的 MySQL 驱动或者老版本 Navicat,连接时可能报 Authentication plugin 'caching_sha2_password' cannot be loaded。这时候有两个选择:升级客户端驱动,或者把账号改为 mysql_native_password 插件。生产环境推荐升级驱动,mysql_native_password 虽然兼容性好,但安全性不如 caching_sha2_password。
4. 服务本身启动成功,为什么远程还是连不上
4.1 先分清是“没监听”还是“被防火墙拦了”
我处理过太多“MySQL 启动成功但远程连不上”的问题,发现大多数人第一步就判断错了方向。判断方向的方法很简单,先在 MySQL 服务器本机执行:
bash复制mysql -uroot -p
如果本机都登录不了,那问题出在 MySQL 自身,比如账号权限、密码错误或者服务异常,和网络环境没有关系。如果本机能登录,再用另一台机器测试:
bash复制telnet 数据库服务器IP 3306
如果 telnet 显示 Connection refused 或者超时,大概率是网络层面拦截或 MySQL 没有监听外网接口。接着在本机检查监听状态:
bash复制ss -lntp | grep 3306
如果看到 127.0.0.1:3306,说明 MySQL 只监听了本机回环地址。修改 /etc/my.cnf,把 bind-address 配置注释掉或改成 0.0.0.0,然后重启服务。如果看到 0.0.0.0:3306 或 *:3306,说明 MySQL 本身监听正常,问题大概率在防火墙层面。
看到 *:3306 表示 mysqld 已经监听所有接口,这时候才需要考虑防火墙。
4.2 防火墙规则:firewalld 的具体操作
CentOS 7 以上默认使用 firewalld 作为防火墙管理工具。先查看当前状态:
bash复制systemctl status firewalld
如果服务在运行,确认 3306 端口是否已经放行:
bash复制firewall-cmd --list-ports
如果没有,添加规则并重载:
bash复制firewall-cmd --permanent --add-port=3306/tcp
firewall-cmd --reload
再验证一次:
bash复制firewall-cmd --list-ports
这里有一个生产环境的重要建议:不要简单地把 3306 端口对所有来源开放。如果你用的是云服务器,安全组层面可以限制源 IP;如果你用的是物理机加 firewalld,可以用 source 限制,比如:
bash复制firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.0/24" port protocol="tcp" port="3306" accept'
firewall-cmd --reload
数据库是核心资产,监听范围越小,被扫描爆破的概率越低。
4.3 别忘了 SELinux 这个隐形拦路虎
很多 CentOS 用户会把 SELinux 直接 disabled,以省去麻烦。但如果你不想关 SELinux,或者公司安全规定不允许关,就要注意它对 MySQL 端口的限制了。默认情况下,SELinux 允许 mysqld 进程监听 3306 端口,因为该端口在 mysqld_port_t 类型里。你不需要做额外设置。
但如果你修改了 MySQL 端口,比如改成 3307,SELinux 就会拦截。检查一下:
bash复制semanage port -l | grep mysqld_port_t
如果看到默认 3306,而你改用了 3307,就需要把新端口加到策略里:
bash复制semanage port -a -t mysqld_port_t -p tcp 3307
semanage 命令由 policycoreutils-python-utils 包提供,如果没有可以安装。遇到连不上数据库且防火墙已放行的情况,可以查一下 SELinux 的审计日志:
bash复制grep mysql /var/log/audit/audit.log | tail -20
如果看到 avc: denied 关键字,就是 SELinux 拦截了。处理完后再测试连接是否恢复。
4.4 客户端连上了却被强制改密码
还有一种情况:远程连接本身通了,但执行任何语句都提示需要先修改密码。这是因为账号的密码过期状态。用这个账号登录 MySQL 后执行:
mysql复制ALTER USER 'app_user'@'192.168.1.%' IDENTIFIED BY '新密码';
执行完再试,就能正常操作了。如果不想设置过期策略,可以在创建用户时不加 PASSWORD EXPIRE,或者在账号策略里设置默认不过期。
5. 安装收尾后建议马上做的几件事:日志、字符集与基础调优
5.1 日志和备份策略:平时不起眼,出事了你才知道重要
MySQL 默认会把错误日志写到 /var/log/mysqld.log,这里记录了启动、关闭以及运行过程中的异常信息。建议你花两分钟确认一下日志的轮转机制。如果没配置 logrotate,日志文件会无限增长,最终占满磁盘。CentOS 默认的 logrotate 配置在 /etc/logrotate.d/mysqld,一般会自动处理,但值得确认一下 mysqld.log 的属主和权限,确保 mysqld 进程能正常写入。
再顺手配置下慢查询日志,这对后续定位慢 SQL 很有帮助。打开 /etc/my.cnf,在 [mysqld] 段添加:
ini复制slow_query_log = 1
slow_query_log_file = /var/log/mysql-slow.log
long_query_time = 2
long_query_time 设为 2 秒,意味着超过 2 秒的查询会被记录。这个日志在你后续做 SQL 优化时非常有用。注意建好日志目录并 chown mysql:mysql,否则 mysqld 可能没权限写。
5.2 字符集统一为 utf8mb4,别再为中文乱码折腾
如果你的 MySQL 是 5.7,或者要从老版本迁移,字符集配置几乎是必备项。虽然 8.0 默认字符集已经是 utf8mb4,但在 5.7 里默认是 latin1,应用写入中文时会直接变成问号,特别难排查。
统一字符集的方法是在 /etc/my.cnf 的 [mysqld] 段里写:
ini复制character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
然后在 [client] 和 [mysql] 段写上:
ini复制default-character-set = utf8mb4
改完重启 MySQL,然后登录验证:
mysql复制SHOW VARIABLES LIKE 'character_set%';
看到 character_set_server 和 character_set_database 都是 utf8mb4,才算真正改好。这里有个细节:如果库里已经存在 latin1 编码的老表,光改服务端字符集不会自动转换已有数据,需要 ALTER TABLE 转换。所以在建库建表前就定好字符集,是最省事的做法。
5.3 刚装完就该调整的基础参数
MySQL 安装后的默认配置偏保守,能跑但性能远不是最优。有几个参数值得在业务上线前调整。最核心的是 InnoDB 缓冲池大小,它决定了 InnoDB 引擎能缓存多少数据和索引在内存里,直接影响查询速度。一般建议设为物理内存的 50% 到 70%。如果服务器有 16G 内存,可以设 8G 到 10G。修改 /etc/my.cnf:
ini复制innodb_buffer_pool_size = 8G
设置前先确认机器确实有这么大内存,别把机器搞到 OOM。可以用 free -h 查看当前内存总量。
另一个常见参数是 max_connections。默认值是 151,对于流量稍大的应用可能不够,会出现 Too many connections 报错。可以先观察一下实际连接数:
mysql复制SHOW VARIABLES LIKE 'max_connections';
SHOW STATUS LIKE 'Threads_connected';
Threads_connected 一直逼近 max_connections 的话,就需要调大,比如 500 或 1000。但要同时考虑操作系统层面 ulimit -n 的限制,连接数越多,需要打开的文件描述符越多,两者要匹配。
还有一个容易被忽略的参数是事务隔离级别和 binlog 相关的配置。如果你打算后续做主从复制,binlog 必须提前打开:
ini复制server_id = 1
log_bin = mysql-bin
这些参数看起来简单,但上线后再改往往要重启服务,影响业务。所以趁着刚装完,一次性把基线配置好,后面能省很多事。
5.4 定时备份:不要等到数据丢了才追悔
最后提一下备份。我知道大家刚装完数据库很有成就感,但真正考验你的是某天误执行了 DELETE 没有 WHERE 条件,或者磁盘损坏。一个最简单的备份方案是用 mysqldump 做每日全量备份,配合 crontab 定时任务:
bash复制mysqldump -uroot -p'密码' --single-transaction --routines --events --triggers yourdb > /backup/yourdb_$(date +%F).sql
通过 crontab 每天凌晨执行:
bash复制0 2 * * * /usr/bin/mysqldump -uroot -p'密码' --single-transaction yourdb > /backup/yourdb_$(date +\%F).sql
注意几点:mysqldump 的路径用 which mysqldump 确认,crontab 里最好写绝对路径,避免 PATH 问题;--single-transaction 参数对 InnoDB 表可以做到非锁表备份,适合线上环境;备份要保留一定周期,可以用 find 定期清理超过 N 天的文件。
如果你对恢复时间要求很高,或者数据量很大,mysqldump 就不够用了,需要上 Percona XtraBackup 这类物理备份工具,那就是另一个话题了。
code复制
在我自己的一堆服务器上,装 MySQL 从最早的五六个小时(被依赖搞崩溃),到现在十几分钟就能跑完一套可用的实例,最关键的变化就是养成了“先确认再动手”的习惯。系统版本、残留包、仓库版本、端口监听、防火墙、SELinux、账号权限,每一个环节都提前确认和规划好,装一次就能跑很久。
最后再分享一个小技巧:每次装完 MySQL,我都会把执行过的命令和遇到的报错记录在一个 Markdown 文件里,按日期归档。下次再遇到同类问题,直接翻自己的踩坑笔记,比重新在网上搜答案快得多。你也试试,这习惯能在关键时刻帮你省下大把时间。
