RSS
菜单
全部文章快讯开发科技深度热点

第 33 章 MySQL踩坑:安装与配置篇

内容摘要

本篇是 MySQL 踩坑系列的第一篇,专门收拾"还没开始写业务,就已经被环境按在地上摩擦"的那类问题——字符集、时区、默认参数、服务起不来、权限全开。这些坑几乎每个人都在第一次部署时踩过一遍,区别只在于你是踩完记下来,还是每次重装再踩一遍。前置知识是你已经能独立把 MySQL 8.0 装上、并用客户端连上。学完之后,你应该能在一台干净机器上一次性把字符集、时区、sql_mode、连接数、Buffer Pool 配到位,并能对着一份报错的 my.cnf 说出它为什么起不来。本系列所有示例基于 MySQL 8.0 语法书写,本示例基于 MySQL 8.0 环境。

📖 本篇简介:本篇是 MySQL 踩坑系列的第一篇,专门收拾"还没开始写业务,就已经被环境按在地上摩擦"的那类问题——字符集、时区、默认参数、服务起不来、权限全开。这些坑几乎每个人都在第一次部署时踩过一遍,区别只在于你是踩完记下来,还是每次重装再踩一遍。前置知识是你已经能独立把 MySQL 8.0 装上、并用客户端连上。学完之后,你应该能在一台干净机器上一次性把字符集、时区、sql_mode、连接数、Buffer Pool 配到位,并能对着一份报错的 my.cnf 说出它为什么起不来。本系列所有示例基于 MySQL 8.0 语法书写,本示例基于 MySQL 8.0 环境。

目录

  • 坑 1:建库字符集用错导致中文乱码
  • 坑 2:时区没配对导致时间差八个小时
  • 坑 3:root 空密码直接对外暴露
  • 坑 4:配置文件路径写错服务启动失败
  • 坑 5:datadir 残留旧数据初始化错乱
  • 坑 6:lower_case_table_names 不一致表名找不到
  • 坑 7:max_connections 默认值太小被打满
  • 坑 8:innodb_buffer_pool_size 用默认值性能差
  • 坑 9:sql_mode 没开严格模式脏数据入库
  • 坑 10:binlog 不清理把磁盘写满
  • 坑 11:端口占用与防火墙导致连不上
  • 坑 12:配置文件注释符号用错
  • 速查表:安装配置坑点速查
  • 注意事项:安装后必做的检查项
  • 练习题:动手自测与参考

坑 1:建库字符集用错导致中文乱码

场景 / 错误示范:

-- 错误示范:装完 MySQL 直接建库,字符集沿用默认的 latin1
CREATE DATABASE shop;
USE shop;

-- 建表时也没指定字符集,继承了库的 latin1
CREATE TABLE goods (
  id   BIGINT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(100)
);

-- 插入中文,客户端看起来暂时正常,存进去已经是乱码
INSERT INTO goods(name) VALUES('机械键盘');

-- 再查出来,或者换到另一台客户端,就变成 ???? 或者一堆怪字符
SELECT * FROM goods;

为什么是坑:MySQL 8.0 之前默认字符集是 latin1,一个汉字要占三个字节,latin1 根本装不下,于是被截断或替换成 ?。这种乱码不是显示问题,而是数据在落盘那一刻就已经错了,事后想修要转码,非常痛苦。即使是 8.0,如果你在建库建表时手贱选了 utf8(即 utf8mb3),存 emoji 还是会报错。

正确做法:

-- 正确做法:建库直接指定 utf8mb4 + 通用排序规则
CREATE DATABASE shop
  DEFAULT CHARACTER SET utf8mb4
  DEFAULT COLLATE utf8mb4_0900_ai_ci;

USE shop;

