我想运行单独的goroutine,它可以与MySQL数据库一起工作.我编写了代码,如果goroutine的数量少于1000,它确实可以工作.但当我把它改成1000时,Go开始panic .

package main

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    "time"
)

func routine(db *sql.DB, id int, ch chan<- string) {
    update, _ := db.Prepare("SELECT salary FROM users WHERE id = (?)")
    defer update.Close()
    var salary string
    update.QueryRow(id).Scan(&salary)
    ch <- salary
}

func main() {
    n := 1000

    ch := make(chan string)
    list := make([]string, n)

    db, _ := sql.Open("mysql", "root:root@/database")
    db.SetConnMaxLifetime(time.Minute * 3)
    defer db.Close()

    for i := 0; i < n; i++ {
        go routine(db, 123, ch)
    }
    for i := 0; i < n; i++ {
        list[i] = <-ch
    }
}

这里是错误

panic: runtime error: invalid memory address or nil pointer dereference
        panic: runtime error: invalid memory address or nil pointer dereference

goroutine 948 [running]:
database/sql.(*Stmt).Close(0x0)
        C:/Program Files/Go/src/database/sql/sql.go:2872 +0x37
panic({0x4e8b20, 0x689240})
        C:/Program Files/Go/src/runtime/panic.go:838 +0x207
database/sql.(*Stmt).QueryContext(0x0, {0x5788e8, 0xc000018050}, {0xc000d5bf60, 0x1, 0x1})
        C:/Program Files/Go/src/database/sql/sql.go:2767 +0x82
database/sql.(*Stmt).QueryRowContext(0x0?, {0x5788e8?, 0xc000018050?}, {0xc000d5bf60?, 0x27?, 0x0?})
        C:/Program Files/Go/src/database/sql/sql.go:2845 +0x2c
database/sql.(*Stmt).QueryRow(...)
        C:/Program Files/Go/src/database/sql/sql.go:2867
main.routine(0x0?, 0x0?, 0x0?)
        C:/Users/me/Desktop/go/5. MySQL/main.go:13 +0xfb
created by main.main
        C:/Users/me/Desktop/go/5. MySQL/main.go:28 +0xba

我真的不确定这个错误的原因是什么.该代码使用较少的goroutine.此外,我try 使用SQLite适配器,1000个Goroutine工作得很好.但MySQL中的1000不是.

你能描述一下如何摆脱这些panic ,让1000个甚至上万个goroutine与数据库一起工作吗?


My Go version is 1.18.3 windows/amd64
github.com/go-sql-driver/mysql - v1.6.0

推荐答案

判断你的错误,不要忽略它们.例如

update, _ := db.Prepare("SELECT salary FROM users WHERE id = (?)")

db.Prepare返回的第二个值是error,它可以解释出现了什么问题,但您忽略了它.如果prepare失败,update将损坏.当你try 在update.QueryRow(id).Scan(&salary)defer update.Close()中使用它时,你会感到panic .

判断错误并进行处理.在这种情况下,请打印并返回.

update, err := db.Prepare("SELECT salary FROM users WHERE id = (?)")
if err != nil {
  fmt.Println("db.Prepare failed:", err)
  return
}

// This has to come afterwards else you'll try to close nil.
defer update.Close()

对可能返回错误的所有内容都执行此操作.这意味着db.Preparesql.OpenRow.Scan.

另见Errors and Exception Handling in Golang.


注:MySQL有a maximum number of prepared statements at one time个.如果将其设置为较低,例如1024,则可能是问题所在.但是错误应该告诉你.

注意:一次又一次地准备、执行和关闭同一条语句会违背准备语句的观点.在实际应用程序中,您需要准备一次语句并将其传递到每个Goroutine中.只有当所有的Gorouting都完成后,你才能关闭声明.

Mysql相关问答推荐

mysql查询汇总数据

MySQL生成的整型计算可以为空吗?

含有子查询的MySQL函数出现语法错误

使用不同的 where 列进行子 Select

获取每个参数的记录,不重复

使用mysql查询获取不关注user1的user3

global max_connections 和 spring.hikari.maximumPoolSize 有什么区别?

根据 Power Query 中的条件替换值

需要按总金额检索前 3 个供应商,每个类别 - 子类别

从多到多表中 Select 数据而无需重复

在 MySQL 中转换 JSON 数组值

如果在表中多次发现 a 列中的相同值,则排除所有行

WHERE SQL 语句中的列顺序是否重要

MySQL 视图

提高mysql导入速度

脚本超时,如果要完成导入,请重新提交相同的文件,导入将恢复

如何将 MySQL 5.7 更新到新的 MySQL 8.0?

SQL WHERE 列 = 一切

MySQL更新查询与左连接和分组依据

MySQL CREATE TABLE 语句中的 PRIMARY KEY 定义