☰
Ubuntu下MySQL从安装到运维:破解auth_socket、配置与备份全攻略
2026/10/6 13:20:28 网站建设 项目流程

先别急着拿 Windows 里的习惯来操作 Ubuntu 上的 MySQL。我身边好几个朋友第一次在 Ubuntu 上用 apt 装完 mysql-server,紧接着就被 mysql -uroot -p 的登录方式搞懵了:密码明明输对了,界面却无情地给出 Access denied。更离谱的是,有人不小心敲了 sudo mysql,竟然不需要密码直接进去了。这不是灵异事件,而是 Ubuntu 发行版把 root 的认证方式默认成了 auth_socket 插件。今天这篇就围绕 Ubuntu 下的 MySQL,把安装选型、初始化配置、日常运维、故障排查和备份恢复这些事一条龙讲清楚,全部是基于我在真实环境里反复踩过、也验证过的实操路径。

如果你是后端开发、运维工程师,或者正在自学 Linux 加数据库组合的学生,这篇可以直接当一份避坑手册来用。我会尽量把每个操作背后的“为什么”也说清楚,不光是给命令。

1. 动手之前先弄懂三件事:auth_socket、版本选择和部署路径

1.1 反直觉的 auth_socket:为什么 root“没有密码”

Ubuntu 从 16.04 之后,apt 源里的 MySQL 做了一个和其他平台很不一样的设计:root 用户默认启用的认证插件是 auth_socket,而不是 mysql_native_password 或 caching_sha2_password。

auth_socket 的工作机制很简单:它不校验密码,而是看当前发起连接的 Linux 系统用户是谁。如果你当前是系统的 root 用户,或者通过 sudo 提升了权限,那么 MySQL 的 root 用户就可以直接放行,连密码都不用输入。打开终端输入:

sudo mysql

你会发现自己直接进到了 MySQL 的命令行里,没有任何密码提示。而换成:

mysql -uroot -p

无论你怎么输入密码,大概率都会收到 Access denied。因为 auth_socket 根本不读密码字段,它认的是操作系统用户身份。

可以用下面这个 SQL 查看 root 当前的认证插件:

SELECT user, host, plugin FROM mysql.user WHERE user = 'root';

执行结果里插件栏如果写着 auth_socket,那就说明你正处在这个“免密但不让你用密码登录”的特殊状态。

这个设计本身是出于安全考虑的:Ubuntu 希望本地 root 权限和数据库 root 权限绑定,避免出现那种设置了弱密码、结果被局域网扫描爆破的情况。但对于需要从远程客户端连接数据库的开发场景,这个默认配置就非常不友好了。所以后面我会专门讲怎么切换认证方式。

1.2 同是 Ubuntu,不同版本默认的 MySQL 版本不同

这是一个特别容易被忽略的细节。很多人搜索“Ubuntu 安装 MySQL”,搜到的教程可能是三四年前写的,里面说的是安装 MySQL 5.7,但在当前的 Ubuntu 上执行 apt install mysql-server,装出来的是 8.0 版本。

不同的 Ubuntu 发行版,软件源里的 mysql-server 版本大概是这样:

Ubuntu 版本默认 MySQL 版本
Ubuntu 16.04MySQL 5.7
Ubuntu 18.04MySQL 5.7
Ubuntu 20.04MySQL 8.0.x
Ubuntu 22.04MySQL 8.0.x
Ubuntu 24.04MySQL 8.0.x(更新补丁版本)

在安装之前,建议先看一眼当前源里的候选版本:

apt-cache policy mysql-server

输出里的 Candidate 字段就是 apt 会安装的版本。如果你需要 MySQL 5.7,注意 Ubuntu 20.04 及之后的官方源里已经没有 5.7 了,只能通过通用二进制包自行安装,这个我在下面会详细演示。

1.3 版本选型:5.7 还是 8.0

MySQL 5.7 已经在 2023 年底结束了官方维护,5.7.44 是这一系列的最终版本。除非你的业务系统有非常硬性的兼容要求,否则新部署的话我强烈建议直接用 8.0。

8.0 相比 5.7 有几个关键变化:

  • 默认字符集是 utf8mb4,不再需要手动指定;
  • 默认认证插件是 caching_sha2_password,安全性更高,但老客户端需要适配;
  • 查询缓存被彻底移除;
  • 窗口函数、公共表表达式这些 SQL 特性在 8.0 里才比较完善。

