Linux下搭建MySQL,从零到能扛业务的完整实录
在Linux上把MySQL跑起来,这件事看起来特别“入门”,随便一搜教程一大把。但我这些年帮人排查过不少数据库问题,发现很多故障的根源,恰恰是最初安装配置时埋下的雷——目录规划不合理、字符集没设对、密码策略没理清、防火墙/SELinux没放行,甚至有人装完直接抡起root账号当业务账号用。这篇文章就围绕“linux搭建mysql”这件事,把从版本选择、环境准备、正式安装、初始化配置、远程访问到日常备份的完整链路捋一遍,最后附上我实际踩过的高频问题排查记录。
这篇内容适合谁?刚接触Linux想搭一套开发库的新人,准备在服务器上部署生产环境MySQL的运维/后端同学,以及想系统了解MySQL安装规范和注意细节的开发者。我尽量把“为什么这么做”也讲清楚,不是单纯丢给你一串命令。
1. 动手之前,先把这几个关键问题想清楚
1.1 该选哪个Linux发行版和MySQL版本
先说结论:我推荐 Rocky Linux 8 / 9 或 CentOS 7(如果你是老机器),MySQL 版本直接上 8.0,别再新装 5.7。为什么?
MySQL 5.7 其实在2023年10月已经结束官方支持(EOL),这意味着之后发现的漏洞不会再打补丁,合规审计都过不了。对于新项目,直接上8.0是天经地义的选择。MySQL 8.0 相比5.7在很多方面是实打实的提升:默认字符集就是utf8mb4,不用再手动改来改去;支持窗口函数和公用表表达式(CTE),写复杂统计SQL方便很多;优化器更强了,很多老SQL在8.0上跑得更快;还有数据字典统一管理,不再散落一堆.frm文件。
操作系统层面,Ubuntu和Debian也是常见选择,但国内企业里CentOS系(包括Rocky Linux、AlmaLinux)的使用率还是最高的,文档多、踩坑案例多、遇到问题好搜。所以我下面的实操以Rocky Linux 8为例,CentOS 7的差异我会标注出来。
1.2 机器配置和目录规划
很多新手装MySQL根本不关心目录规划,默认装完datadir在/var/lib/mysql,日志在/var/log/mysqld.log,一切看起来都正常。但等数据库跑起来,数据量涨了,系统盘满了,那时候再迁移数据目录就很被动了。
我给一个比较稳的目录规划方案:
- 数据目录:/data/mysql(独立挂载一块数据盘,至少50G起,SSD最好)
- 日志目录:/data/mysql/logs(可以和datadir放一起,也可以单独分)
- 备份目录:/data/backup/mysql(尽量和数据库不在同一块磁盘上)
解释一下为什么坚持独立数据盘:数据库是典型的随机读写负载,如果和系统共用一块盘,操作系统的日志写入、软件的临时文件都可能跟数据库抢IO。更关键的是,系统盘万一出问题,重装系统时数据盘可以拔下来挂到别的机器上抢救数据。我见过太多把数据库放在/根分区,磁盘写满后MySQL直接拒写、甚至表损坏的案例,都是血的教训。
如果没有独立数据盘,至少也要预留足够的根分区空间,并且定期监控磁盘使用率。
1.3 安装方式:源码编译、二进制包、还是yum仓库
MySQL在Linux上的安装方式主要有三种,我直接说结论,各有利弊但要分场景选:
- 源码编译安装:适合有特殊定制需求的场景,比如要打特殊补丁、要自定义编译参数。但对绝大多数人来说是三重折磨:耗时长(一台服务器编译2小时起步)、依赖多(缺哪个库都得装)、升级麻烦。新人不建议碰。
- 二进制免编译包:把解压后的目录放到指定位置,然后初始化数据目录就行。优点是灵活,可以随意指定安装路径;缺点是一切都要手动配,环境变量、服务脚本、开机自启全要靠自己搞,容易遗漏。
- yum/dnf仓库安装:最推荐。官方仓库会处理好依赖关系和服务脚本,后续小版本升级一条yum update就完事,配置也集中在/etc/my.cnf里,好找好改。
2. 服务器初始化和环境准备
2.1 系统基础配置:主机名、时间同步、SELinux
新装的Linux服务器,建议先做三件基础事,不然后面排查问题会很痛苦。
第一,设置主机名。执行hostnamectl set-hostname mysql-server,然后编辑/etc/hosts,把主机名和IP对应起来。这一步很关键,因为MySQL在启动时会反解主机名,如果你没配好hosts,启动日志里会出现各种奇怪的DNS解析错误。
第二,确认时间同步。数据库对时间同步敏感度极高,如果服务器时间漂移,binlog里的时间戳、慢查询日志、事务时间全会乱套,甚至会让你在排查问题时得出完全错误的结论。用chrony做时间同步:
yum install -y chrony systemctl enable --now chronyd chronyc sources -v看到输出的时间源状态是^* 就说明同步正常。
第三,SELinux的处理策略。这个在4.3节会详细讲,这里先记住一点:不要无脑setenforce 0关掉,而是按需放行MySQL需要的端口和资源。
2.2 配置MySQL官方yum源
不要从第三方源安装MySQL,版本混乱且质量没保障。一定要用MySQL官方源。
Rocky Linux 8 / CentOS 8 使用如下命令:
dnf install -y https://dev.mysql.com/get/mysql80-community-release-el8-9.noarch.rpmCentOS 7则用el7的包:
yum install -y https://dev.mysql.com/get/mysql80-community-release-el7-7.noarch.rpm装完以后验证一下仓库是否生效:
dnf repolist | grep mysql正常会看到mysql-community-server、mysql-community-client等仓库。如果服务器无法访问外网,可以从能联网的机器上下载rpm包和所有依赖,然后内网离线安装。离线安装的依赖列表比较长,建议用yumdownloader带--resolve参数把依赖一次性拉下来。
2.3 卸载系统自带的MariaDB
很多Linux发行版默认带了MariaDB(一套MySQL的社区分支)。如果端口被它占用,你启动MySQL时会报Port 3306 is already in use的错误,或者安装时提示包冲突。
检查是否装了MariaDB:
rpm -qa | grep mariadb有输出的话直接卸载:
yum remove -y mariadb*卸载后顺手检查一下3306端口是否被占用:
ss -lntp | grep 3306没输出就说明端口空闲。如果被其他进程占用,确认不是重要服务后手动处理。
3. 正式安装与初始化
3.1 安装MySQL Community Server
仓库配好了,装起来就是一条命令的事:
dnf install -y mysql-community-server这个过程会自动安装mysql-community-client、mysql-community-libs等依赖,大约需要下载几百兆的包,取决于网速。
安装完成以后,先别急着启动,做两件事。
第一,检查一下MySQL的版本:
mysql --version第二,规划数据目录。如果按前面说的用/data/mysql作为数据目录,需要先准备出来:
mkdir -p /data/mysql mkdir -p /data/mysql/logs mkdir -p /data/backup/mysql然后对数据目录做SELinux上下文标记,这个很多教程都会漏掉:
semanage fcontext -a -t mysqld_db_t "/data/mysql(/.*)?" restorecon -Rv /data/mysql不做这一步的话,SELinux开启时MySQL无法写数据目录,启动会直接失败,报错信息还是Permission denied,特别迷惑。
3.2 启动服务与临时密码处理
准备好目录后,修改/etc/my.cnf中的datadir指向,然后启动:
[mysqld] datadir=/data/mysql socket=/var/lib/mysql/mysql.sock启动服务:
systemctl start mysqld systemctl enable mysqld第一次启动时,MySQL会自动完成数据目录的初始化工作,并且为root用户生成一个临时密码,记录在日志文件里:
grep 'temporary password' /var/log/mysqld.log输出的格式类似:A temporary password is generated for root@localhost: xxxxxxxx。注意,这个密码只在初始状态下有效,登录后必须修改。
注意这个日志位置,如果你修改了log-error的配置,要看对应的文件,别找错地方。
3.3 mysql_secure_installation安全初始化
拿到临时密码后,登录MySQL:
mysql -uroot -p登录成功后第一步就是改密码。MySQL 8.0默认密码策略是中等强度(validate_password.policy为MEDIUM),要求密码至少8位、包含大小写字母、数字和特殊字符。
ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourStrongP@ssw0rd';如果只是本地开发环境,想用简单密码,也可以临时调低策略:
SET GLOBAL validate_password.policy = LOW; SET GLOBAL validate_password.length = 6; ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';但这只是图省事的做法,生产环境千万别这么干。
接下来运行官方自带的加固脚本:
mysql_secure_installation这个脚本是交互式的,会依次问你:
- 是否设置密码验证插件(VALIDATE PASSWORD COMPONENT)——按需选择,生产建议yes
- 是否修改root密码——刚才已经改过可以选No
- 是否删除匿名用户——必须Yes
- 是否禁止root远程登录——生产环境建议Yes
- 是否删除test测试数据库——必须Yes
- 是否重新加载权限表——Yes
这几项全选Yes就对了,尤其是删除匿名用户和test库,这是很多安全扫描工具必查的项,不处理的话机器一上公网就可能被扫到漏洞。
4. 核心配置与远程访问
4.1 看懂my.cnf关键参数
MySQL装好只是第一步,真正决定性能的是/etc/my.cnf里的参数配置。很多新手直接用默认配置跑生产,这其实挺危险的——MySQL默认配置是为了“能跑起来”而不是“跑得好”。
我把最核心的几个参数列出来,并说清楚每个参数的意义:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| port | 3306 | MySQL监听端口,生产环境可以改掉降低被扫描概率 |
| bind-address | 0.0.0.0或内网IP | 默认只监听127.0.0.1,想远程访问必须改 |
| character-set-server | utf8mb4 | 数据库默认字符集,强烈建议utf8mb4 |
| collation-server | utf8mb4_unicode_ci | 排序规则,和字符集配套 |
| max_connections | 500-2000 | 最大连接数,取决于并发量和机器配置 |
| innodb_buffer_pool_size | 物理内存的50%-70% | InnoDB缓冲池大小,这是MySQL性能的头号因素 |
| slow_query_log | ON | 开启慢查询日志,定位性能瓶颈 |
| long_query_time | 2 | 超过2秒的SQL记录到慢查询日志 |
| log_bin | mysql-bin | 二进制日志,用于数据恢复和主从复制 |
一个比较通用的配置文件模板如下:
[mysqld] port=3306 bind-address=0.0.0.0 datadir=/data/mysql socket=/var/lib/mysql/mysql.sock character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci max_connections=800 max_connect_errors=1000 innodb_buffer_pool_size=4G innodb_log_file_size=512M innodb_flush_log_at_trx_commit=1 slow_query_log=ON slow_query_log_file=/data/mysql/logs/slow.log long_query_time=2 log_error=/var/log/mysqld.log注意修改配置后需要重启MySQL生效:
systemctl restart mysqldinnodb_buffer_pool_size是InnoDB引擎用来缓存数据页和索引的内存区域。这个值设太小,数据频繁读磁盘,性能急剧下降;设太大又可能导致内存不足触发系统OOM。经验值是物理内存的50%到70%。比如机器有8G内存,设4G到5G都合理,但前提是机器上没跑其他吃内存的服务。
我见过一个经典错误:机器8G内存,只给buffer pool设了128M的默认值,结果跑一个不算大的报表查询都要好几秒。调整到4G后,同样的查询秒出。这个参数就是MySQL性能里最立竿见影的一个。
4.2 创建业务账号和授权
root账号权限太大,不应该让业务代码直连。正确的做法是创建独立的应用账号,只授予必要权限。
假设我们要创建一个名为myapp的账号,可以访问mydb库所有表:
CREATE USER 'myapp'@'%' IDENTIFIED BY 'AppP@ssw0rd2024'; GRANT ALL PRIVILEGES ON mydb.* TO 'myapp'@'%'; FLUSH PRIVILEGES;这里的'myapp'@'%'表示允许从任何主机连接,mydb.*表示只对mydb库有权限。如果业务只跑在某一台应用服务器上,更安全的是限制IP来源:
CREATE USER 'myapp'@'192.168.1.100' IDENTIFIED BY 'AppP@ssw0rd2024';MySQL 8.0的账号授权要注意,8.0不再支持GRANT语句中隐式创建用户,必须先CREATE USER再GRANT,这点和5.7及更早版本有区别。
另外,MySQL 8.0默认的认证插件是caching_sha2_password,有些老版本客户端工具(比如很早的Navicat版本)连接时会报“Authentication plugin 'caching_sha2_password' cannot be loaded”错误。解决办法可以创建用户时指定mysql_native_password:
CREATE USER 'myapp'@'%' IDENTIFIED WITH mysql_native_password BY 'AppP@ssw0rd2024';但我个人建议是升级客户端工具,而不是降级认证方式——native_password在8.4中已经被标记废弃,长远看还是要向caching_sha2_password迁移。
4.3 防火墙和SELinux,两个最容易被忽略的拦路虎
很多人在服务器本地连MySQL都没问题,一用Navicat或者别的客户端远程连接就连不上,此时90%的可能是防火墙没放行3306端口,剩下10%是SELinux拦截。
先看防火墙。使用firewalld的机器执行:
firewall-cmd --permanent --add-port=3306/tcp firewall-cmd --reload确认端口已经放行:
firewall-cmd --list-ports如果是纯内网环境,可以限定来源IP,只允许内网网段访问:
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.0.0/16" port protocol="tcp" port="3306" accept'再讲SELinux。你可以先用getenforce查看当前状态。如果是Enforcing,只放行防火墙还不够,MySQL的网络连接和文件访问都可能被SELinux拦。前面2.1节说了不要一刀切关SELinux,正确做法是只放行MySQL需要的权限:
setsebool -P mysqld_connect_any 1这个布尔值控制MySQL能否建立网络连接。另外还需要在SELinux里放行MySQL监听的端口:
semanage port -a -t mysqld_port_t -p tcp 3306前面在数据目录上做的restorecon也是SELinux相关操作。把这几步做完,SELinux基本上不会成为MySQL的阻碍,同时系统的整体安全策略还是保持开启的。
5. 日常管理与运维要点
5.1 服务管理与状态检查
安装配置完以后,日常打交道最多的是这些命令:
systemctl status mysqld # 查看服务状态 systemctl start mysqld # 启动 systemctl stop mysqld # 停止 systemctl restart mysqld # 重启 systemctl enable mysqld # 设置开机自启 systemctl is-enabled mysqld # 检查是否已设置开机自启确认MySQL确实在监听端口:
ss -lntp | grep 3306看到LISTEN状态说明服务正常。再试一下能不能登录:
mysql -uroot -p -e "SELECT VERSION();"能输出版本号就说明一切正常。
5.2 慢查询日志与error log
排查线上性能问题,慢查询日志是第一个要看的。前面配置文件里已经开启了,确认一下状态:
SHOW VARIABLES LIKE 'slow_query_log%'; SHOW VARIABLES LIKE 'long_query_time%';在MySQL里也可以动态开启,不需要重启:
SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 2;但注意动态设置重启后会失效,想永久生效还是要把配置写进my.cnf。
分析慢日志,其实没必要用复杂的工具,先直接看几条:
tail -50 /data/mysql/logs/slow.log每条慢日志会记录执行时间、锁等待时间、返回行数、扫描行数以及具体的SQL语句。看到扫描行数特别大或者执行时间特别长的,就该去分析SQL了。
error log的位置在/etc/my.cnf里配置,默认是/var/log/mysqld.log。MySQL启动失败、连接异常断开、复制中断这些关键错误都会记录在这里。排查问题时第一时间打开它,比网上乱搜高效得多。
5.3 备份方案:物理备份与逻辑备份
说实话,很多小团队根本没有备份策略,或者只是偶尔手动导一下数据。但数据库一旦出问题,备份就是最后的救命稻草。我强烈建议任何环境都要配置自动备份。
最常用的两类备份方式:
逻辑备份用mysqldump,简单可靠,适合中小数据量:
mysqldump -uroot -p --single-transaction --master-data=2 --routines --triggers --events mydb > /data/backup/mysql/mydb_$(date +%F).sql参数说明:--single-transaction用于InnoDB表实现一致性快照备份,不锁表;--master-data=2会在备份文件中记录binlog文件名和位置,对后续搭建从库或做时间点恢复很有用;--routines和--triggers用来备份存储过程、触发器和事件。
恢复时执行:
mysql -uroot -p mydb < /data/backup/mysql/mydb_2025-01-01.sql物理备份推荐Percona XtraBackup,适合大数据量,备份速度快,支持增量备份。它直接拷贝数据文件,但要求版本和MySQL兼容。装好以后备份命令:
xtrabackup --backup --target-dir=/data/backup/mysql/inc1 -u root -p最后用crontab配置定时任务。比如每天凌晨2点做全量备份:
0 2 * * * mysqldump -uroot -p'password' --single-transaction --master-data=2 mydb | gzip > /data/backup/mysql/mydb_$(date +\%F).sql.gz备份这件事,最怕的是“备份了但恢复不了”。所以定期做一次恢复演练很重要,把备份文件拷到测试机上实际恢复一遍,确认备份可用。我见过不少团队,备份文件堆了一大堆,真到要恢复的时候才发现备份是坏的,那比没有备份还让人崩溃。
6. 常见问题排查实录
6.1 六类高频问题速查表
我把这些年遇到的高频问题整理成一个速查表,方便你遇到问题时快速定位:
| 现象 | 可能原因 | 排查命令 / 解决方法 |
|---|---|---|
| 启动失败,日志报Permission denied | SELinux未放行数据目录 | 执行restorecon -Rv /data/mysql,或用semanage添加上下文 |
| 本机能连,远程连接被拒 | 防火墙未放行3306;bind-address未修改 | firewall-cmd --add-port=3306/tcp;my.cnf中bind-address改为0.0.0.0 |
| 远程连接报Can't connect (10060) | 云安全组未添加入站规则 | 检查云平台安全组,放行3306端口 |
| 客户端报caching_sha2_password错误 | 客户端版本太老,不支持MySQL 8.0认证插件 | 升级数据库客户端工具;或临时改用mysql_native_password |
| 密码正确但登录报Access denied | 账号host类型不匹配 | 检查SELECT user,host FROM mysql.user;,确保登录来源在允许列表中 |
| 启动成功但连3306没反应 | 端口被占用或MySQL没监听 | ss -lntp | grep 3306,看是否被mysqld监听 |
6.2 忘记root密码怎么重置
这个场景太常见了,而且网上教程鱼龙混杂。我讲一个在MySQL 8.0下亲测有效的方法。
先停掉MySQL服务:
systemctl stop mysqld然后以跳过授权表的方式启动:
mysqld_safe --skip-grant-tables --skip-networking &注意加上--skip-networking,防止在无认证状态下被远程连接,这是个安全细节。
使用root直接登录,不需要密码:
mysql -uroot然后重新加载授权表并修改密码:
FLUSH PRIVILEGES; ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewP@ssw0rd';重置完后重启MySQL:
systemctl restart mysqld如果mysqld_safe启动方式不太好用,也可以直接修改/etc/my.cnf,在[mysqld]段下加一行skip-grant-tables,重启服务,改完密码后记得删除这一行再重启。
6.3 中文乱码问题:字符集必须用utf8mb4
MySQL中文乱码的根子基本都在字符集不一致。现在MySQL 8.0默认字符集就是utf8mb4,如果你是8.0版本且安装时没有特意改字符集,那么这个问题基本不存在。但如果你用的是5.7或者默认配置没动过,还是要检查一下:
SHOW VARIABLES LIKE 'character_set%';关键看character_set_server,如果是latin1或者utf8,就说明服务端字符集不对。修改my.cnf:
[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci同时确认客户端连接时也使用utf8mb4。在连接字符串里加上字符集参数,比如JDBC连接串:
jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8mb4顺带说一句,utf8mb4和utf8的区别在于,utf8mb4支持完整的Unicode字符集,包括emoji和一些生僻字。utf8在MySQL里最多存3字节的字符,遇到emoji这种占用4字节的就会报错。所以任何新项目,无脑用utf8mb4就对了。
7. Linux下MySQL的架构和工作原理速览
既然标题是“linux搭建mysql”,我觉得有必要顺带聊一下装好的MySQL在Linux上是怎么组织的。很多面试题也喜欢从这里切入。
MySQL从架构上可以分成三层:连接层、服务层和存储引擎层。
连接层负责处理客户端的连接请求,验证用户名密码,建立线程来处理每个连接。这也是为什么max_connections参数重要——每个连接都要占用一个线程和一部分内存。
服务层是MySQL的核心业务逻辑所在,包括SQL解析器、查询优化器、缓存等。你写的SQL到了这里会被解析成语法树,然后交给优化器生成执行计划,决定用哪个索引、按什么顺序扫描表。
存储引擎层是真正干活的。InnoDB是MySQL 8.0的默认引擎,它的核心设计包括:
- 聚簇索引:表数据本身按主键索引组织,叶子节点直接存储行数据。这意味着主键查询效率极高,但非主键索引的查询需要回表。
- 缓冲池(Buffer Pool):把磁盘上的数据页缓存在内存中,极大减少磁盘IO。这就是innodb_buffer_pool_size参数发挥作用的地方。
- redo log:事务提交时先写redo log(WAL机制),保证即使数据库崩溃,已提交的事务也可以恢复。
- undo log:用于事务回滚和MVCC多版本控制。
在Linux上,MySQL的数据文件存放在datadir目录下,InnoDB表的表结构定义和系统数据字典在MySQL 8.0中统一存储在数据字典中,业务数据则存在每个表对应的.ibd文件中。
了解这些原理有什么实际意义?比如你看到一条SQL慢,你能判断是索引问题还是buffer pool太小;比如你备份时知道InnoDB支持在线备份,配合--single-transaction能拿到一致性快照而不锁业务;比如主从复制出问题了,你知道要看binlog位置和relay log的状态。这些都不是纯理论消耗,是实打实能用于日常运维判断的知识储备。
回到安装这件事上,装好MySQL只是万里长征第一步。把基础打牢——版本选对、目录合理、字符集正确、权限清晰、备份到位——数据库才能真正稳定可靠地为你服务。我在实际项目中还养成了一个习惯:装完MySQL后,把建库建账号的SQL、my.cnf配置、初始化过程全部整理成一份文档放进项目的wiki里。这样半年后新同事接手,或者哪天上生产要再排一套环境,直接照着文档操作,不用重新摸索踩坑。
最后再分享一个小技巧:配置改完别急着大改特改,一次只改一到两个参数,观察一段时间的运行情况,再决定下一步。数据库优化是渐进式的,不是一蹴而就的事。