找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3301

积分

0

好友

437

主题
发表于 4 小时前 | 查看: 4| 回复: 0

MySQL 避坑指南系列·第①篇,共 4 篇。
本系列从安装配置、Docker 部署、SQL 性能到生产运维,覆盖 MySQL 全链路的高频踩坑。建议收藏整个系列,按需查阅。


很多人装完 MySQL 就直接开干,等到中文乱码、连不上远程、配置没生效这些问题出现,才意识到「安装」这步其实坑不少。本篇整理安装、字符集、配置文件、用户权限 4 个方向共 10 个高频坑,每个坑都有根因和解决方案。


环境说明

项目 版本
MySQL 5.7.x / 8.0.x(差异处单独标注)
操作系统 Ubuntu 20.04/22.04、CentOS 7/8/9

一、安装坑(3 个)

坑 1:系统源安装的 MySQL 版本太旧

现象:

sudo apt install mysql-server
mysql --version
# mysql  Ver 8.0.28,但项目要求 8.0.36

系统默认源的 MySQL 版本由发行版维护,更新严重滞后于 MySQL 官方,小版本差异有时包含重要安全补丁。

根本原因: Linux 发行版打包更新周期慢,apt/yum 默认源不是 MySQL 官方维护的。

解决方案: 使用 MySQL 官方源安装指定版本。

# Ubuntu:添加官方 APT 源
wget https://dev.mysql.com/get/mysql-apt-config_0.8.30-1_all.deb
sudo dpkg -i mysql-apt-config_0.8.30-1_all.deb
# 弹窗中选择目标版本(如 MySQL 8.0)后确认
sudo apt update && sudo apt install mysql-server

# CentOS:添加官方 YUM 源
sudo rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el7-11.noarch.rpm
sudo yum install mysql-community-server

坑 2:装完不知道 root 密码在哪

现象: 安装完成后执行 mysql -uroot -p,输什么密码都报 Access denied

根本原因: MySQL 8.0 安装时会把临时密码写到日志;Ubuntu 系的系统还可能用 auth_socket 插件认证,根本不需要密码,直接用 sudo 进。

解决方案:

# 方法一:从日志找临时密码(MySQL 8.0 通用)
sudo grep 'temporary password' /var/log/mysql/error.log

# 方法二:auth_socket 模式,直接用 sudo 进入(不加 -p)
sudo mysql

# 进入后立刻修改 root 密码
ALTER USER 'root'@'localhost'
  IDENTIFIED WITH mysql_native_password BY 'YourPassword123!';
FLUSH PRIVILEGES;

坑 3:mysql_secure_installation 跑完反而登不进去

现象: 安全初始化时密码明明设了,重新登录还是 Access denied

根本原因: MySQL 8.0 默认认证插件是 caching_sha2_password,部分客户端和驱动不兼容;也有可能是 socket 认证和密码认证混用,互相覆盖。

解决方案:

-- 检查 root 当前用的认证插件
SELECT user, host, plugin FROM mysql.user WHERE user='root';

-- 如果插件是 auth_socket 或 caching_sha2_password,改成兼容性更好的
ALTER USER 'root'@'localhost'
IDENTIFIED WITH mysql_native_password BY 'YourPassword123!';
FLUSH PRIVILEGES;

二、字符集与编码坑(2 个)

坑 4:中文写进去,查出来是问号 ???

现象: INSERT 中文成功,SELECT 出来全是 ??? 或乱码。

根本原因: MySQL 5.7 默认字符集是 latin1,根本无法存储中文。即使数据库字符集对了,连接字符集不对照样乱码——数据库、表、连接三者必须统一。

解决方案:

第一步,定位问题范围:

SHOW VARIABLES LIKE 'character%';
SHOW VARIABLES LIKE 'collation%';

第二步,修改配置文件(/etc/mysql/mysql.conf.d/mysqld.cnf/etc/my.cnf):

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

[client]
default-character-set = utf8mb4

[mysql]
default-character-set = utf8mb4

第三步,已有库和表的字符集迁移:

-- 修改数据库
ALTER DATABASE your_db
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;

-- 修改表(含所有列)
ALTER TABLE your_table
CONVERT TO CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;

⚠️ 一定要用 utf8mb4,不是 utf8 MySQL 里的 utf8 是残缺版,只支持最多 3 字节的字符,emoji(4 字节)存不进去,会直接截断或报错。


坑 5:查询不区分大小写,排序结果乱

现象: WHERE name = 'Alice' 同时查出 aliceALICEORDER BY 排序不符合预期。

根本原因: utf8mb4_general_ciutf8mb4_unicode_ci 都是大小写不敏感排序规则(ci = case insensitive)。

解决方案:

-- 需要精确区分大小写时,列级别改用 _bin 规则
ALTER TABLE users
MODIFY name VARCHAR(100)
CHARACTER SET utf8mb4
COLLATE utf8mb4_bin;

-- 或者只在查询时临时指定,不改表结构
SELECT * FROM users
WHERE name COLLATE utf8mb4_bin = 'Alice';

三、配置文件坑(3 个)

坑 6:明明改了 my.cnf,重启后配置没变化

现象: 修改了 /etc/my.cnf 并重启 MySQL,SHOW VARIABLES 里的值还是原来的。