-- 建表同样显式指定,避免依赖库的默认值
CREATE TABLE goods (
  id   BIGINT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(100)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- 现在中文和 emoji 都能正常存
INSERT INTO goods(name) VALUES('机械键盘⌨️');
SELECT * FROM goods;

-- 随时查看当前生效的字符集
SHOW VARIABLES LIKE 'character_set%';

加分点:客户端连接字符集也要对齐,在 my.cnf 里把 [client]、[mysql]、[mysqld] 都设成 utf8mb4,否则服务端存对了,客户端读出来还是乱码。utf8mb4 才是完整的 UTF-8,mysql 里的 utf8 是历史包袱,永远别用。

坑 2:时区没配对导致时间差八个小时

场景 / 错误示范:

-- 错误示范:Java 应用通过连接池写入,代码里用的是东八区时间
-- 结果数据库里存的 create_time 比业务时间少了 8 小时
SELECT NOW();                 -- 查出来是 02:30,业务明明是 10:30
SELECT @@global.time_zone;    -- 显示 SYSTEM,而系统时区又是 UTC

-- 更隐蔽的是:用 ON UPDATE CURRENT_TIMESTAMP 自动更新的字段
-- 存进去的时间和用户看到的对不上,客服一堆工单

为什么是坑:很多云镜像和 Docker 容器默认时区是 UTC,MySQL 又默认跟随系统时区。NOW()、CURRENT_TIMESTAMP 这些函数取的是数据库时区的当前时间,应用按东八区传值时就会错位。这个坑不会报错,只会在凌晨对账时让你怀疑人生。

正确做法:

-- 正确做法一:运行时把会话和全局时区都改成东八区
SET GLOBAL time_zone = '+08:00';
SET time_zone = '+08:00';

-- 再次确认
SELECT NOW();                 -- 应该和业务时间一致
SELECT @@global.time_zone;    -- +08:00

-- 做法二:写进配置文件,永久生效(my.cnf 的 [mysqld] 段)
-- default-time-zone = '+08:00'

-- 建表时显式用 DATETIME 还是 TIMESTAMP 想清楚:
-- TIMESTAMP 会随时区转换,DATETIME 存什么就是什么
CREATE TABLE orders (
  id          BIGINT PRIMARY KEY,
  create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

加分点:跨时区业务建议统一存 UTC,由应用层按用户所在时区渲染,避免数据库时区一改、历史数据全错位。容器部署时记得挂载宿主机时区或者在镜像里设 TZ 环境变量。

坑 3:root 空密码直接对外暴露

场景 / 错误示范:

-- 错误示范:图省事,root 不设密码,还允许从任意主机登录
CREATE USER 'root'@'%' IDENTIFIED BY '';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;

-- 更离谱的是装完没改密码,还把 3306 端口暴露在公网
-- 结果扫段的脚本一小时内就能把库拖走,还顺手删库留勒索信

为什么是坑:root 是超级管理员,@'%' 意味着任何 IP 都能连。空密码加公网端口等于把数据库大门敞开。MySQL 历史上有过不少弱口令批量入侵事件,运维层面的疏忽比 SQL 注入还致命。

正确做法:

-- 正确做法:root 只允许本机登录,业务另建专用账号
ALTER USER 'root'@'localhost' IDENTIFIED BY '你的强密码';

-- 业务账号按最小权限授权,只给需要的库和权限
CREATE USER 'shop_app'@'10.0.%' IDENTIFIED BY '另一个强密码';
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'shop_app'@'10.0.%';
FLUSH PRIVILEGES;

-- 查一下现在有哪些账号、能从哪连,心里有数
SELECT user, host FROM mysql.user;

-- 禁用远程 root、删除匿名账号
DELETE FROM mysql.user WHERE User='';

加分点:密码用 caching_sha2_password 强加密插件,别退回老的 mysql_native_password。生产库不要直连公网,前面挂堡垒机或内网 VIP,3306 端口用安全组限制来源 IP。

坑 4:配置文件路径写错服务启动失败

场景 / 错误示范:

# 错误示范:Windows 下把 my.ini 放错了位置,或者路径里用了反斜杠转义
[mysqld]
datadir=D:\mysql\data        # 反斜杠在 ini 里可能被转义,建议用正斜杠
basedir=D:/mysql
port=3306

# 更常见的:改了 my.cnf 但 MySQL 根本没读这个文件
# 启动时报 "Can't open the mysql.plugin table" 或者直接退出码 1
# 错误示范:凭感觉把配置文件丢在 /etc/my.cnf,其实编译时指定的是别的路径
systemctl start mysqld        # 启动失败,日志里啥有用信息都没有

为什么是坑:MySQL 启动时按一组固定顺序找配置文件,你以为改的是生效的那个,实际它读的是另一个。Windows 下还经常因为 my.ini 编码是 UTF-8 with BOM 导致第一行配置解析失败。服务起不来时,第一反应不是猜,而是去看错误日志。

正确做法:

# 正确做法:先问 MySQL 自己到底读哪个配置文件
mysqld --verbose --help | grep -A1 "Default options"
# 输出会列出它按顺序查找的所有 my.cnf / my.ini 路径

# 手动指定配置文件启动,排除"读错文件"的干扰
mysqld --defaults-file=/etc/my.cnf --user=mysql

# 看错误日志定位真因(默认在 datadir 下的 .err 文件)
tail -100 /var/lib/mysql/$(hostname).err
# 正确的 my.cnf 关键段,路径一律用正斜杠或双反斜杠
[mysqld]
basedir     = /usr/local/mysql
datadir     = /var/lib/mysql
port        = 3306
character-set-server = utf8mb4
default-time-zone    = '+08:00'

加分点:改完配置先用 mysqld --validate-config(8.0 支持)做语法校验,再重启服务,避免改一行错一个字符导致整个实例起不来。Windows 的 my.ini 务必用 ANSI 或无 BOM 的 UTF-8 保存。

坑 5:datadir 残留旧数据初始化错乱

场景 / 错误示范:

# 错误示范:之前装过 MySQL,datadir 里还有旧的 mysql 系统库
# 这次重新初始化,直接在已有数据目录上跑
mysqld --initialize --user=mysql
# 报错:[ERROR] --initialize specified but the data directory has files in it. Aborting.
-- 更隐蔽的:没初始化就直接启动,起来了但 root 密码是随机的
-- 或者起来了但表是旧版本的,升级时报 "Table upgrade required"

为什么是坑:MySQL 8.0 的数据目录有严格的版本和初始化状态要求。--initialize 要求数据目录必须为空(或不存在),否则它拒绝初始化,防止把你旧数据搞坏。很多人图省事直接删库跑路,结果删不干净,残留的 ibdata1 让新实例行为诡异。

正确做法:

# 正确做法:初始化前确认数据目录是空的
ls -la /var/lib/mysql
# 如果有残留,要么换个空目录,要么干净地清空
rm -rf /var/lib/mysql/*
mkdir -p /var/lib/mysql
chown mysql:mysql /var/lib/mysql

# 初始化,--initialize-insecure 会生成空密码(仅测试环境)
mysqld --initialize-insecure --user=mysql --datadir=/var/lib/mysql

# 生产环境用 --initialize,会在错误日志里打印一个临时 root 密码
# 然后用这个密码登录后立刻改密
grep 'temporary password' /var/lib/mysql/*.err

加分点:升级大版本时不要直接在旧 datadir 上跑新 mysqld,要用 mysqld_upgrade 或先备份。容器化部署时把 datadir 挂到独立卷,别和镜像层混在一起。

坑 6:lower_case_table_names 不一致表名找不到

场景 / 错误示范:

-- 错误示范:开发在 Windows 上(默认 lower_case_table_names=1,表名存小写)
-- 建了表 ShopOrder,部署到 Linux(默认 0,区分大小写)
-- 应用一查:Table 'shop.ShopOrder' doesn't exist

SELECT * FROM ShopOrder;   -- Windows 能跑,Linux 上报错

为什么是坑:lower_case_table_names 控制表名是否区分大小写。Windows/macOS 默认不区分(1),Linux 默认区分(0)。跨环境部署时,你以为表名是 ShopOrder,Linux 上实际存的是建表那一刻的原样。更坑的是这个参数必须在初始化时设定,事后改会导致表找不到。

正确做法:

# 正确做法:在数据库初始化之前,就把 lower_case_table_names 统一设成 1
# my.cnf 的 [mysqld] 段
[mysqld]
lower_case_table_names = 1
-- 查看当前设置(初始化后才能看,且不建议事后改)
SHOW VARIABLES LIKE 'lower_case_table_names';

-- 团队规范:所有表名一律用小写 + 下划线,如 shop_order
CREATE TABLE shop_order (
  id BIGINT PRIMARY KEY
);

加分点:MySQL 8.0 规定 lower_case_table_names 必须在初始化时确定,事后修改会导致数据字典与表名大小写不匹配而无法启动。所以跨平台项目从第一天就统一小写命名,别赌运气。

坑 7:max_connections 默认值太小被打满

场景 / 错误示范:

-- 错误示范:用默认 max_connections=151,应用一上量就报错
-- 应用日志里全是:Too many connections
SHOW VARIABLES LIKE 'max_connections';   -- 151

-- 连接池配了 200,超过 MySQL 上限,新连接全部被拒
-- 此时连 root 都可能连不上(MySQL 留了 1 个管理连接)

为什么是坑:默认 151 是给单机开发用的。Web 应用每个 Tomcat 实例开几十个连接,几个实例叠加就轻松破百。连接打满后,新请求全部排队或失败,看起来像应用崩了,实际是数据库拒连。而且连接是要占内存的,无脑调太大也会 OOM。

正确做法:

-- 正确做法:按业务规模调到合理值,比如 500~1000
SET GLOBAL max_connections = 500;

-- 写进配置永久生效
-- [mysqld]
-- max_connections = 500

-- 观察实际连接使用情况,别拍脑袋
SHOW STATUS LIKE 'Threads_connected';   -- 当前已用
SHOW STATUS LIKE 'Max_used_connections'; -- 历史峰值

-- 配套:wait_timeout 调小,让空闲连接及时释放
SET GLOBAL wait_timeout = 600;          -- 10 分钟
# my.cnf 参考配置
[mysqld]
max_connections = 500
wait_timeout    = 600
interactive_timeout = 600

加分点:连接数不是越大越好,每个连接约占几百 KB 内存,500 个连接就要预留几百 MB。高并发场景优先用连接池(如 HikariCP)复用连接,而不是无限调大 max_connections。

坑 8:innodb_buffer_pool_size 用默认值性能差

场景 / 错误示范:

-- 错误示范:一台 32G 内存的服务器,Buffer Pool 还是默认 128M
-- 结果热点数据全在磁盘上,每次查询都读 IO,QPS 上不去
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';   -- 134217728 (128M)

-- CPU 看着不高,磁盘 IO 却打满,数据库响应忽快忽慢

为什么是坑:InnoDB 的 Buffer Pool 是缓存数据页和索引页的地方,命中率直接决定性能。默认 128M 只够跑个 demo。生产环境通常把物理内存的 50%~70% 分给 Buffer Pool,让热点数据尽量常驻内存。配小了,再多 CPU 也白搭,瓶颈全在磁盘 IO。

正确做法:

-- 正确做法:假设机器 32G 内存,分给 Buffer Pool 约 18G
SET GLOBAL innodb_buffer_pool_size = 18 * 1024 * 1024 * 1024;  -- 运行时调整

-- 写进配置永久生效
-- [mysqld]
-- innodb_buffer_pool_size = 18G
-- innodb_buffer_pool_instances = 8   -- 减少锁竞争

-- 查看命中率,应该接近 99%
SHOW STATUS LIKE 'Innodb_buffer_pool_read_requests';  -- 总请求
SHOW STATUS LIKE 'Innodb_buffer_pool_reads';          -- 实际读磁盘次数
-- 命中率 = 1 - reads / read_requests
# my.cnf 参考
[mysqld]
innodb_buffer_pool_size = 18G
innodb_buffer_pool_instances = 8

加分点:Buffer Pool 不是越大越好,要给操作系统、连接、其他进程留够内存,通常不超过物理内存的 70%。8.0 支持 online 调整 Buffer Pool 大小,不用重启,但调整的步进要对齐 chunk size。

坑 9:sql_mode 没开严格模式脏数据入库

场景 / 错误示范:

-- 错误示范:sql_mode 是空的或者只有宽松设置
-- 插入一个超长字符串,数据库不报错,默默截断存进去
CREATE TABLE user (
  id   BIGINT PRIMARY KEY,
  name VARCHAR(5)
);

INSERT INTO user VALUES(1, '这是一个超长的中文名字');
-- 宽松模式下:不报错,name 被截成 5 个字符,业务完全无感
SELECT * FROM user;   -- 数据已经是错的了

为什么是坑:非严格模式下,MySQL 对越界、类型错误的字段采取"能存就存、存不下就截断"的策略,只给 warning 不报错。这种脏数据会在业务层埋下无数雷,等你发现时已经分不清哪条是对的。8.0 默认开了严格模式,但很多人为了兼容老代码又手动关掉。

正确做法:

-- 正确做法:显式开启严格模式 + 常用约束组合
SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO';

-- 查看当前模式
SELECT @@global.sql_mode;

-- 再插一次超长字段,直接报错,业务层能立刻感知
INSERT INTO user VALUES(2, '这是一个超长的中文名字');
-- ERROR 1406: Data too long for column 'name' at row 1
# my.cnf 固定下来
[mysqld]
sql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO

加分点:严格模式会让老的、不规范的 SQL 批量报错,切换前先用 SHOW WARNINGS 在测试库跑一遍回归。宁可在开发期炸出来,也别上线后悄悄存错数据。

坑 10:binlog 不清理把磁盘写满

场景 / 错误示范:

-- 错误示范:开了 binlog(主从复制必备),但没设过期时间
-- 跑了半年,/var/lib/mysql 下堆了几十 G 的 binlog.000001 ...
SHOW BINARY LOGS;                 -- 列出一大堆
SHOW VARIABLES LIKE 'expire_logs_days';   -- 0,永不过期

-- 直到磁盘写满,MySQL 直接挂掉,业务全停

为什么是坑:binlog 是追加写的,每次提交事务都在往新文件里写。不设过期时间它就永远攒着。大库一天的 binlog 可能几个 G,几周就能吃满磁盘。磁盘满了之后 MySQL 无法写入,连日志都记不下来,恢复都困难。

正确做法:

-- 正确做法:设置 binlog 自动过期,比如保留 7 天
SET GLOBAL binlog_expire_logs_seconds = 7 * 24 * 60 * 60;  -- 8.0 用秒

-- 手动清理已经备份过的 binlog(小心:正在用的别删)
PURGE BINARY LOGS BEFORE '2026-09-01 00:00:00';

-- 或者按文件名清理
PURGE BINARY LOGS TO 'binlog.000100';

-- 查一下现在占了多少
SHOW BINARY LOGS;
# my.cnf 固定
[mysqld]
log_bin = /var/lib/mysql/binlog
binlog_expire_logs_seconds = 604800   # 7 天
max_binlog_size = 1G

加分点:清理前确认从库已经把这些 binlog 同步完了,否则从库会断。主从架构下,保留时间要长于从库最长延迟 + 备份周期,别把还没同步的日志 purge 了。

坑 11:端口占用与防火墙导致连不上

场景 / 错误示范:

# 错误示范:MySQL 起来了,但客户端怎么都连不上
mysql -h 127.0.0.1 -uroot -p
# ERROR 2003 (HY000): Can't connect to MySQL server on '127.0.0.1'

# 常见原因一:3306 被别的进程占了
netstat -ano | findstr :3306     # Windows
ss -lntp | grep 3306             # Linux

# 常见原因二:MySQL 只绑了 127.0.0.1,远程连不进来
# 常见原因三:云服务器安全组没放通 3306
-- 查 MySQL 自己监听的地址
SHOW VARIABLES LIKE 'bind_address';   -- 127.0.0.1 的话只能本机连

为什么是坑:"连不上"从来不是一个原因,而是一串可能:端口被占、bind_address 只绑本地、防火墙挡了、云安全组没开、账号 host 不匹配。新手容易在其中一个点上死磕,而不去分层排查。

正确做法:

# 正确做法:分层排查
# 第一步:本机能不能连
mysql -h 127.0.0.1 -uroot -p

# 第二步:MySQL 监听在哪
ss -lntp | grep 3306
# 如果要远程连,bind_address 得改成 0.0.0.0 或具体内网 IP

# 第三步:防火墙
firewall-cmd --list-ports      # Linux firewalld
ufw status                      # Ubuntu ufw

# 第四步:云厂商安全组控制台放通 3306(或应用端口)
# my.cnf:按需绑定地址
[mysqld]
bind_address = 0.0.0.0     # 需要远程访问时;只本机用就保持 127.0.0.1
port = 3306

加分点:生产环境千万别把 3306 直接暴露公网。应用和数据库走内网,数据库账号的 host 字段精确到应用服务器 IP,比 % 安全得多。

坑 12:配置文件注释符号用错

场景 / 错误示范:

; 错误示范一:在 [mysqld] 段用 ; 注释
; 其实 my.cnf 里 ; 只在很老的版本或特定位置有效
; 现在推荐用 #,很多人混用导致这一行没被注释,反而被当成配置项

# 错误示范二:Windows 下 my.ini 里把注释写成 --
-- 这是 SQL 注释,不是 ini 注释,会导致配置解析报错

; 错误示范三:在行尾加中文注释,编码不对时把整个配置文件读乱
max_connections = 500 ; 最大连接数

为什么是坑:MySQL 配置文件是 ini 格式,注释符是 #(; 在部分解析器里也支持但不推荐)。-- 是 SQL 里的注释,写进 my.cnf 会被当成配置项名解析,直接报未知变量。编码不对时中文注释还会让整个文件读取错乱,症状非常隐蔽。

正确做法:

# 正确做法:统一用 # 注释,注释单独占一行
[mysqld]
# 字符集统一 utf8mb4
character-set-server = utf8mb4

# 最大连接数按业务规模调整
max_connections = 500

# Buffer Pool 设为物理内存的 60% 左右
innodb_buffer_pool_size = 18G

# 用严格模式
sql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
# 改完配置,先校验再重启
mysqld --defaults-file=/etc/my.cnf --validate-config
systemctl restart mysqld

加分点:配置文件尽量纯英文注释,避免编码坑。用版本管理把 my.cnf 存起来,每次变更留 diff,出问题能秒回滚。

速查表:安装配置坑点速查

| 坑号 | 坑点 | 关键参数 / 命令 | 一句话结论 |

|---|---|---|---|

| 1 | 中文乱码 | CHARACTER SET utf8mb4 | 建库建表一律 utf8mb4,别用 utf8 |

| 2 | 时间差 8 小时 | default-time-zone='+08:00' | 容器和系统时区都要配对 |

| 3 | root 空密码 | ALTER USER + 最小权限 | root 只留本机,业务账号按需授权 |

| 4 | 服务起不来 | --defaults-file + 错误日志 | 先看 .err 日志,别猜 |

| 5 | datadir 残留 | --initialize 空目录 | 数据目录必须空再初始化 |

| 6 | 表名大小写 | lower_case_table_names=1 | 初始化前定好,表名全小写 |

| 7 | 连接打满 | max_connections | 默认 151 太小,配合连接池 |

| 8 | Buffer Pool 小 | innodb_buffer_pool_size | 物理内存 50%~70% 给它 |

| 9 | 脏数据截断 | STRICT_TRANS_TABLES | 严格模式必须开 |

| 10 | binlog 写满 | binlog_expire_logs_seconds | 设过期,定期 purge |

| 11 | 连不上 | bind_address + 防火墙 | 分层排查:本机→监听→防火墙→安全组 |

| 12 | 注释符号 | ini 用 # | 别把 SQL 注释写进配置文件 |

注意事项:安装后必做的检查项

装完一套 MySQL 8.0,别急着连业务,按下面这张清单过一遍,能挡掉本篇 80% 的坑:

1. 字符集:SHOW VARIABLES LIKE 'character_set%'; 确认服务端、连接、结果集都是 utf8mb4。

2. 时区:SELECT @@global.time_zone; 不是 SYSTEM 或已是 +08:00。

3. 账号:SELECT user,host FROM mysql.user; 没有匿名账号、没有 root@'%' 空密码。

4. sql_mode:确认包含 STRICT_TRANS_TABLES。

5. Buffer Pool:innodb_buffer_pool_size 不是 128M。

6. binlog:binlog_expire_logs_seconds 不是 0。

7. 连接数:max_connections 与应用连接池上限匹配。

8. 错误日志:启动后翻一遍 .err 文件,没有 warning 和 error。

把这 8 条做成部署脚本里的自动校验,比靠人记靠谱得多。

练习题:动手自测与参考

练习 1:你在一台全新 Linux 服务器上初始化 MySQL,启动后 root 登录不上,日志里有一行 A temporary password is generated for root@localhost:,接下来该怎么做?

<details>

<summary>参考答案</summary>

用日志里打印的临时密码登录:mysql -uroot -p,然后立刻 ALTER USER 'root'@'localhost' IDENTIFIED BY '新强密码';。临时密码只能用一次,不改密无法做其他操作。

</details>

练习 2:业务反馈"时间对不上,比真实时间早了 8 小时",数据库里 NOW() 查出来是 UTC。给出一个临时修复和一个永久修复。

<details>

<summary>参考答案</summary>

临时:SET GLOBAL time_zone='+08:00'; 并让应用重连会话。永久:在 my.cnf 的 [mysqld] 段加 default-time-zone='+08:00' 后重启。同时检查容器 TZ 环境变量和宿主机时区。

</details>

练习 3:Too many connections 报错,但应用连接池配置才 100。可能是哪些原因?

<details>

<summary>参考答案</summary>

可能:MySQL max_connections 默认 151 偏小;多个应用实例叠加超过上限;wait_timeout 太大导致空闲连接不释放;有长事务占着连接不还。先 SHOW STATUS LIKE 'Threads_connected'; 看当前占用,再调大 max_connections 并调小 wait_timeout。

</details>

下一篇预告:第 34 章 MySQL踩坑:索引篇

— 全文完 —回到顶部 ↑
下载推广海报

文章推广海报

《第 33 章 MySQL踩坑:安装与配置篇》完整推广海报
DISCUSSION

文章回复

0 条公开回复
未登录回复需要审核后公开
还没有回复,欢迎参与讨论。