如果因为历史项目原因必须用 5.7,比如某些老框架的 ORM 对 8.0 的认证方式支持不好,那就别死磕 apt 了,直接上通用二进制包,自己掌控一切。

2. 实测三种安装方式:apt、通用二进制包和 Docker

2.1 apt 方式:最快,但要注意初始化环节

apt 方式是最省事的,适合绝大多数日常开发场景:

sudo apt update sudo apt install mysql-server

装完之后检查一下服务状态:

sudo systemctl status mysql

看到 active (running) 基本就成了。这一阶段 Ubuntu 的默认配置里,数据目录在 /var/lib/mysql,配置文件在 /etc/mysql/mysql.conf.d/mysqld.cnf,socket 文件在 /var/run/mysqld/mysqld.sock,服务由 systemd 管理。

接下来很多人会执行 mysql_secure_installation 做安全初始化,交互过程中会问是否设置 root 密码、是否删除匿名用户等。这里有个真实的坑:在 auth_socket 为默认认证方式的条件下,即使你在这一步设置了 root 密码,MySQL 仍然会继续使用 auth_socket 插件,密码并没有真正生效。我当时就是在这里误以为密码已设置成功,后面用密码登录被拒了半小时。

所以正确的理解是:mysql_secure_installation 主要做的是清理匿名账户、移除测试库、限制远程 root 访问这些事,root 密码要真正生效,需要手动切换认证插件。具体方法在第 3 节。

2.2 二进制包方式:想要 5.7 就用 tar.gz 自己装

如果你需要 MySQL 5.7.44,那么最可控的方式是下载官方通用二进制包。官网的 MySQL Archives 页面里可以找到 mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz。

安装步骤大致如下:

cd /usr/local sudo tar zxvf mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz sudo mv mysql-5.7.44-linux-glibc2.12-x86_64 mysql

然后创建 mysql 系统用户,并准备好数据目录:

sudo groupadd mysql sudo useradd -r -g mysql -s /bin/false mysql sudo mkdir -p /usr/local/mysql/data /usr/local/mysql/tmp sudo chown -R mysql:mysql /usr/local/mysql

初始化数据目录。5.7 时代我习惯用 --initialize-insecure,这样 root 初始密码为空,进入后可以自己设置:

sudo /usr/local/mysql/bin/mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data

接着写一份最简配置文件 /etc/my.cnf:

[mysqld] basedir=/usr/local/mysql datadir=/usr/local/mysql/data socket=/tmp/mysql.sock port=3306 user=mysql

为了让 systemd 能管理它,创建一个服务文件 /etc/systemd/system/mysql-custom.service:

[Unit] Description=MySQL Community Server 5.7.44 After=network.target [Service] Type=simple User=mysql Group=mysql ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf LimitNOFILE=65535 [Install] WantedBy=multi-user.target

然后重新加载并启动:

sudo systemctl daemon-reload sudo systemctl start mysql-custom sudo systemctl enable mysql-custom

这里额外提醒一句:5.7.44 虽然还在下载页面上,但它已经是功能冻结状态,不会再有任何安全补丁。把它当成“遗留系统的兼容兜底”可以,别当成“长期稳定方案”,迁移到 8.0 的规划要先做起来。

2.3 Docker 方式:隔离度高,但最容易因细节失败

Docker 跑 MySQL 最大优势是环境完全不污染宿主机,也能方便地跑多个版本。一条最基本的命令是这样的:

docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=YourPass@123 \ -e MYSQL_DATABASE=appdb \ -v /opt/mysql/data:/var/lib/mysql \ mysql:8.0

这条命令本身不复杂,但在实际环境里失败率不低,常见原因我列一下:

  • 宿主机 3306 端口已经被本机 MySQL 占用,容器启动直接报端口冲突。这种情况把宿主机端口换个高位映射就行,比如 -p 33066:3306。
  • 挂载目录权限问题。容器里的 mysql 用户 uid 是 999,如果宿主机 /opt/mysql/data 的属主不是 uid 999,容器会因为无法写入而退出。建议提前执行 chown -R 999:999 /opt/mysql/data。
  • 数据目录已经有旧数据。如果 /var/lib/mysql 挂载目录里有之前初始化过的文件,MYSQL_ROOT_PASSWORD 这类环境变量不会再生效,此时要用旧密码登录再改密码。
  • 客户端认证方式不兼容。8.0 镜像默认创建的 root 用的是 caching_sha2_password,较老的客户端连不上。