根本原因: MySQL 按固定顺序读取多个配置文件,后读的覆盖先读的。改了一个文件,可能有另一个文件把它覆盖了。

解决方案: 先确认 MySQL 实际读的是哪个文件:

mysql --verbose --help | grep -A 1 'Default options'

# 典型输出示例:
# /etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf

然后把自定义配置统一写到优先级最高的那个文件。Ubuntu 建议写到 /etc/mysql/mysql.conf.d/mysqld.cnf,CentOS 建议写到 /etc/my.cnf


坑 7:Too many connections,连 root 都进不去

现象: 高峰期报 ERROR 1040: Too many connections,DBA 想进去排查,发现 root 账号也连不上。

根本原因: max_connections 默认 151,所有连接额度耗尽后,任何账号(包括 root)都无法新建连接。

解决方案:

[mysqld]
# 调整总连接上限
max_connections      = 500
# 为超级用户保留连接额度,管理员永远能进去
max_user_connections = 490
# 不重启临时生效
SET GLOBAL max_connections = 500;

# 查看当前连接数和连接来源
SHOW STATUS LIKE 'Threads_connected';
SHOW PROCESSLIST;

调大连接数不是根本解法。每个连接约占 1MB 内存,500 个就是 500MB。真正的解法是连接池:Java 用 HikariCP,Python 用 SQLAlchemy 连接池,Node.js 用 mysql2 pool,连接池能让几十个真实连接服务几百个并发请求。


坑 8:lower_case_table_names 迁移时表突然找不到

现象: 从 Linux 迁移到 Windows(或容器迁移),某些表查询报 Table doesn't exist,但明明存在。

根本原因: Linux 文件系统区分大小写,Ordersorders 是两张不同的表;Windows 不区分,迁移后大小写不一致的表名发生冲突或丢失。MySQL 8.0 不允许初始化完成后再修改此参数,必须在初始化前设定。

解决方案:

# 必须在 MySQL 首次初始化(数据目录为空)之前写入
[mysqld]
# 0 = 区分大小写(Linux 默认)
# 1 = 不区分,存储时转为小写(跨平台推荐)
# 2 = 不区分,存储时保留原始大小写
lower_case_table_names = 1

治本之道:从项目开始就约定表名全部小写+下划线,彻底与此参数解耦。


四、用户权限坑(2 个)

坑 9:本地能连,远程客户端连不上

现象: 本机 mysql -uroot -p 正常,Navicat / DBeaver 远程连接报 Access denied for user 'root'@'xxx.xxx.xxx.xxx'

根本原因: MySQL 默认 root 账号只允许 localhost 连接;另外 bind-address 默认绑定 127.0.0.1,根本不监听外部请求。

解决方案(开发环境):

第一步,修改 my.cnf 开启监听:

[mysqld]
# 监听所有网卡,不仅仅是本地
bind-address = 0.0.0.0

第二步,创建允许远程连接的专用账号(不要直接开放 root):

CREATE USER 'dev_user'@'%'
IDENTIFIED WITH mysql_native_password BY 'StrongPassword!';
GRANT ALL PRIVILEGES ON your_db.* TO 'dev_user'@'%';
FLUSH PRIVILEGES;

⚠️ 生产环境严禁对公网开放 3306 端口。 生产远程管理统一用 SSH 隧道:

ssh -L 3307:localhost:3306 user@your-server
# 然后本地连 127.0.0.1:3307 即可

坑 10:GRANT 执行成功,但用户还是没权限

现象: GRANT 语句没有报错,但用新账号连上去执行操作还是 Access denied

根本原因: 2 个常见原因:① 忘记 FLUSH PRIVILEGES;② mysql.user 表里同一个用户名存在多条 host 不同的记录,MySQL 用的是最精确匹配的那条,而不是你刚改的那条。

解决方案:

-- 先查清楚用户的所有记录
SELECT user, host, plugin FROM mysql.user WHERE user='your_user';

-- 检查实际生效的权限
SHOW GRANTS FOR 'your_user'@'%';

-- 刷新权限缓存
FLUSH PRIVILEGES;

快速自检清单(安装配置)

完成安装后,逐项确认:

  • ☑ 字符集已设为 utf8mb4,而不是 utf8latin1
  • ☑ 已确认 MySQL 实际读取的配置文件路径
  • lower_case_table_names 在初始化前已按需设置
  • max_connections 已根据预估并发量调整
  • ☑ 已删除匿名用户:DELETE FROM mysql.user WHERE User='';
  • ☑ 已删除测试库:DROP DATABASE IF EXISTS test;
  • ☑ 已创建应用专用账号,未直接使用 root
  • ☑ 3306 端口未对公网开放

小结

本篇 10 个坑的核心规律只有一条:MySQL 的默认配置不是为生产准备的,它追求的是兼容性和最小化,而不是安全性和性能。安装完之后,至少要把字符集、连接数、账号权限这 3 件事做对,后面才不会反复踩坑。

下一篇聚焦 Docker 部署,那是踩坑最密集的场景。欢迎到云栈社区继续探讨更多数据库实战经验。




上一篇:开源邮件服务新选择:Sendflare,一个用Rust构建的Resend替代品
下一篇:斗象开源ClawVault:透明网关与生成式策略守护AI Agent资产安全
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-25 10:34 , Processed in 0.978696 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表