☰
Linux下MySQL日常运维实战:从安装、权限到备份恢复
2026/10/19 5:10:49 网站建设 项目流程

如果你已经能在Linux上把服务跑起来,那多半逃不过跟MySQL打交道。开源生态里数据库方案不少,但MySQL在中小团队和运维场景里的存在感依然很强。这篇文章不是讲高可用、不是讲分库分表,就是纯粹聊一聊我在日常运维里最常用到的那些MySQL操作,从安装到建库建表,从授权到备份,把那些容易踩坑的地方一并说清楚,适合刚接触Linux服务端、或者一直在用图形工具但想在命令行下接手数据库的朋友。

我常跟别人说,MySQL在Linux下的"简单使用"其实是相对的。装个包、启个服务不难,难的是你清楚每个操作背后改了什么、怎么看状态、出了问题从哪里查起。下面这些内容,基本覆盖了我处理大多数日常任务时用到的思路和命令,照着走一遍,你会发现自己其实也能脱离图形界面把数据库管得明明白白。

1. 安装之前,先想清楚版本与源的问题

很多人装MySQL就是一条命令敲下去,装完发现版本不对、或者是MariaDB、又或者装到一半依赖冲突。这些问题的根源,大多数出在没先想清楚"从哪个软件源装、装哪个版本"。

1.1 不同发行版的默认源差异

Linux发行版这么多,但服务端生态基本分成两大派:Debian系和RedHat系。两派的包管理工具完全不同,默认软件源里的数据库组件也差得远。

Debian系(比如Ubuntu、Debian本身)的默认源里有一个mysql-server包,但它的真实身份在不同版本里不太一样。有些版本的mysql-server是Oracle的MySQL,有些则是MariaDB的替身。Ubuntu 20.04及之后版本默认指向MySQL 8.0,这点还算明确;但Debian本身在相当长时间里默推MariaDB。所以如果你在Debian上直接apt install mysql-server,装出来的很可能是MariaDB,命令兼容,但底层行为略有差异。

RedHat系(CentOS、RHEL、Rocky Linux)就更直接了。默认源里只提供MariaDB,想装MySQL必须先去Oracle官方仓库下载对应的rpm包。CentOS 7时代大家习惯用yum install mariadb-server,CentOS 8之后虽然可以用dnf install mysql-server,但这个包指向的其实是社区分支。

所以第一条经验:装之前用包管理工具查一下源里到底有什么。比如在Ubuntu上跑apt-cache policy mysql-server,能看到候选版本和当前源里真实存在的包;在CentOS上跑yum list available | grep mysql,看清是mysql-community还是mariadb。宁可多花一分钟确认,也别装完发现完全不对再卸载。

1.2 选版本时最容易忽略的约束

如果你决定安装MySQL官方版本,那就需要先了解一下它的版本节奏。当前主线是8.0,8.0之前是5.7,这两个版本在配置参数、认证插件上有不少差异,网上教程很可能混着讲,实操时要注意区分。

选版本时首先看业务兼容性。如果线上有老应用用的还是5.6/5.7时代的SQL写法,直接上8.0可能出现兼容问题,尤其是GROUP BY的语义变化、认证插件从mysql_native_password变成caching_sha2_password,老版本驱动可能连不上。先确认驱动版本,再决定数据库大版本,这个顺序不能反。

其次看服务器资源。MySQL 8.0对内存的占用比5.7明显高,默认配置下跑起来轻轻松松吃掉几百MB。如果你的机器只有1GB内存,建议装5.7或者干脆用MariaDB。我遇到过有人在只有512MB内存的VPS上装MySQL 8.0,安装能完成,一启动就OOM,最后只能换版本。

还有一个容易忽略的:仓库优先级。同时配了EPEL源和MySQL官方源时,很容易出现依赖被覆盖的情况。RedHat系上装MySQL官方rpm时,建议先执行yum localinstall安装官方仓库rpm,再yum --enablerepo=mysql80-community install mysql-community-server,或者直接禁用EPEL源里的mysql相关组件,避免冲突。

2. 安装与初始化:从零到能登录的完整过程

选好了版本和源,接下来就是安装、初始化、登录三步曲。这一步看起来平淡,其实藏了不少坑,尤其是"首次登录密码"这件事,新手最容易卡在这里。

2.1 Debian系与RedHat系安装命令差异

