使用go-sql-driver操作MySQL时QueryRow始终返回nil,请求排查
问题分析与解决:Go + MySQL容器环境下QueryRow查询异常
问题背景
我搭建了一个三容器项目(前端、Go后端、MySQL),核心逻辑是:前端提交含姓名、邮箱的表单到后端,后端检查邮箱是否已存在于MySQL,不存在则存储数据并发送成功邮件。但使用github.com/go-sql-driver/mysql库的QueryRow方法检查邮箱时,即便邮箱已存入数据库,该方法始终返回nil。排查两小时后,重启所有容器问题解决。
代码中的潜在bug
先看这段引发问题的代码片段:
searched_email := "'" + user.Email + "'" row := db.QueryRow("SELECT email FROM users WHERE email = ?", searched_email)
这里手动给邮箱字符串添加了单引号,但QueryRow的参数占位符?会自动处理字符串的转义和引号包裹,最终执行的SQL会变成查询email = 'xxx@xx.com'(这里的引号是手动添加的,等于要求数据库匹配带引号的邮箱值),但数据库里存储的是不带引号的原始邮箱,所以永远查不到结果,返回nil。
修复后的代码
去掉手动添加的引号,直接传入原始邮箱值,同时优化错误判断逻辑:
func Is_User_Saved(user appmodel.User) bool { dsn := "root:example@tcp(db)/mysql" db, err := sql.Open("mysql", dsn) if err != nil { log.Printf("%v\n", err) return false } defer db.Close() // 直接使用user.Email作为参数,无需手动加引号 row := db.QueryRow("SELECT email FROM users WHERE email = ?", user.Email) var email string err = row.Scan(&email) if err != nil { // sql.ErrNoRows是"未找到数据"的正常情况,单独判断 if err == sql.ErrNoRows { return false } log.Printf("%v\n", err) return false } return true }
补充说明:原代码把所有错误都直接返回false,但sql.ErrNoRows是预期的"邮箱未存在"的正常场景,单独判断可以避免把数据库连接异常等错误和业务逻辑正常情况混淆。
容器重启解决问题的可能原因
虽然代码本身存在bug,但重启容器后问题临时解决,可能是以下原因之一:
- 容器启动顺序问题:后端容器启动时MySQL容器还未完全初始化,驱动建立了无效连接并缓存,重启后MySQL已就绪,连接正常建立
- 连接池缓存问题:Go的sql包维护的连接池缓存了无效连接,重启后端容器后重建了连接池,获取到有效连接
- Docker网络DNS解析异常:偶尔出现容器间DNS解析失败,重启容器后重新触发DNS解析,恢复了
db到MySQL容器的IP映射
内容的提问来源于stack exchange,提问作者ilpandy
相关产品推荐
相关产品推荐

