报错如下
golang 版本 1.19
Error 1040: Too many connections
D:/GolandProjects/souti/src/main/mysql_conn.go:56 (0xe21d0f)
DbQuery: panic(err)
D:/GolandProjects/souti/src/main/aid_ctrl.go:19 (0xe20e15)
initAidCtrl.func1: qListRes := Dbc.Query(“SELECT 马赛克 ORDER BY CONVERT(马赛克,SIGNED) LIMIT 5”, c.Param(“aid”))
D:/GOPATH/pkg/mod/github.com/gin-gonic/gin@v1.8.1/context.go:173 (0xde7f81)
(*Context).Next: c.handlersc.index
D:/GOPATH/pkg/mod/github.com/gin-gonic/gin@v1.8.1/recovery.go:101 (0xde7f6c)
CustomRecoveryWithWriter.func1: c.Next()
D:/GOPATH/pkg/mod/github.com/gin-gonic/gin@v1.8.1/context.go:173 (0xde7086)
(*Context).Next: c.handlersc.index
D:/GOPATH/pkg/mod/github.com/gin-gonic/gin@v1.8.1/logger.go:240 (0xde7069)
LoggerWithConfig.func1: c.Next()
D:/GOPATH/pkg/mod/github.com/gin-gonic/gin@v1.8.1/context.go:173 (0xde6190)
(*Context).Next: c.handlersc.index
D:/GOPATH/pkg/mod/github.com/gin-gonic/gin@v1.8.1/gin.go:616 (0xde5df8)
(*Engine).handleHTTPRequest: c.Next()
D:/GOPATH/pkg/mod/github.com/gin-gonic/gin@v1.8.1/gin.go:572 (0xde593c)
(*Engine).ServeHTTP: engine.handleHTTPRequest©
D:/Go/src/net/http/server.go:2947 (0xc2616b)
serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
D:/Go/src/net/http/server.go:1991 (0xc21386)
(*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req)
D:/Go/src/runtime/asm_amd64.s:1594 (0x9b6d20)
goexit: BYTE $0x90 // NOP
golang都吹出花来了,这也好,那也好,就是坑也不少
我试了网上各种各样的方法,有说要手动Close连接的,下一条查询直接就会报错
说结论吧:
如果你查询的结果只有一条,不能用 Query 函数查询,需要使用 QueryRow 函数查询
一切都因为我写了个骚操作
我看着网上循环取数据的 demo 私自改装出了只取一条数据的代码
//这是我参考的循环代码
变量 := Dbc.Query("寻常的SQL语句", 参数) //查询
for 变量.Next() {//我把它理解成了java中的 hasNext()方法
ele := 结构体{} //使用结构体接收数据
变量.Scan(&ele.Qid, &ele.Num, &ele.Name, &ele.Desc)//查询数据
eleList1 = append(eleList1, ele)//拼装数组
}
//然后我写出了这样的代码 注意,这里直接获取了数据而没有循环
变量 := Dbc.Query("只会返回一条数据的SQL语句", 参数) //查询
变量.Next()
变量.Scan(&e.属性)
于是,当程序运行一段时间后,错误就发生了
查询了一下这个问题,有说要调用 Ping 函数的,有说要 Close 的,我都尝试了,根本没用。
在看到很多说 golang 泄露内存的内容后,我灵光一闪,可能问题就出在我改造的这条语句。
于是我把他换成了 QueryRow 的方式调用,果然问题消失了。
经过多次尝试后,下面这种写法不会造成内存泄漏
变量 := Dbc.Query("只会返回一条数据的SQL语句", 参数) //查询
变量.Next() //第一次调用 Next
变量.Scan(&e.属性) //调用 Scan
变量.Next() //第二次调用 Next
只有他,可以等同于
//应该使用的形式
变量 := Dbc.QueryRow("只会返回一条数据的SQL语句", 参数) //单行查询
//只能换成这种形式
变量 := Dbc.Query("只会返回一条数据的SQL语句", 参数) //多行查询
变量.Next() //第一次调用 Next
变量.Scan(&e.属性) //调用 Scan
变量.Next() //第二次调用 Next
于是我们可以得到结论:
golang 数据库操作中,QueryRow 函数不会造成内存(指连接池资源)泄漏,但是 Query 函数有可能,你必须要使用循环去读取 Query 的结果,才不会导致泄漏,如果你自作聪明,对只返回一条语句的查询使用 Query 而非 QueryRow,golang 将会教你做人。除非,你可以完美的骗过 golang 假装你是个循环,这块内容的底层就是为 for i 循环设计的(for range 也不例外)。可行的绕过方式是先判断 Next() 然后执行 然后再判断 Next() 。在这个过程中 Next() 函数需要被调用两次,就像for循环中的条件那样,如此才不会引发问题。