Debian/Ubuntu上安装官方MySQL的路径是先装仓库配置文件。去MySQL官网下载对应系统的deb包,比如mysql-apt-config_0.8.22-1_all.deb,然后:

sudo dpkg -i mysql-apt-config_0.8.22-1_all.deb sudo apt update sudo apt install mysql-server

dpkg安装过程会弹出交互界面,问你要选哪个版本,一般默认8.0即可。后续apt install mysql-server才会真正拉取并安装。

RedHat系(以CentOS/Rocky为例)是下载官方仓库rpm:

sudo rpm -ivh mysql80-community-release-el9-1.noarch.rpm sudo dnf install mysql-community-server

命令不同,但思路一致:先让系统认识官方源,再装服务。如果你只是Ubuntu用户,也可以直接用apt install mysql-server,装的就是系统自带版本,大部分基础使用足够了。

2.2 初始化脚本与默认root密码

安装完成后,数据库还没真正准备好。RedHat系需要手动初始化:

sudo mysqld --initialize --user=mysql sudo systemctl start mysqld

初始化过程中MySQL会生成一个临时root密码,存放在错误日志里。不同发行版日志路径不一样:

发行版错误日志路径
Debian/Ubuntu/var/log/mysql/error.log
RedHat/CentOS/var/log/mysqld.log

找密码的命令:

sudo grep 'temporary password' /var/log/mysqld.log

拿到临时密码后,首次登录必须立刻修改:

mysql -uroot -p ALTER USER 'root'@'localhost' IDENTIFIED BY '你的新密码';

Debian系就省心一些。Ubuntu安装mysql-server时会自动初始化,并且root账号默认使用auth_socket认证插件,也就是说用系统root身份或sudo可以直接进MySQL,不需要密码:

sudo mysql

这种方式适合本机管理,但不适合程序远程调用,后面需要单独建业务账号。

2.3 三种登录方式的区别

登录MySQL不只是mysql -uroot -p这一种。根据连的对象不同,有三种常见方式:

  • 本地socket登录:不加-h参数,MySQL客户端默认通过socket文件与本地服务通信。socket文件一般在/var/run/mysqld/mysqld.sock。这种方式最快,绕过了TCP协议栈,也避开了网络层认证限制。
  • 本地TCP登录:显式指定-h 127.0.0.1 -P 3306,走TCP/IP。这种方式会经过网络层,如果MySQL配置了跳过网络或防火墙拦截,就登录不了。
  • 远程TCP登录:mysql -h 192.168.x.x -P 3306 -u user -p。这种方式要求MySQL允许远程连接,用户账号也绑定了对应host,比如'app'@'%'或'app'@'192.168.%.%'。

我调试本地问题时永远用socket方式,只有在测试远程连接时才加-h。一个很常见的坑:本地能登录,远程连不上。原因往往是bind-address配置或者用户host没写好。MySQL默认只监听127.0.0.1,想对外提供服务得改配置bind-address = 0.0.0.0,然后重启服务。

3. 建库建表与字符集:第一步决定后续体验

登录进去之后,很多人的第一件事是CREATE DATABASE、CREATE TABLE。这个环节的草率,会在日后数据写入时集中爆发,最常见的就是中文乱码和排序异常。

3.1 为什么字符集要在建库前定好

字符集是MySQL里最容易被忽略、改起来最麻烦的东西。MySQL的字符集分几个层级:服务器层、数据库层、表层、字段层。下层没有显式指定时,会继承上层的默认值。

现代MySQL和MariaDB都推荐使用utf8mb4,而不是老旧的utf8。原因很简单:utf8在MySQL里最多存3字节,像emoji这种4字节字符存不进去,一插入就报错;utf8mb4才是完整的Unicode支持。既然utf8mb4能全覆盖,没必要用有缺陷的utf8。

建库时明确指定字符集和排序规则:

CREATE DATABASE IF NOT EXISTS app_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

utf8mb4_unicode_ci是基于Unicode排序规则,比较准确;utf8mb4_general_ci更快但排序不够精确。日常业务建议用前者,按拼音排序的场景也能得到更符合直觉的结果。

建完数据库再改字符集是件麻烦事。虽然可以用ALTER DATABASE app_db CHARACTER SET utf8mb4,但已有的表不会跟着改,每张表、每个字段都得单独ALTER,字段多的表光改这个就能花半天。所以建库之前想清楚,尤其老项目从utf8迁移到utf8mb4时,务必先确认索引长度限制,因为utf8mb4下字符占字节更多,某些索引会超出长度上线。