虽然说 Docker 方式对数据生命周期管理和备份策略的要求更高,但对于做本地联调、试用不同版本、搭建临时测试环境来说,确实是最干净的方案。

3. 装完之后的安全基线:把 root 认证、密码强度和字符集一次配好

3.1 root 认证方式切换:从 auth_socket 到 caching_sha2_password

无论你用的是 apt 还是二进制包,最终面向应用连接的数据库账号都不能依赖 auth_socket。切换 root 或业务账号的认证方式,SQL 写法如下:

ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY '你的强密码'; FLUSH PRIVILEGES;

执行完再退出,用 mysql -uroot -p 登录测试,这时候密码就真正生效了。如果客户端驱动太老、还不支持 caching_sha2_password,可以暂时退回 mysql_native_password,但我不建议把这种方式作为长期默认,升级客户端驱动才是正经出路。

另外强调一句:业务代码里不要用 root 连接数据库。新建一个最小权限账号,只授予业务库的增删改查权限,这是我在生产环境里反复确认过的安全底线。

3.2 密码策略组件:validate_password

MySQL 8.0 里,密码强度校验使用的是 validate_password 组件。安装和查看方式:

INSTALL COMPONENT 'file://component_validate_password'; SHOW VARIABLES LIKE 'validate_password%';

默认情况下,MySQL 8.0 的密码策略是 MEDIUM:密码长度至少 8 位,必须包含数字、大小写字母和符号。如果你觉得这个策略太严格,可以调整:

SET GLOBAL validate_password.policy = 'LOW'; SET GLOBAL validate_password.length = 6;

但需要注意,这些是运行时设置,重启后失效。想让策略永久生效,需要写进配置文件里。对于生产环境,我建议保持 MEDIUM 以上策略,没必要为了图省事降低密码门槛。

3.3 字符集和时区:utf8mb4 应该成为默认

在 8.0 里,默认字符集已经是 utf8mb4,不需要额外配置。但如果是 5.7 或者从旧版本迁移过来的库,最好检查一下:

SHOW VARIABLES LIKE 'character_set%';

如果字符集还是 latin1,就需要在配置文件里修改:

[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_0900_ai_ci

记住一个核心逻辑:utf8mb4 是 utf8 的超集,emoji 表情和一些生僻字必须用 utf8mb4 才能存,MySQL 里的 utf8 其实只能存最多 3 个字节的字符。

时区配置也是一个容易踩的坑。如果服务器时区不是 UTC,但数据库默认跟系统时区走,应用层记录的 timestamp 容易出现偏差。我习惯在配置文件里显式指定:

[mysqld] default-time-zone='+08:00'

写成固定偏移量,而不是 SYSTEM,这样即使服务器改时区,数据库行为依然稳定。

4. 日常操作实战:排序优化、锁排查与存储过程

4.1 ORDER BY 背后的排序陷阱

MySQL 里排序相关的热搜词一直不少,很多人以为 ORDER BY 就是简单加个字段,实际上数据量一大,排序会变成性能瓶颈。

最常见的性能信号是 EXPLAIN 结果里的 Extra 列出现 Using filesort。这就意味着 MySQL 没有利用索引顺序,而是把结果集拉出来在内存或磁盘上额外排了一遍。

比如有一张订单表:

CREATE TABLE orders ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, amount DECIMAL(10,2) NOT NULL, created_at DATETIME NOT NULL, KEY idx_created_amount (created_at DESC, amount ASC) );

8.0 支持降序索引,可以把 ORDER BY created_at DESC, amount ASC 这个查询在索引层面直接满足,避免 filesort。而在 5.7 及更早版本里,降序排序通常只能靠反向扫描实现,覆盖不了所有场景。

所以优化排序的核心思路不是“堆索引”,而是让查询的 WHERE、JOIN、ORDER BY 全部落到同一个索引的字段顺序里。覆盖索引解决排序问题,是性价比最高的手段。

4.2 锁的分类与死锁定位

InnoDB 的锁体系是事务并发的核心,也是面试和实际问题排查的高频区。我习惯从三个维度去理解它:

