Golang中数据库查询上下文取消的问题

Golang中数据库查询上下文取消的问题 你好,

当我向下面的路由发送数百个并发请求时,我收到了很多 context canceled 错误——即使我将超时时间增加到100秒。就数据库交互而言,这是Go的常规做法,还是我遗漏了什么?

我知道当响应返回时上下文会被取消,但我在想,如果查询仍在运行,响应尚未返回,为什么会出现 context canceled 错误?WithCancel() 导致了这种错误情况。

谢谢

func Handler(w http.ResponseWriter, r *http.Request) {
    err := Insert(r.Context())
    // 处理其余部分
}

func Insert(ctx context.Context) error {
	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
	defer cancel()

	if _, err := mysql.ExecContext(ctx, `MY QUERY`, "xyz"); err != nil {
		return err // context canceled
	}

	return nil
}

// 这是我处理其余部分的方式
//
// mysql, err := sql.Open("mysql", "user:pass@tcp(:3306)/client")
// if err != nil {
// 	log.Fatalln(err)
// }
//
// http.ListenAndServe(":8080", Handler)

更多关于Golang中数据库查询上下文取消的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中数据库查询上下文取消的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中处理数据库查询时遇到context canceled错误,通常是因为客户端在查询完成前关闭了连接。当HTTP请求被取消(例如客户端断开连接)时,上下文会被取消,这会传播到数据库查询。即使你设置了10秒超时,如果客户端提前断开,查询仍会被取消。

以下是关键点和示例代码:

  1. 上下文传播:HTTP请求的上下文会传播到数据库查询。如果请求被取消,数据库查询也会被取消。
  2. 连接池管理:确保数据库连接池配置合理,避免连接耗尽导致上下文取消。

示例代码,展示如何验证上下文取消的原因:

func Insert(ctx context.Context) error {
    ctx, cancel := context.WithTimeout(ctx, time.Second*10)
    defer cancel()

    if _, err := mysql.ExecContext(ctx, `MY QUERY`, "xyz"); err != nil {
        // 检查错误是否为上下文取消
        if errors.Is(err, context.Canceled) {
            log.Println("查询被上下文取消")
        } else if errors.Is(err, context.DeadlineExceeded) {
            log.Println("查询超时")
        }
        return err
    }

    return nil
}
  1. 并发请求处理:当数百个并发请求到达时,数据库连接池可能不足,导致查询排队。如果请求在排队期间被取消,查询会失败并返回context canceled

检查数据库连接池配置:

mysql.SetMaxOpenConns(100) // 根据数据库负载调整
mysql.SetMaxIdleConns(10)
mysql.SetConnMaxLifetime(time.Hour)
  1. 请求生命周期:确保在查询完成前,HTTP响应没有提前返回或取消。如果处理函数在查询完成前返回,上下文会被取消。

示例:使用sync.WaitGroup等待查询完成(仅用于调试,生产环境不推荐阻塞响应):

func Handler(w http.ResponseWriter, r *http.Request) {
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        err := Insert(r.Context())
        if err != nil {
            log.Println("插入错误:", err)
        }
    }()
    wg.Wait() // 仅用于调试,避免在生产中使用
    w.WriteHeader(http.StatusOK)
}
  1. 数据库驱动行为:某些MySQL驱动可能在上下文取消后立即返回错误,即使查询仍在数据库端运行。这是Go数据库驱动的预期行为。

总结:context canceled错误在并发请求中是常见的,通常是由于客户端断开或请求处理提前结束导致的。确保合理配置连接池,并处理上下文取消的错误情况。

回到顶部