3.2 字段类型的选型参考

建表时字段类型选得对,后面省很多心。几个经验:

  • 整数类型:能用INT不要用BIGINT,能用TINYINT不要用INT。别为"以后可能变大"预留过大的类型,浪费存储和内存。
  • 字符串:定长内容(MD5、手机号状态码)用CHAR,变长内容用VARCHAR。注意VARCHAR在MySQL里是变长存储,但定义长度时的单位是字符而非字节。
  • 时间类型:用DATETIME还是TIMESTAMP?如果业务跨时区,TIMESTAMP会自动转换;如果只记录本地时间,DATETIME更直观。我个人倾向于DATETIME,因为不受2038年问题影响,也方便查日志。
  • JSON字段:MySQL 5.7开始支持JSON类型,但不要滥用。能用普通字段表达的,就别塞JSON。JSON在查询、索引、统计上都不如普通字段灵活,适合存放真正结构不定的数据。

3.3 修改已有库表的注意事项

线上环境有时候必须加字段、改类型。这里最大的教训就是:大表DDL要趁低峰期做,并且提前评估锁表时间。

MySQL 5.7及之前,很多DDL操作需要拷贝整表数据,期间表被锁住,业务写入中断。5.7引入了ALGORITHM=INPLACE,8.0进一步优化,但并不是所有操作都支持在线执行。加字段、改默认值通常还好,改主键、改字符集这种操作危险程度高很多。

准备改动之前,先查一下表大小和数据量:

SELECT table_name, table_rows, data_length FROM information_schema.tables WHERE table_schema = 'app_db' AND table_name = 'users';

如果表已经很大,可以考虑用pt-online-schema-change这类工具在后台无锁执行,或者干脆在维护窗口操作,不要赌它秒完成。

4. 用户与权限:最小授权原则的落地

MySQL装好、库表建完,接下来的事情就是给业务创建账号。这里最常见的错误是图省事,直接给应用用root连接数据库,或者一股脑GRANT ALL PRIVILEGES ON *.*。看起来方便,出问题时完全没有隔离度。

4.1 创建用户的完整语法

MySQL里"创建用户"和"授权"是两步操作。MySQL 8.0里,创建用户的标准写法:

CREATE USER 'app'@'localhost' IDENTIFIED BY 'StrongPassword123'; CREATE USER 'app'@'192.168.10.%' IDENTIFIED BY 'StrongPassword123';

这里的host部分特别关键。'app'@'localhost'表示只允许从本机连接,'app'@'%'表示允许从任何地址连接,'app'@'192.168.10.%'表示允许从指定网段连接。host一定要按最小范围写,能写具体网段就不要写%。

MySQL 8.0默认的认证插件是caching_sha2_password,如果你用老版本客户端(比如PHP 7.1之前的mysqli扩展),可能连不上。真遇到这种情况,可以在创建用户时指定老插件:

CREATE USER 'app'@'localhost' IDENTIFIED WITH mysql_native_password BY 'StrongPassword123';

但我还是建议尽量升级客户端,而不是降级认证方式,因为caching_sha2_password的安全性更好。

4.2 授权粒度怎么拆分

权限设计要遵循"够用就好"。一个只读业务的账号,不给写权限;一个只操作单库的账号,不给全库权限。下面这组授权比较典型:

-- 只读账号 GRANT SELECT ON app_db.* TO 'app_read'@'localhost'; -- 常规读写账号,只操作业务库 GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app'@'192.168.10.%'; -- 需要执行DDL的运维账号 GRANT ALL PRIVILEGES ON app_db.* TO 'app_admin'@'localhost' WITH GRANT OPTION;

第二行授权是对应用程序最常用的。注意我没有授权DROP、ALTER、CREATE权限给业务账号,防止误操作导致结构变更。

如果你要在MySQL 8.0里给用户授予ALL PRIVILEGES且允许它继续授权给其他人,记得加WITH GRANT OPTION。不加的话,该账号只能使用自己的权限,不能给别的账号授权。

还有几个容易忽略的:

  • GRANT ALL不等于所有权限,它不包括授权自己的权限。
  • 存储过程/函数需要单独授权EXECUTE权限。
  • 查看所有库的元信息需要SHOW DATABASES权限。

4.3 权限查看与回收

授权之后,确认权限是否生效很重要。最常用的命令:

SHOW GRANTS FOR 'app'@'192.168.10.%';

