做后端业务和技术运维的人,跟 Linux 和 MySQL 打交道基本是躲不开的。先说结论:Linux 环境下的 MySQL 搭建,真没有网上那些教程写得那么玄乎,核心就三件事——选对版本和安装方式、把初始化和安全配置做干净、再把数据目录和关键参数提前规划好。这篇文章我会以实际部署一台服务器为例,从操作系统选型开始,一步一步带你走完安装、初始化、调优、备份和排错的全流程,顺手把我当时踩过的坑、后来面试别人时经常问的点也都串进去。适合刚接触 Linux 的开发者、刚接手服务器的运维新人,以及想自己搭一套环境做学习和项目验证的朋友们。
1. 动手之前:先想清楚三件事
1.1 操作系统和 MySQL 版本怎么选
很多人上来就yum install mysql-server或者apt install mysql-server,结果发现装出来的版本、目录结构、初始化方式和自己搜到的教程完全对不上,然后就开始怀疑人生。这种情况我见了太多,根源就是没在第一步把操作系统和数据库版本定下来。
先说操作系统。CentOS 7 虽然已经进入维护尾声,但存量服务器依然很多,网上的教程也大部分基于它;CentOS Stream、Rocky Linux、AlmaLinux 以及 Ubuntu 20.04/22.04、Debian 也是常见选择;如果你在用 openEuler、统信 UOS、麒麟这类国产系统,底层基本还是兼容 systemd 和 rpm/deb 包管理体系的,安装思路完全通用。我的建议是:如果你是学习或新项目,选 Ubuntu 22.04 或 Rocky Linux 9,软件源比较新,踩坑少。
再定 MySQL 版本。目前主流是 5.7 和 8.0:
- MySQL 5.7 曾是生产环境绝对主力,但官方早已停止维护,新装环境不建议再选。
- MySQL 8.0 是目前默认推荐,默认字符集是 utf8mb4、支持窗口函数和 CTE、性能和安全都有明显提升。
- 很多云厂商或者老项目里还有 Percona Server、MariaDB,兼容性大体一致,但语法细节和部分参数有差异。
需要特别提醒:8.0 的默认认证插件是 caching_sha2_password,老版本客户端和部分旧驱动可能连不上。如果你用的是比较老的应用,先确认驱动是否支持,否则就得在创建用户时指定mysql_native_password,这属于兼容性的一个关键点。
1.2 三种安装方式对比,别一上来就源码编译
Linux 下装 MySQL 常见的有三条路,我先把各自适合的场景说清楚,免得你走弯路:
- 在线仓库安装:用系统包管理器直接装官方源里的包。优点是省事,自动处理依赖,升级维护也方便;缺点是版本跟随仓库,可能不是最新版。这是我最推荐的方式,也是下文主要演示的路径。
- 二进制免安装版:官方提供
mysql-8.0.x-linux-glibc2.17-x86_64.tar.xz这样的压缩包,解压后初始化再配置目录和系统服务。适合没有外网的内网环境、需要自定义安装路径的部署,也适合想彻底搞懂 MySQL 目录结构的学习者。缺点是一切手动来,权限、目录、配置、启动脚本都要自己弄。 - 源码编译安装:从源码用 CMake 编译,耗时最长,除非你要对内核参数或功能做深度定制,否则当前阶段完全不建议。我见过不少新人折腾两天源码编译,结果败在缺依赖上,最后换回 yum 十分钟搞定,完全没必要。
一句话总结:生产环境和绝大多数学习场景,直接走官方仓库安装;想深入理解目录结构,再用二进制包自己折腾第二遍。下面我就按官方仓库安装这条路来展开。
2. 从空机器到可用的 MySQL
2.1 配置官方软件源并完成安装
在 CentOS/RHEL 系机器上,直接用系统自带的源通常是装不到 MySQL 的,因为默认源里往往只有 MariaDB。所以第一步是安装 MySQL 官方仓库的 rpm 包。
# 下载官方仓库 rpm 包 wget https://repo.mysql.com/mysql80-community-release-el7-7.noarch.rpm # 安装仓库配置,会自动写入 /etc/yum.repos.d/mysql-community.repo rpm -ivh mysql80-community-release-el7-7.noarch.rpm # 刷新缓存并安装 MySQL 服务器端 yum install -y mysql-community-server这里解释一下刚才发生了什么:rpm 包做的事情是把 MySQL 官方 yum 源写入系统,顺便导入官方 GPG 密钥。所以后面yum install的时候,系统才知道去哪里下载真正的 MySQL 软件包。
Ubuntu 这边稍微有点差异,默认apt install mysql-server装的是 Ubuntu 自己维护的 MySQL 分支,版本可能落后。想装官方版本,需要去 MySQL APT 仓库配置页面下载对应的 deb 包:
wget https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb dpkg -i mysql-apt-config_0.8.29-1_all.deb apt update apt install -y mysql-server安装完成后验证一下:
mysql --version rpm -qa | grep mysql # 查看所有相关包,Debian 系用 dpkg -l | grep mysql systemctl status mysqld这一步输出的mysql Ver 8.0.36 for Linux on x86_64就算装好了。顺带说一句,rpm -qa | grep mysql这类命令不只是装机验证用,平时排查包冲突、清理残留也经常靠它。
2.2 数据目录和系统配置:这一步别省
很多教程装完直接systemctl start mysqld就完事了,数据文件全塞在系统盘/var/lib/mysql。这在测试环境没问题,但在生产或者正经一点的项目里,我强烈建议把数据目录放到独立的数据盘上。原因很实在:业务数据增长可能很快,如果和数据目录共用系统盘,万一系统分区写满,整个服务器包括操作系统都可能卡死甚至崩溃;独立数据盘出问题时影响面也更可控,备份恢复更清晰。
我们按/data/mysql作为数据目录的规划来操作:
# 创建数据目录并设置属主属组 mkdir -p /data/mysql chown -R mysql:mysql /data/mysql chmod 750 /data/mysql然后修改主配置文件/etc/my.cnf。MySQL 的配置文件是分层读取的,/etc/my.cnf是全局生效的入口,里面也可以再!includedir引入其它目录下的配置。一个最基础但合理的配置大概是这样:
[mysqld] # 数据文件和 socket 文件的位置 datadir=/data/mysql socket=/tmp/mysql.sock # 日志文件 log-error=/var/log/mysqld.log pid-file=/var/run/mysqld/mysqld.pid # 端口和监听地址,默认监听本地 port=3306 bind-address=0.0.0.0 # 字符集 character-set-server=utf8mb4 collation-server=utf8mb4_general_ci这里多说一句字符集。MySQL 8.0 默认已经是 utf8mb4,但 5.7 或一些系统默认可能还是 latin1,如果你不在建库建表时显式指定,后面出现中文乱码几乎无可避免。utf8mb4是真正的四字节 UTF-8,能存储 emoji 和生僻字,和utf8这种 MySQL 里的“假 utf8”完全不是一回事。只要不是有特别老的业务兼容需求,一律用 utf8mb4。
改完配置之后,建议先不要着急启动,先在命令行执行mysqld --validate-config检查一遍语法,免得配置写错导致启动失败,然后再systemctl start mysqld。
2.3 启动、获取临时密码并完成登录验收
MySQL 8.0 在 rpm 包安装完成后,第一次启动会自动完成数据目录的初始化。所以首次启动前,只要数据目录是空的、权限正确就行,不需要像二进制包那样手动执行mysqld --initialize。
systemctl start mysqld systemctl enable mysqld # 设置开机自启启动后,初始密码会以临时密码的形式写到日志文件里:
grep 'temporary password' /var/log/mysqld.log拿到临时密码后,登录并修改 root 密码:
mysql -uroot -p # 输入临时密码进入 mysql> 提示符 ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourStrongPassword@123';8.0 默认装了 validate_password 组件,对密码强度有硬性要求,太简单的密码会直接报错。这也是为什么我在很多教程里看到有人说“为什么我执行 ALTER USER 不通过”,基本就是密码复杂度不够。如果这是纯内网测试环境,实在不想管它,可以卸载组件,但生产环境务必保留。
登录后快速跑几个命令验收:
SHOW DATABASES; SELECT VERSION(); SELECT @@datadir;能正常返回结果,说明核心环境已经通了。到这里,“装好一个 MySQL”就算完成,但离“能用得放心”还差几步。
3. 初始化配置和安全加固:别把数据库裸奔
3.1 用安全脚本把默认项收一遍
MySQL 自带一个安全初始化脚本mysql_secure_installation,我建议安装完第一时间就跑一遍。它的作用是把默认的开放项收紧,交互式回答几个问题:
mysql_secure_installation它主要做四件事:
- 移除匿名用户。默认安装后可能存在匿名账号,任何人不需要密码就能连进来,必须删掉。
- 禁止 root 远程登录。root 只保留 localhost 访问,远程连接一律用业务账号。
- 删除 test 测试库。测试库默认是所有人可访问的,留着就是一个漏洞。
- 刷新权限表,让所有修改立即生效。
实际操作时,除了确认当前 root 密码,后面的问题基本都选 Y 就行。唯一可能需要注意的是,有些版本最后会问是否强制 root 用auth_socket插件认证,这个根据你本机管理的需要来,如果希望 root 通过密码登录,就选 N。
我见过不少团队数据库裸奔很久,直到被扫描工具发现root空密码或者存在匿名账号,才急急忙忙补救。这一步花钱花时间都不多,但价值极高。
3.2 远程访问权限怎么开才稳
安装完成后,MySQL 默认只允许 localhost 连接。业务服务器和应用要访问它,就需要创建远程账号并授权。这里有个很多人容易踩的坑:直接执行UPDATE mysql.user SET host='%' WHERE user='root';,把 root 改成任何主机都能连。这么做非常危险,相当于把最高权限账号裸露到网络里,只需要一个弱密码,数据库就可能被端掉。
正确的做法是:按需创建专用账号,限制来源主机和权限范围。假设我们需要让应用服务器192.168.10.20访问数据库app_db:
-- 创建账号,只允许指定内网 IP 段连接 CREATE USER 'app_user'@'192.168.10.%' IDENTIFIED BY 'StrongPassword@2025'; -- 只授予业务需要的库和权限 GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_user'@'192.168.10.%'; -- 如果后续交给运维用于结构变更等,再单独评估是否需要 DDL 权限 FLUSH PRIVILEGES;这里的'192.168.10.%'非常关键,它限定了允许连接的来源网段。实际环境里可以根据客户端 IP 收紧到单个 IP,最小权限原则永远是数据库安全的底线。
同时,确认服务器防火墙放行了 3306 端口:
# firewalld 环境下 firewall-cmd --permanent --add-port=3306/tcp firewall-cmd --reload # 如果是 ufw ufw allow 3306/tcp这里再补一句,很多云服务器除了系统内部防火墙,还有一层安全组规则。如果你怎么配都连不上,先去控制台看看安全组有没有放行 3306。类似“为什么我本机连接正常,远程就是不通”的问题,绝大部分出在这一层。
3.3 root 账号的日常使用禁忌
root 账号的定位是管理员,平时 DBA 或运维做架构变更、配置调整时才用来登录,业务代码连接数据库绝对不能用 root。具体原因不用多解释,一旦应用侧被 SQL 注入或者配置泄露,root 权限等于把整个实例的所有库都交出去。
我一般会在生产库上建立三层账号:
- 管理员账号:仅 DBA 和运维持有,来源 IP 限制为堡垒机。
- 开发账号:有库表结构和数据读写权限,只开放给开发环境或者测试环境。
- 应用账号:生产环境供应用连接,只给业务库的增删改查权限。
另外,8.0 的密码策略可以单独配置密码过期时间,也可以要求定期更换。比如强制 90 天过期:
ALTER USER 'app_user'@'192.168.10.%' PASSWORD EXPIRE INTERVAL 90 DAY;如果业务代码里用的是固定密码,这个功能要谨慎开启,否则到期后应用直接连不上,白天现场改密是件相当折磨人的事。我个人的做法是:外网暴露的实例开启密码过期策略,纯内网实例靠网络隔离和安全组兜底,密码过期周期设置得长一些。
4. 关键参数与性能调优:按内存算出来的配置
4.1 刚上手最值得改的几个参数
数据库安装完能跑只是及格,想要稳定高效,关键参数必须根据服务器实际配置来调整。这里先说几个新手最应该关注、也最容易看到效果的参数。
innodb_buffer_pool_size是 InnoDB 的缓冲池大小,也是 MySQL 内存占用的大头,直接决定了数据读写缓存的容量。经验上设置为物理内存的 60%~75%,但要留出操作系统、MySQL 线程栈、以及各种临时结构的余量。比如 8G 内存的机器,设 5G~6G 比较合适:
innodb_buffer_pool_size = 5Gmax_connections控制最大连接数。很多人喜欢把它调到 2000、5000,觉得越多越好。其实每个连接都要占用线程栈内存,连接数太多,内存消耗会呈线性上涨,反而容易把系统压垮。一般业务场景先设 500 观察,如果运行状态里Max_used_connections长期接近上限,再逐步增加。
character-set-server这个前面提过,统一字符集为 utf8mb4,避免应用层乱码和排序规则不一致的问题。
default-time-zone在跨时区场景下必须显式设置。比如国内服务器统一设置:
default-time-zone = '+08:00'sql_mode决定了 SQL 的严格程度。8.0 默认是一套比较严格的模式,比如ONLY_FULL_GROUP_BY会禁止 select 列表里出现没有参与 group by 的字段。老项目迁移过来时经常会因为这个问题报错,我的建议是:新项目保持默认,老项目如果实在改不动 SQL,再在确认风险后调整。
给出一个按内存估算的参考表:
| 服务器内存 | innodb_buffer_pool_size | max_connections | 适用场景 |
|---|---|---|---|
| 4G | 2G-3G | 300-500 | 小型业务、学习环境 |
| 8G | 5G-6G | 500-800 | 常规业务 |
| 16G | 10G-12G | 800-1200 | 业务增长期 |
| 32G | 20G-24G | 1000-2000 | 中大规模业务 |
这些不是绝对标准,而是起点值,真正的数值要靠观察和压测来校准。
4.2 日志和 binlog:出事的时候才知道重要
MySQL 的日志体系有 error log、slow query log、general log、binlog 等,其中 binlog 和慢查询日志是日常运维最常用的两个。
binlog(二进制日志)记录了所有数据变更操作,是主从复制和数据恢复的核心。如果需要做主从复制,或者将来要从某个时间点恢复数据,binlog 必须开启:
[mysqld] server-id = 1 log-bin = /data/mysql/binlog/mysql-bin binlog_format = ROW expire_logs_days = 15 max_binlog_size = 512M其中binlog_format=ROW是现在推荐的格式,记录的是行级变更,相比 statement 格式,对数据一致性更友好,虽然产生的日志量会大一些,但出问题的概率低很多。
慢查询日志是排查慢 SQL 的核心依据:
slow_query_log = 1 slow_query_log_file = /data/mysql/mysql-slow.log long_query_time = 1long_query_time设为 1 秒,意味着执行超过 1 秒的 SQL 都会被记录下来。新项目起步阶段,慢日志的量一般不大,等业务量上来再调大阈值。有了慢日志,配合EXPLAIN分析 SQL 执行计划,就能定位绝大多数性能问题。
4.3 查看配置是否生效
改动/etc/my.cnf之后必须重启 mysqld 才能生效,这一点很多人会忘。重启前可以先验证参数是否正确:
mysqld --validate-config systemctl restart mysqld进到 MySQL 里确认:
SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW VARIABLES LIKE 'max_connections'; SHOW VARIABLES LIKE 'character_set_server'; SHOW VARIABLES LIKE 'binlog_format';顺便补充一个知识:SET GLOBAL可以动态修改部分参数,但只对当前运行实例有效,重启后又会变成配置文件里的值。所以线上做参数调整时,要同时确认“动态修改”和“写入配置文件”两步都做了,否则重启完参数又变回去,容易造成误判。
5. 备份和恢复:这条底线必须守住
5.1 逻辑备份:mysqldump 的正确用法
数据库这东西,跑着的时候大家都不觉得备份重要,真出故障开始丢数据的时候,才满世界找备份。给 MySQL 做备份,最基础、最通用的工具是mysqldump,它生成的是逻辑 SQL 文件,跨版本跨平台迁移都方便。
一个适合 InnoDB 业务的备份命令模板:
mysqldump -u备份用户 -p --single-transaction --set-gtid-purged=OFF --default-character-set=utf8mb4 app_db > /backup/app_db_$(date +%Y%m%d_%H%M%S).sql这里有两个关键参数要特别说明:
--single-transaction:对 InnoDB 表做一致性快照备份,备份过程中不会锁表,线上业务基本无感。注意这个参数只对 InnoDB 有效,MyISAM 表还是会锁表。--set-gtid-purged=OFF:MySQL 8.0 默认开启了 GTID 模式,如果备份文件里带上 GTID 信息,恢复到某些目标库时可能因为 GTID 冲突而失败,所以备份时显式关掉它。
恢复就很简单:
mysql -u用户 -p app_db < /backup/app_db_20250601_020000.sql恢复前要保证目标库已经创建好,并且该用户对目标库有相应权限。如果是全实例备份,恢复时用mysql -uroot -p < 全量.sql即可。
5.2 定时自动备份:crontab + 脚本
手敲备份命令一次两次可以,长期靠人肉不现实。把备份做成自动定时任务,才是正经运维的做法。我常用的备份脚本思路如下:
#!/bin/bash BACKUP_DIR="/backup/mysql" DB_HOST="127.0.0.1" DB_USER="backup_user" DB_PASS="BackupPass@123" DB_NAME="app_db" DATE=$(date +%Y%m%d_%H%M%S) mysqldump -h$DB_HOST -u$DB_USER -p$DB_PASS --single-transaction --set-gtid-purged=OFF --default-character-set=utf8mb4 $DB_NAME | gzip > $BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz # 删除 7 天前的备份文件 find $BACKUP_DIR -name "*.sql.gz" -type f -mtime +7 -exec rm -f {} \;然后写入 crontab:
crontab -e # 每天凌晨 2 点执行备份脚本,日志输出到统一文件 0 2 * * * /opt/scripts/mysql_backup.sh >> /var/log/mysql_backup.log 2>&1这里我要多说一句:备份策略必须包含恢复演练。很多团队备份脚本写了三年,从来没验证过备份文件能不能用,直到机房故障才发现备份的 SQL 文件早就因为磁盘空间不足写到一半就断了。我的习惯是每季度至少抽一个备份文件,在测试机完整跑一次恢复,验证备份数据的可用性。这一点比把备份脚本写得花里胡哨重要得多。
5.3 物理备份和云备份的补充思路
mysqldump是逻辑备份,适合中小规模数据量(几十 GB 以内)。如果数据量大到上百 GB,逻辑备份速度会成为瓶颈,这时候要考虑物理备份方案,比如 Percona XtraBackup。它直接复制 InnoDB 数据文件,备份和恢复速度都快很多,但操作复杂度也更高,不建议新手一上来就用。
如果用的是云厂商的 MySQL 服务,云平台本身会提供自动快照和归档功能,建议至少保留一份异地的自动备份副本。如果是自建机房或自建服务器,可以把备份文件用rsync同步到另一台机器,避免“服务器整台挂了,备份也在同一台机器上”的尴尬局面。
6. 故障排查实录:把这些坑趟平了
6.1 启动失败、临时密码遇不到
第一次启动 MySQL,最常见的问题就是日志报错。排查顺序很重要,我先看systemctl status mysqld,然后再看/var/log/mysqld.log的尾部:
systemctl status mysqld tail -100 /var/log/mysqld.log常见原因无非这几类:
- 数据目录权限不对。如果你把
datadir改成了/data/mysql,但目录属主不是mysql,启动一定会报权限错误。解决办法:chown -R mysql:mysql /data/mysql。 - 磁盘满了。别笑,这个问题出现的频率相当高。用
df -h查磁盘剩余,df -i查 inode 是否耗尽。 pid-file指向的目录不存在。MySQL 启动时如果没有权限在其中创建 pid 文件,也会直接退出,确保/var/run/mysqld目录存在且属主是 mysql。- SELinux 拦截。CentOS 系如果开了 SELinux,且你把数据目录放到了非默认位置,SELinux 会阻止 MySQL 读写
/data/mysql。要么调整上下文semanage fcontext,要么先临时setenforce 0验证,生产环境建议正确配置上下文而不是直接关掉 SELinux。
遇到“找不到临时密码”的情况,先确认是不是数据目录本来就已经初始化过,比如你之前手动执行过mysqld --initialize,那密码就在当时输出的日志里。实在找不到,最简单的方式是重置 root 密码(见下一小节)。
6.2 忘记 root 密码怎么救
忘记 root 密码是 DBA 的经典场景之一。MySQL 8.0 下的标准救援思路是:以跳过权限表的方式启动,登录后用 SQL 改密码,然后再恢复正常启动。
操作步骤如下:
# 1. 编辑 /etc/my.cnf,在 [mysqld] 段末尾加一行 # skip-grant-tables systemctl restart mysqld # 2. 此时不需要密码就能进入 mysql -uroot mysql> FLUSH PRIVILEGES; mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewStrongPassword@123'; mysql> FLUSH PRIVILEGES; mysql> EXIT; # 3. 注释掉配置文件里的 skip-grant-tables,然后重启 systemctl restart mysqld有一个细节必须强调:不要开着skip-grant-tables长时间运行数据库,因为此时任何客户端都能无密码连接上来,权限认证完全失效。所以这个操作只用于紧急救援,操作完成后立即去掉配置并重启。
顺带提另一个问题:如果忘了的是业务账号密码,不需要重启数据库,root 登录后直接执行:
ALTER USER 'app_user'@'192.168.10.%' IDENTIFIED BY 'NewPassword@123';权限立刻生效,已有的连接不会受影响,新连接使用新密码即可。
6.3 3306 端口连不上,从哪里查起
“我能登录本机,但远程怎么都连不上”是运维群里反复出现的问题。排查思路我建议按下面顺序来:
- 先确认 MySQL 进程在监听:
ss -lntp | grep 3306。如果只监听了127.0.0.1,说明bind-address没配置好,在/etc/my.cnf里设置为0.0.0.0后重启。 - 再确认防火墙放行:
firewall-cmd --list-ports或者iptables -L -n。 - 如果用了云服务器,最后检查安全组是否放行 3306。
- 最后再确认账号和权限:
SELECT user, host FROM mysql.user;以及SHOW GRANTS FOR 'app_user'@'192.168.10.%';
远程登录常见的报错对照:
| 错误码 | 含义 | 常见原因 |
|---|---|---|
| 2003 | 无法连接到 MySQL 服务器 | 网络不通、防火墙拦截、进程未监听 |
| 1045 | 访问被拒绝 | 账号密码错误、账号 host 不匹配 |
| 1130 | 主机不允许连接 | 账号没有对应来源主机的授权 |
如果你创建了'app_user'@'192.168.10.%',但是客户端 IP 是192.168.20.50,那就会报 1130。解决办法就是按实际客户端 IP 段去授权,不要图省事直接用%。
6.4 字符集乱码问题
中文乱码问题在 Linux 环境里几乎人人都遇到过。前面提过配置文件里要设置character-set-server=utf8mb4,但只改这个还不够。
检查当前字符集:
SHOW VARIABLES LIKE 'character_set%';按照标准,以下几项都应该是utf8mb4:character_set_server、character_set_database、character_set_connection、character_set_client、character_set_results。连接层和应用层也要保证一致。比如 Java 连接串要显式加characterEncoding=utf8mb4,命令行客户端连接时可以先执行:
SET NAMES utf8mb4;建库建表时最好也显式指定:
CREATE DATABASE app_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;如果你已经确认服务端、连接层都没问题,但页面上还是乱码,那就要检查应用代码从请求到存储全链路是否统一用了 UTF-8。比如后端读取请求参数时用了ISO-8859-1做了一次错误的解码,这种问题和数据库本身没关系,但排查起来比数据库层面更隐蔽。我的经验是从上到下、从接入层到存储层一层层检查字节流向,用HEX()函数看库里存的实际字节,通常很快就能定位。
6.5 数据盘空间和 inode 耗尽
还有一类不太显眼但杀伤力极大的故障:服务器数据盘没有彻底满,但 inode 耗尽。通俗讲,磁盘容量就好比书架的层板面积,inode 就是书架上的格子编号。面积够了但编号用完了,书也放不进去。mysqld 写数据、写 binlog、写慢日志,全部需要文件系统分配 inode。
排查命令:
df -h # 看容量 df -i # 看 inode如果 inode 使用率 100%,即使磁盘剩余容量很大,MySQL 也会频繁报“No space left on device”。这种情况一般是因为某些目录下生成了海量小文件,比如/tmp下的 session、缓存文件,或者备份脚本没做清理把目录塞满了。解决思路是找出小文件最多的目录,清理掉无用的文件,并且设置好周期清理任务。
写在最后
装好一个 MySQL 从时间成本上说并不高,但把环境规划好、安全做扎实、备份落地并验证,才是真正拉开差距的地方。我个人在实际操作中的体会是:多数线上故障不是发生在技术难点上,而是发生在那些“以为没问题”的细节里,比如数据目录和系统盘混放、root 密码太弱、备份从来没恢复演练过、字符集没统一。如果这篇文章能帮你把其中一两个坑提前填掉,那就很值了。最后再分享一个小技巧:新服务器第一次环境准备时,把整套安装和初始化步骤整理成脚本,下次部署新环境直接执行,十分钟就能生成一个生产级可投入使用的 MySQL 实例,这也算是运维生涯里最物超所值的一次时间投资了。