「 做 MySQL 运维,有些坑年年有人踩:utf8 存 emoji 乱码、连接池半夜被断、升级后 GROUP BY 报错、varchar 不加引号索引就飞、大表改字段锁全表、主从延迟查半天。这篇是我们一线带学员、做交付时反复踩的 6 个场景,命令都能直接复制,建议先收一份。 」
一、utf8 不是真 UTF-8,存 emoji/生僻字直接乱码

▲ 架构图:分层架构 + 6 坑落点(对照正文)
MySQL 里的 utf8 是 utf8mb3 的别名,最多只占 3 字节,存不了 4 字节字符(emoji、部分生僻汉字、补充平面字符)。表现就是插入报错或查出来全是问号。
|--------------|-------------------------------------------------------------------------------|
| 场景 | 命令 |
| 看当前字符集 | SHOW VARIABLES LIKE 'character_set_%'; |
| 建库用真 UTF-8 | CREATE DATABASE db1 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; |
| 已有表转 utf8mb4 | ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; |
坑点 :光库表改成 utf8mb4 还不够。连接层 character_set_client / connection / results 三个也得一致,否则客户端按 latin1 传、库里按 utf8mb4 存照样乱。统一用 SET NAMES utf8mb4,或在 JDBC 连接串加 ?useUnicode=true&characterEncoding=utf8mb4(MySQL 8 Connector/J 认 utf8mb4)。
二、wait_timeout 默认 8 小时,连接池半夜被断
默认 wait_timeout=28800(8 小时),interactive_timeout 同值。应用连接池走的是非交互连接 ,认 wait_timeout。长连接空闲超过这个时间,服务端直接把连接掐了,应用再拿来用就报 MySQL server has gone away / Lost connection。
|------------|-------------------------------------|
| 场景 | 命令 |
| 看超时设置 | SHOW VARIABLES LIKE 'wait_timeout'; |
| 调大(动态,全局) | SET GLOBAL wait_timeout=28800; |
| JDBC 加心跳校验 | 连接串配 autoReconnect=true 不推荐,见坑点 |
坑点 :别只靠 JDBC 的 autoReconnect=true。它会在连接断开后静默重连,但事务中途重连有数据不一致风险,新版 Connector/J 也趋向弃用。正确做法是在连接池开探活(testWhileIdle / validationQuery),或把 wait_timeout 调得大于应用最长空闲窗口。也别无脑设成 0(无限)------空闲连接堆积会拖垮 max_connections。
三、ONLY_FULL_GROUP_BY 一升级就报 SQL 错
5.7 起 sql_mode 默认带 ONLY_FULL_GROUP_BY。GROUP BY 没列全、或 SELECT 了既非聚合又非分组列的字段,直接报错:Expression #1 of SELECT list is not in GROUP BY clause。
|--------------|---------------------------------------------------------------------------|
| 场景 | 命令 |
| 看当前 sql_mode | SELECT @@sql_mode; |
| 临时关(仅当前会话) | SET SESSION sql_mode='STRICT_TRANS_TABLES'; |
| 持久关(改配置) | my.cnf: mysqld sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,...' 后重启 |
坑点 :SET SESSION 只对当前连接有效,换连接又报。要持久就改 my.cnf 再重启。更稳的做法是把 SELECT 的列都放进 GROUP BY,或改用聚合函数------别图省事直接把 ONLY_FULL_GROUP_BY 摘掉,那只是把语义错误藏起来。这是老库迁到 5.7+/8.0 最常见的坑之一。
四、隐式类型转换:varchar 字段 `= 123` 索引直接失效
字符串列跟数字比,MySQL 会按"把字符串转成数字"做隐式转换,结果等价于对列套了个函数,优化器只能放弃索引走全表扫。
|--------------|-----------------------------------------------------------|
| 场景 | 命令 |
| 错误写法(数字比字符串) | SELECT * FROM t_order WHERE order_no = 20240001; |
| 正确写法(类型对齐) | SELECT * FROM t_order WHERE order_no = '20240001'; |
| 验证是否走索引 | EXPLAIN SELECT * FROM t_order WHERE order_no = 20240001; |
坑点 :EXPLAIN 里 type=ALL、key=NULL 就是没走索引。同样会失效的还有:前导 % 模糊(LIKE '%abc')、WHERE DATE(create_time)=... 这种函数包列。记住一条------列上别套函数、比较两边类型对齐,索引才肯干活。隐式转换是慢查询第一大来源。
五、大表加字段/改类型:Online DDL 也不是万能
5.6+ 支持 InPLACE / Online DDL,但仍有 metadata lock(MDL)风险:DDL 前后要拿 MDL 锁,只要当时有长事务或没提交的事务,DDL 就会被卡住,甚至反过来阻塞后面所有读写。
|-----------|----------------------------------------------------------------|
| 场景 | 命令 |
| 尽量在线加字段 | ALTER TABLE t ADD COLUMN c1 INT, ALGORITHM=INPLACE, LOCK=NONE; |
| 看 MDL 锁等待 | SELECT * FROM performance_schema.metadata_locks; |
| 看长事务 | SELECT * FROM information_schema.innodb_trx; |
坑点 :即便 ALGORITHM=INPLACE,开始那一刻还是要短暂拿 MDL 排他锁。若这时有长事务没提交,DDL 会一直 Waiting for table metadata lock,并把新请求也堵住。上线改表前先清掉长事务、挑低峰。8.0 对"加列"支持 INSTANT 更快,但改类型、改字符集仍要重建表。大表 DDL 强烈建议用 gh-ost / pt-osc 这类在线变更工具,别直接 ALTER 锁全表。
六、主从延迟:单线程回放跟不上写
主库并行写,老版本从库靠单线程 SQL 线程回放,写入一高延迟就涨。现象是 Seconds_Behind_Master 变大,从库读到的数据"旧"。
|-------------|-------------------------------------------------------------------------------------|
| 场景 | 命令 |
| 看延迟 | SHOW SLAVE STATUS\G → 看 Seconds_Behind_Master(8.0 也可写 SHOW REPLICA STATUS) |
| 开并行复制 | STOP SLAVE; SET GLOBAL slave_parallel_workers=8; START SLAVE;(8.0 默认 LOGICAL_CLOCK) |
| 看 binlog 格式 | SHOW VARIABLES LIKE 'binlog_format'; 推荐 ROW |
坑点 :Seconds_Behind_Master 只是秒级粗粒度,网络抖一下、一条大事务(一次 UPDATE 十万行)都会瞬间拉大。根源常是"大事务"------主库一个事务,从库要整体回放完。拆小事务 + 开并行复制 + binlog 用 ROW,延迟基本可控。

▲ 一图速查:核心要点汇总(建议收藏)
小结
MySQL 运维记住 6 条:utf8→utf8mb4,连接层三件套也要对齐 ;wait_timeout 配连接池探活别只靠 autoReconnect ;GROUP BY 列写全,别摘 ONLY_FULL_GROUP_BY ;类型对齐防隐式转换 ;大表 DDL 用 pt-osc/gh-ost,先清长事务 ;主从延迟靠并行复制 + 拆小事务。命令都在上表,出问题照着敲基本能救。
以上整理自云贝教育一线运维笔记。想系统把 MySQL 吃透,顺着官方文档 + 认证体系(如 MySQL OCP)走一遍会稳很多,觉得有用顺手收藏。