这个输出会列出该账号所有权限,是排查"为什么这个账号没权限"的第一手段。另一个查询当前账号权限的方式:

SHOW GRANTS FOR CURRENT_USER();

回收权限用REVOKE:

REVOKE DELETE ON app_db.* FROM 'app'@'192.168.10.%';

删除用户用DROP USER,注意要连同host一起写:

DROP USER 'app_read'@'localhost';

有时候改了权限想立刻生效也不用太紧张,MySQL的权限系统在GRANT/REVOKE后会自动更新内存,不需要手动FLUSH PRIVILEGES。我自己在写脚本时偶尔会加一句FLUSH PRIVILEGES,以防有旧连接缓存,但官方文档说明这不是必需的。真正需要刷新的是直接修改mysql.user表这种不走授权命令的操作。

5. 命令行日常:批量操作、导入导出与备份恢复

日常维护里,大量工作其实是重复性的:导出数据、导入测试库、给业务账号重新授权、写个脚本定时备份。这时候,命令行方式远比图形工具灵活。

5.1 用-e参数让SQL进脚本

很多教程教你mysql -uroot -p进入交互模式,一条条敲SQL。这在临时查数据时没问题,但一旦操作变成周期性任务,就得让SQL跑在脚本里。mysql -e参数就是干这个的:

mysql -uapp -p -h127.0.0.1 -e "SELECT id, name FROM app_db.users WHERE created_at > '2024-01-01';"

在shell脚本里,可以这么写:

mysql -uapp -p'密码' -h127.0.0.1 app_db <<EOF SELECT COUNT(*) FROM users WHERE status = 1; EOF

不要把密码直接写在命令行参数里,会有被ps命令看到的风险。更稳妥的方式是使用~/.my.cnf配置文件:

[client] user=app password=你的密码 host=127.0.0.1

这样脚本里只要mysql -e "SQL"就会自动加载配置。注意~/.my.cnf的权限要设为600,否则MySQL会警告配置文件权限不安全。

5.2 导入SQL文件的三条路径

拿到一个.sql文件,怎么导入MySQL?我常用的路径有三条:

# 路径一:重定向 mysql -uapp -p app_db < /path/to/dump.sql # 路径二:在MySQL命令行里source mysql -uapp -p source /path/to/dump.sql; # 路径三:用管道(从远程服务器拉取) ssh user@remote 'mysqldump ... ' | mysql -uapp -p app_db

三条路径本质上一样,都是把SQL文本喂给MySQL执行,区别在于现场操作还是脚本化。导入大文件时,建议用路径一,因为可以方便地加--force跳过错误?不,--force不是mysql客户端的常规选项,导入大文件更实用的是先查看文件大小,如果太大就分段导入。

实际操作中,导入大SQL文件最常见的坑有两个。一是文件本身不是utf8mb4编码,导入后中文乱码,排查半天结果是该用--default-character-set=utf8mb4参数:

mysql --default-character-set=utf8mb4 -uapp -p app_db < /path/to/dump.sql

第二个是导出的SQL里带了CREATE DATABASE和USE语句,导入时会切换到指定库。如果想强制导入到当前库,可以用:

mysql -uapp -p -o app_db < /path/to/dump.sql

这个-o是--one-database的简写,表示只执行当前库相关的语句。

5.3 备份恢复的最小可靠方案

Linux下最经典的备份工具是mysqldump,也是我日常用的主力。最小的可用备份脚本:

#!/bin/bash backup_dir="/backup/mysql" date_str=$(date +%F) mkdir -p $backup_dir mysqldump -uroot -p'密码' --single-transaction --routines --triggers --events app_db | gzip > $backup_dir/app_db_$date_str.sql.gz # 删除30天前的备份 find $backup_dir -name "*.sql.gz" -mtime +30 -delete

几个参数的含义一定要理解:

  • --single-transaction:InnoDB表导出时开启一个事务做一致性快照,不锁表。MyISAM表还是会被锁,这也是我劝大家尽量都用InnoDB的原因之一。
  • --routines --triggers --events:导出存储过程、触发器、事件。不显式指定的话,这些对象默认不导出,恢复后业务报找不到存储过程才发现备份不完整。
  • --databasesvs 不带:不带表示只导出某个库的表数据,不包含建库语句;带--databases app_db会在文件里生成CREATE DATABASE和USE语句,恢复时直接执行即可。

恢复时:

