MySQL索引优化需结合Go应用层:在database/sql或gorm中定位索引问题,验证EXPLAIN结果,确保WHERE条件匹配联合索引顺序,避免LIKE全模糊查询,精简SELECT字段,并理解Preload与Joins的SQL生成差异。

MySQL 索引优化是 Golang 后端面试里最常被问、也最容易答偏的点——不是不会写 SQL,而是没把 Go 应用层和数据库层的耦合关系说清楚。面试官真正想听的,是你怎么在 database/sql 或 gorm 场景下识别、定位、验证索引问题。
为什么 EXPLAIN 在 Go 项目里不能只看一次就完事
很多候选人现场手写 EXPLAIN SELECT * FROM users WHERE name = ?,然后背出“type=ref、key=user_name_idx”就算结束。但真实项目里,这个查询可能跑在 sql.DB.QueryRow 里,参数来自 HTTP query string,而 name 字段没加 INDEX 或用了 LIKE '%xxx',导致全表扫描。更隐蔽的是:Go 层用了 Scan 读取大量字段,但实际只用其中 2 个,却没加 SELECT id, name 投影优化。
- 必须确认查询是否真走索引:在 Go 服务日志里开启
log.SetOutput(os.Stdout)+sql.Open("mysql", "...&interpolateParams=true"),把拼出的实际 SQL 拿去 MySQL 执行EXPLAIN - 注意
WHERE条件顺序和联合索引字段顺序是否匹配(INDEX(a,b,c)能加速WHERE a=1 AND b=2,但对WHERE b=2无效) - 如果用
gorm.Model(&User{}).Where("name LIKE ?", "%"+q+"%").Find(&users),基本等于放弃索引,得改成前缀匹配WHERE name LIKE ?+ 参数q + "%",并确保字段有索引
gorm 的 Preload 和 Joins 性能差异在哪
面试官常问“一对多查用户+订单,怎么避免 N+1?”——光答“用 Preload”不够,得说清底层行为。Preload 默认发两条 SQL(先查用户,再用 IN (id1,id2,...) 查订单),而 Joins 是单条 JOIN 查询。当用户数少(SELECT u.id,u.name,o.id,o.status 明显更省带宽和内存。
- Preload 的
IN子句有长度限制:MySQL 默认max_allowed_packet限制它一次最多塞几千个 ID,超了会报ERROR 1390 (HY000) - Joins 容易误写成笛卡尔积:比如
Joins("JOIN orders ON users.id = orders.user_id").Joins("JOIN products ON orders.product_id = products.id"),没加WHERE过滤时,一个用户多个订单+多个商品就会爆炸式膨胀结果集 - GORM v2 开始支持
Joins("LEFT JOIN ...").Select("users.*, COUNT(orders.id) as order_count"),但必须手动Group("users.id"),漏掉就逻辑错误
连接池设置不当比慢 SQL 更致命
很多人调优只盯着 SQL,却让 db.SetMaxOpenConns(100) 和 db.SetMaxIdleConns(10) 硬编码在 init 函数里。线上 QPS 上千时,连接池打满,新请求直接卡在 acquireConn,表现为 HTTP 503 或延迟突增,但 EXPLAIN 看所有 SQL 都很快。
立即学习“go语言免费学习笔记(深入)”;
-
SetMaxOpenConns不是越大越好:超过数据库 max_connections 会触发 MySQL 的Too many connections错误;一般设为min(100, DB_max_connections * 0.8) -
SetMaxIdleConns应 ≤SetMaxOpenConns,否则空闲连接数可能超过上限,被自动回收,造成频繁建连开销 - 必须监控
db.Stats().OpenConnections和db.Stats().Idle,用 Prometheus 抓取,告警阈值设为 OpenConnections ≥ 90% MaxOpenConns
真正难的不是写出优化语句,而是把 Go 应用的生命周期(连接池、context 超时、defer 关闭)、SQL 执行计划、MySQL 服务配置三者串起来诊断。比如看到 P99 延迟升高,要能快速判断是慢查询拖垮了连接池,还是连接池太小导致请求排队——这中间没有黑盒,只有可测、可调、可验证的链路。


















