go-sql-driver/mysql报“dial tcp: lookup xxx: no such host”是DNS解析失败,主因是host写错(如localhost触发Unix socket、容器名未同网络)、my.cnf绑定127.0.0.1导致外部不可达。

为什么 go-sql-driver/mysql 会报 dial tcp: lookup xxx: no such host
这是最常卡住新手的第一步:连不上数据库,但错误不是密码错或端口拒接,而是 DNS 解析失败。根本原因往往是 host 参数写成了本地服务名(比如 mysql)却没跑在 Docker 网络里,或写成 localhost 却没意识到 Go 的 MySQL 驱动对 localhost 有特殊处理——它会尝试走 Unix socket 而非 TCP。
- 用
127.0.0.1代替localhost,强制走 TCP - Docker 场景下,如果应用和 MySQL 在不同容器,
host填对方容器名(如mysql-db),且确保在同一自定义 network 中 - 检查
my.cnf是否绑定了127.0.0.1而非0.0.0.0,否则外部容器无法访问
sql.Open 不等于连接成功,漏掉 db.Ping() 就上线很危险
sql.Open 只是初始化连接池配置,不校验数据库是否可达。线上服务启动后第一次 Query 才真正拨号,这时才暴露网络或认证问题,导致请求雪崩。
- 务必在
sql.Open后立即调用db.Ping(),并处理返回的 error - 加超时控制:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second),再传给db.PingContext(ctx) - 如果
Ping()失败,不要静默重试,要让进程明确失败退出,避免假启动
参数拼错一个字母,连接就静默变只读或乱码
MySQL 连接字符串里的参数(如 parseTime、loc、charset)大小写敏感,且部分参数值必须 URL 编码。拼错会导致时间解析失败、中文变问号、事务不生效等隐蔽问题。
-
parseTime=true必须显式开启,否则time.Time字段会读成字符串 - 时区要用
loc=Asia%2FShanghai(不能写Asia/Shanghai,/ 需编码) - 字符集推荐写全:
charset=utf8mb4&collation=utf8mb4_unicode_ci,避免 emoji 插入失败 - 禁止用
timeout,正确参数是timeout(连接建立超时)、readTimeout、writeTimeout,三者作用完全不同
用 database/sql 写原生 SQL 时,WHERE IN (?) 不能直接展开切片
这是高频翻车点:db.Query("SELECT * FROM users WHERE id IN (?)", []int{1,2,3}) 会把整个切片当做一个参数传进去,MySQL 收到的是 IN ('[1 2 3]'),语法直接报错。
立即学习“go语言免费学习笔记(深入)”;
- 必须手动拼占位符:
placeholders := strings.Repeat("?,", len(ids)-1) + "?",再用...ids展开参数 - 注意 SQL 注入风险:只对数值型 ID 拼接安全;若含字符串,必须先验证合法性,不可直接拼进 SQL
- 大量 IN 查询建议改用临时表或分批执行,单次超过 1000 个值可能触发 MySQL
max_allowed_packet限制
驱动本身很稳定,真正消耗时间的永远是连接串细节、上下文超时设置、以及对 sql.DB 生命周期的误判——它不是一次性的,不该在 handler 里反复 Open,也不该在函数退出时随便 Close。