gunzip < /backup/mysql/app_db_2024-01-31.sql.gz | mysql -uapp -p app_db

或者先解压再用source方式。恢复前最好先在测试库跑一遍,确认数据完整再上生产,这不是多此一举,是我亲眼见过有人把测试库备份恢复到生产库,数据全乱了的场景。

6. 排错与日志:不懂看日志就谈不上维护

用MySQL用得越多,越能体会到"排错先看日志"这句话的分量。很多问题的答案早就写在日志里了,只是你还没学会去看。

6.1 error log怎么定位

先确认错误日志在哪:

mysql -uroot -p -e "SHOW VARIABLES LIKE 'log_error';"

输出类似:

+---------------+---------------------+ | Variable_name | Value | +---------------+---------------------+ | log_error | /var/log/mysql/error.log | +---------------+---------------------+

错误日志里记录的是启动失败、连接异常、权限问题等关键信息。服务起不来时,第一件事永远是:

sudo tail -n 50 /var/log/mysql/error.log

最常见的启动失败原因之一,是错误日志里出现类似Permission denied,多半是/var/lib/mysql目录权限不对,MySQL进程无法读写数据目录。这时检查目录归属:

sudo chown -R mysql:mysql /var/lib/mysql

另一个常见原因是磁盘满了。错误日志里报No space left on device,用df -h一看确实满了。清理空间之后重启服务即可。这两个问题都属于"日志一看就明白",但如果完全不知道去翻日志,可能在配置文件和命令行之间反复试错很久。

6.2 三个常见故障的排查思路

我在实际运维中遇到最多的故障,集中在下面三类:

连接不上。报Can't connect to local MySQL server,先确认服务状态:

systemctl status mysql

服务在跑,再看socket文件是否存在。socket文件路径可以通过配置或SHOW VARIABLES LIKE 'socket'查到。本地登录连不上,但网络登录可以,大概率是socket路径不一致,客户端找的是默认路径/tmp/mysql.sock,服务端实际用的是/var/run/mysqld/mysqld.sock。解决办法是在连接时显式指定:

mysql -uroot -p -S /var/run/mysqld/mysqld.sock

密码错误或过期。报Access denied for user,先确认密码是否真的正确,同时考虑账号host是否匹配。MySQL 8.0默认启用密码过期策略,如果长时间没改密码,会报Your password has expired。这时登录后必须立刻执行:

ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码';

端口被占用。MySQL起不来,日志提示port 3306 is already in use,多半是之前残留的mysqld进程还没退干净。用:

ss -lntp | grep 3306

找到占用进程的PID,确认没有其他业务使用该端口后,再杀掉进程或调整MySQL的port配置。

6.3 慢查询日志的开关与解读

日常维护里,最有价值的日志其实是慢查询日志。它记录执行时间超过阈值的SQL,是定位性能问题的第一现场。

临时打开慢查询日志:

SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 2; SET GLOBAL slow_query_log_file = '/var/log/mysql/slow-query.log';

注意这些设置重启MySQL后失效,想永久生效,需要写进配置文件/etc/mysql/mysql.conf.d/mysqld.cnf或/etc/my.cnf:

[mysqld] slow_query_log = ON long_query_time = 2 slow_query_log_file = /var/log/mysql/slow-query.log

拿到慢查询日志后,重点关注:

  • Rows_examined远大于返回行数,说明索引没建好或者查询写得不优。
  • 频繁出现同一条SQL,说明这一条是业务热点,必须优化。

比如某天日志里看到一条2秒的查询,表只有几千行,排查后发现WHERE user_id = 123的字段没建索引。这就是慢查询日志的价值——在用户投诉之前,自己先发现问题。

结尾

写到这里,基本把Linux下MySQL从安装到日常维护的一条主线捋了一遍。我这几年最深的感受是,MySQL的"简单使用"并不在于你会多少命令,而在于你建立一个完整的操作习惯:装之前看源、建库表前定字符集、授权时想着最小化、排错时先看日志。把这些习惯刻进日常流程里,比背再多命令都管用。

最后分享一个小技巧:我每次搭好MySQL,都会先把错误日志和慢查询日志的路径写入一个备忘录,并把常用的备份脚本、授权模板统一存放在一个目录里,下次再碰到类似环境,直接复制改改就能用,省去大量重复摸索的时间。希望这篇内容能帮你在Linux下更顺手地使用MySQL,少踩几个我踩过的坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询