分类维度锁类型说明
模式共享锁 S允许其他事务读,但阻止写
模式排他锁 X允许持有者读写,阻止其他事务任何操作
算法记录锁 Record Lock锁定单条记录
算法间隙锁 Gap Lock锁定记录之间的区间,防止幻读
算法临键锁 Next-Key Lock记录锁加间隙锁组合,InnoDB 在可重复读下的默认方案
粒度表锁 / 行锁 / 元数据锁分别对应 DDL、DML、结构变更场景

实际开发中最常见的是死锁问题。举一个典型场景:事务 A 先更新 id=1 的行,再更新 id=2 的行;事务 B 正好相反,先更新 id=2,再更新 id=1。两个事务互相等对方释放锁,就死锁了。

排查时用到这几张表:

SELECT * FROM performance_schema.data_locks\G; SELECT * FROM performance_schema.data_lock_waits\G; SELECT * FROM sys.innodb_lock_waits\G;

sys.innodb_lock_waits 是最直观的,它会告诉你当前哪个事务在等待哪个事务的锁。定位到之后,处理方式无非就是:干掉阻塞事务、优化业务里多行更新的顺序、让所有事务都按同一个顺序拿锁。

4.3 一个“压箱底”的存储过程例子

存储过程这种功能,现在很多团队用得少了,但某些场景下依然很顺手。比如按月统计订单数据,我写过一个很简单但实用的存储过程:

DELIMITER // CREATE PROCEDURE sp_monthly_report(IN y INT, IN m INT) BEGIN SELECT DATE_FORMAT(created_at, '%Y-%m') AS month, COUNT(*) AS order_count, SUM(amount) AS total_amount FROM orders WHERE created_at >= DATE(CONCAT(y, '-', m, '-01')) AND created_at < DATE(CONCAT(y, '-', m, '-01')) + INTERVAL 1 MONTH GROUP BY month; END // DELIMITER ;

调用方式:

CALL sp_monthly_report(2024, 6);

这种把固定统计逻辑固化在数据库里的做法,适合报表口径稳定、不想在多个语言环境里重复实现的场景。但反过来,如果统计逻辑经常调整、或者涉及复杂的业务权限过滤,那就应该放应用层而不是数据库层。存储过程不是不能用,而是要克制地用。

4.4 慢查询日志与 Explain 解析

排查性能问题第一步永远是开慢查询日志:

SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 2; SHOW VARIABLES LIKE 'slow_query_log_file';

线上环境我会把 long_query_time 设置成 1 秒,配合 pt-query-digest 这类工具定期分析慢日志,找出真正需要优化的 SQL,而不是凭感觉去优化。

拿到一条慢 SQL 后,EXPLAIN 是必须做的动作:

EXPLAIN SELECT user_id, SUM(amount) FROM orders WHERE created_at >= '2024-01-01' GROUP BY user_id\G;

重点看几个字段:

  • type:从 system、const、eq_ref、ref、range 到 index、ALL,越往右性能越差。出现 ALL 说明在扫全表。
  • key:实际用到的索引。如果为 NULL,说明没有可用索引。
  • rows:预估扫描行数,这个数字和实际性能高度相关。
  • Extra:出现 Using filesort 或 Using temporary,就要考虑索引设计问题。

5. Ubuntu 下 MySQL 高频问题排查实录

5.1 socket 连接被拒与服务启动失败

这个报错太经典了:

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)

遇到先不要慌,按下面顺序排查:

sudo systemctl status mysql sudo journalctl -u mysql -n 50

如果服务没启动,journalctl 会给出具体原因。常见的是数据目录权限不对、磁盘满了、配置文件语法错误。检查 socket 路径是否被改过,可以执行:

sudo mysqld --verbose --help | grep socket

还有一点容易被忽略:/var/run/mysqld 目录必须存在且属主是 mysql 用户。如果缺了这个目录,MySQL 启动时会无法创建 socket 文件,导致连接被拒。

5.2 root 密码明明对了却登录失败

这个场景我在开头已经描述了。如果你确认密码没错,但 mysql -uroot -p 就是进不去,而 sudo mysql 能进,那 100% 是 auth_socket 在起作用。

解决办法很简单:

sudo mysql

进入后执行:

ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY '新密码'; FLUSH PRIVILEGES;

然后退出,重新用密码登录。这条命令执行完之后,root 的密码认证才真正接管。

5.3 客户端 SSL 连接报错的两种场景

关于 mysql ssl 连接错误,我在实际环境里遇到过两种典型情况。

