资讯详情

资讯详情

Ubuntu下MySQL从安装到运维:破解auth_socket、配置与备份全攻略

先别急着拿 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.7Ubuntu 18.04MySQL 5.7Ubuntu 20.04MySQL 8.0.xUbuntu 22.04MySQL 8.0.xUbuntu 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.0MySQL 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、通用二进制包和 Docker2.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.cnfsocket 文件在 /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 --usermysql --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 port3306 usermysql为了让 systemd 能管理它创建一个服务文件 /etc/systemd/system/mysql-custom.service[Unit] DescriptionMySQL Community Server 5.7.44 Afternetwork.target [Service] Typesimple Usermysql Groupmysql ExecStart/usr/local/mysql/bin/mysqld --defaults-file/etc/my.cnf LimitNOFILE65535 [Install] WantedBymulti-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_PASSWORDYourPass123 \ -e MYSQL_DATABASEappdb \ -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 rootlocalhost IDENTIFIED WITH caching_sha2_password BY 你的强密码; FLUSH PRIVILEGES;执行完再退出用 mysql -uroot -p 登录测试这时候密码就真正生效了。如果客户端驱动太老、还不支持 caching_sha2_password可以暂时退回 mysql_native_password但我不建议把这种方式作为长期默认升级客户端驱动才是正经出路。另外强调一句业务代码里不要用 root 连接数据库。新建一个最小权限账号只授予业务库的增删改查权限这是我在生产环境里反复确认过的安全底线。3.2 密码策略组件validate_passwordMySQL 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-serverutf8mb4 collation-serverutf8mb4_0900_ai_ci记住一个核心逻辑utf8mb4 是 utf8 的超集emoji 表情和一些生僻字必须用 utf8mb4 才能存MySQL 里的 utf8 其实只能存最多 3 个字节的字符。时区配置也是一个容易踩的坑。如果服务器时区不是 UTC但数据库默认跟系统时区走应用层记录的 timestamp 容易出现偏差。我习惯在配置文件里显式指定[mysqld] default-time-zone08: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 先更新 id1 的行再更新 id2 的行事务 B 正好相反先更新 id2再更新 id1。两个事务互相等对方释放锁就死锁了。排查时用到这几张表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): Cant 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 rootlocalhost 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-modeDISABLED但这不是长期方案该配好的证书权限还是要配好。5.4 Docker MySQL 启动失败的快速判断Docker 方式跑 MySQL启动失败的判断路径和本机安装完全不同。先用docker logs mysql8看容器日志。如果日志里有类似[ERROR] Cant 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-purgedOFF \ --all-databases backup_$(date %F).sql这里有两个参数一定要理解--single-transaction 用于 InnoDB 表它通过快照读取保证备份期间数据一致性不锁表避免影响线上写入。--set-gtid-purgedOFF 是为了让备份文件在非 GTID 环境或要导入到其他实例时不带上原实例的 GTID 信息。很多人备份文件导不回去就是这个参数没处理好。单库备份更简单mysqldump -uroot -p --single-transaction dbname dbname.sql6.2 binlog 开启与增量恢复全量备份只能覆盖到备份时间点以前的数据。要让数据库恢复到精确的时间点必须依赖 binlog。在配置文件里加上[mysqld] server-id1 log-binmysql-bin binlog_formatROW8.0 默认 binlog_format 就是 ROW5.7 则需要确认。expire_logs_days 这种清理参数在 8.0 里已经改成了 binlog_expire_logs_seconds注意区分。当需要恢复到某个时间点时mysqlbinlog \ --start-datetime2024-01-01 00:00:00 \ --stop-datetime2024-01-01 10:30:00 \ mysql-bin.000001 | mysql -uroot -p增量恢复的前提是 binlog 文件还在所以备份脚本里一定要包含 binlog 文件的归档并且配置好自动清理策略避免磁盘被日志撑爆。6.3 做一次真实的恢复演练很多团队“有备份”不等于“能恢复”。建议大家在新环境里做一次完整的演练流程是这样的全量备份当前库得到 backup.sql继续写入一批测试数据删除或误操作部分表先用全量备份恢复再用 binlog 把备份时间点到误操作之前的数据补回来校验表数量和关键行数确认和预期一致。演练的目的不是过程本身而是要验证备份文件可用、binlog 权限正确、恢复步骤没有遗漏。实际事故发生时每一分钟都是钱演练做熟了你才能冷静应对。最后分享一点个人习惯每次改 MySQL 配置文件之前先备份一份原始文件重启服务之前先用 mysqld --validate-config 或者 mysqld --verbose --help 做一次配置校验。这个简单的动作替我挡过至少三次因为配置写错导致 MySQL 起不来的事故。Ubuntu 下用 MySQL最大的门槛不是命令记不住而是对发行版默认行为和认证机制的“反直觉”没有提前心里有数。把这篇文章里的路径走通一遍你应该就能很从容地在这套组合上开展实际业务了。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →