MySQL 运维高频 6 坑:字符集、超时、SQL_MODE、隐式转换、Online DDL、主从延迟

「 做 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 配连接池探活别只靠 autoReconnectGROUP BY 列写全,别摘 ONLY_FULL_GROUP_BY类型对齐防隐式转换大表 DDL 用 pt-osc/gh-ost,先清长事务主从延迟靠并行复制 + 拆小事务。命令都在上表,出问题照着敲基本能救。

以上整理自云贝教育一线运维笔记。想系统把 MySQL 吃透,顺着官方文档 + 认证体系(如 MySQL OCP)走一遍会稳很多,觉得有用顺手收藏。

相关推荐
万象新讯1 小时前
企业海外子公司管理外包如何择优?看重数字化管理平台与全球服务覆盖,核心考核覆盖能力、自动化效率与规模化整合实力
运维·人工智能·自动化
2501_933670791 小时前
采购运营校招Excel能力清单:函数、透视表、ERP数据与SQL入门
数据库·sql·excel
captain3761 小时前
网络初 识
运维·服务器·网络
ZzzZZzzzZZZzzzz…1 小时前
K8s---网络:从 Pod 网络模型到 Calico 三种模式
运维·网络·云原生·容器·kubernetes·calico·pod网络模型
kyle~1 小时前
Linux --- epoll (I/O就绪事件通知机制)
linux·运维·服务器
一条泥憨鱼1 小时前
【从0开始学习计算机网络】| 邮件协议入门:SMTP、POP3、IMAP
linux·运维·计算机网络·github
LJianK12 小时前
服务器、节点 、 集群、分布式
运维
姚不倒2 小时前
etcd 学习系列(四):存储模型 —— WAL 和 Snapshot 是如何配合的
运维·etcd
come112343 小时前
Nginx `location` 配置说明(后端开发版)
运维·nginx