第一种是客户端版本太老,报错为:

Authentication plugin 'caching_sha2_password' cannot be loaded

这其实和 SSL 本身无关,是认证插件不匹配。老客户端只认 mysql_native_password,而 8.0 默认创建的用户是 caching_sha2_password。解决办法要么升级客户端驱动,要么对特定账号做认证方式兼容:

ALTER USER 'user'@'%' IDENTIFIED WITH mysql_native_password BY '密码';

第二种才是真正的 SSL 问题。MySQL 8.0 默认启用 SSL,如果配置文件里指向的证书私钥权限不对,服务起来后客户端连接会报 SSL 相关错误。检查私钥文件权限,确保 mysql 用户可读。临时的绕过方式是在客户端命令里加:

mysql -uroot -p --ssl-mode=DISABLED

但这不是长期方案,该配好的证书权限还是要配好。

5.4 Docker MySQL 启动失败的快速判断

Docker 方式跑 MySQL,启动失败的判断路径和本机安装完全不同。先用:

docker logs mysql8

看容器日志。如果日志里有类似:

[ERROR] Can't read dir of '/etc/mysql/conf.d/'

说明挂载配置目录的方式有问题。先别急着加各种自定义配置,用最小参数跑通之后再逐步加环境变量和卷映射,排查效率会高很多。

如果日志里反复出现权限错误,基本可以断定是挂载目录的属主不对。执行:

sudo chown -R 999:999 /opt/mysql/data

再次启动即可。

6. 备份与恢复:mysqldump 和 binlog 双保险

6.1 mysqldump 的正确打开方式

日常备份我最常用的还是 mysqldump。一个比较稳妥的全库备份命令是:

mysqldump -uroot -p \ --single-transaction \ --set-gtid-purged=OFF \ --all-databases > backup_$(date +%F).sql

这里有两个参数一定要理解:

  • --single-transaction 用于 InnoDB 表,它通过快照读取保证备份期间数据一致性,不锁表,避免影响线上写入。
  • --set-gtid-purged=OFF 是为了让备份文件在非 GTID 环境或要导入到其他实例时,不带上原实例的 GTID 信息。很多人备份文件导不回去,就是这个参数没处理好。

单库备份更简单:

mysqldump -uroot -p --single-transaction dbname > dbname.sql

6.2 binlog 开启与增量恢复

全量备份只能覆盖到备份时间点以前的数据。要让数据库恢复到精确的时间点,必须依赖 binlog。

在配置文件里加上:

[mysqld] server-id=1 log-bin=mysql-bin binlog_format=ROW

8.0 默认 binlog_format 就是 ROW,5.7 则需要确认。expire_logs_days 这种清理参数在 8.0 里已经改成了 binlog_expire_logs_seconds,注意区分。

当需要恢复到某个时间点时:

mysqlbinlog \ --start-datetime="2024-01-01 00:00:00" \ --stop-datetime="2024-01-01 10:30:00" \ mysql-bin.000001 | mysql -uroot -p

增量恢复的前提是 binlog 文件还在,所以备份脚本里一定要包含 binlog 文件的归档,并且配置好自动清理策略,避免磁盘被日志撑爆。

6.3 做一次真实的恢复演练

很多团队“有备份”不等于“能恢复”。建议大家在新环境里做一次完整的演练,流程是这样的:

  1. 全量备份当前库,得到 backup.sql;
  2. 继续写入一批测试数据;
  3. 删除或误操作部分表;
  4. 先用全量备份恢复,再用 binlog 把备份时间点到误操作之前的数据补回来;
  5. 校验表数量和关键行数,确认和预期一致。

演练的目的不是过程本身,而是要验证备份文件可用、binlog 权限正确、恢复步骤没有遗漏。实际事故发生时每一分钟都是钱,演练做熟了你才能冷静应对。

最后分享一点个人习惯:每次改 MySQL 配置文件之前,先备份一份原始文件;重启服务之前,先用 mysqld --validate-config 或者 mysqld --verbose --help 做一次配置校验。这个简单的动作,替我挡过至少三次因为配置写错导致 MySQL 起不来的事故。Ubuntu 下用 MySQL,最大的门槛不是命令记不住,而是对发行版默认行为和认证机制的“反直觉”没有提前心里有数。把这篇文章里的路径走通一遍,你应该就能很从容地在这套组合上开展实际业务了。

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

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

立即咨询