我认为我在 Golang 中管理数据库连接池时遇到了严重问题。我使用 Gorilla Web 工具包构建了一个 RESTful API,当只有很少的请求被发送到服务器时,它工作得很好。但现在我开始使用 loader.io 站点执行负载测试。我为这篇长文道歉,但我想给你完整的图片。
在继续之前,这里有一些关于运行 API 和 MySQL 的服务器的信息: 专用主机 Linux 8GB RAM Go 版本 1.1.1 使用 go-sql-driver MySQL 5.1 的数据库连接
使用 loader.io 我可以毫无问题地发送 1000 个 GET 请求/15 秒。但是当我每 15 秒发送 1000 个 POST 请求时,我会收到很多错误,所有这些错误都是 ERROR 1040 too many database connections。许多人在网上报告了类似的问题。请注意,我现在只测试一个特定的 POST 请求。对于这个帖子请求,我确保了以下内容(网上许多其他人也建议了这一点)
我确保我不使用 Open and Close *sql.DB 来处理短期功能。因此,正如您在下面的代码中看到的那样,我只为连接池创建了全局变量,尽管我在这里接受建议,因为我不喜欢使用全局变量。
我确保尽可能使用 db.Exec,并且仅在预期结果时使用 db.Query 和 db.QueryRow。
由于上述没有解决我的问题,我尝试设置 db.SetMaxIdleConns(1000),解决了 1000 POST 请求/15 秒的问题。意味着不再有 1040 错误。然后我将负载增加到 2000 个 POST 请求/15 秒,我又开始收到 ERROR 1040。我试图增加 db.SetMaxIdleConns() 中的值,但这并没有什么不同。
variable_name这里我通过运行 SHOW STATUS WHERE = 'Threads_connected'从 MySQL 数据库中获得了一些关于连接数的连接统计信息;
对于 1000 个 POST 请求/15 秒:观察到 #threads_connected ~= 100 对于 2000 个 POST 请求/15 秒:观察到 #threads_connected ~= 600
我还在 my.cnf 中增加了 MySQL 的最大连接数,但这并没有什么不同。你有什么建议?代码看起来不错?如果是,那么可能是连接受到限制。
您将在下面找到代码的简化版本。
var db *sql.DB
func main() {
db = DbConnect()
db.SetMaxIdleConns(1000)
http.Handle("/", r)
err := http.ListenAndServe(fmt.Sprintf("%s:%s", API_HOST, API_PORT), nil)
if err != nil {
fmt.Println(err)
}
}
func DbConnect() *sql.DB {
db, err := sql.Open("mysql", connectionString)
if err != nil {
fmt.Printf("Connection error: %s\n", err.Error())
return nil
}
return db
}
func PostBounce(w http.ResponseWriter, r *http.Request) {
userId, err := AuthRequest(r)
//error checking
//ready requesy body and use json.Unmarshal
bounceId, err := CreateBounce(userId, b)
//return HTTP status code here
}
func AuthRequest(r *http.Request) (id int, err error) {
//parse header and get username and password
query := "SELECT Id FROM Users WHERE Username=? AND Password=PASSWORD(?)"
err = db.QueryRow(query, username, password).Scan(&id)
//error checking and return
}
func CreateBounce(userId int, bounce NewBounce) (bounceId int64, err error) {
//initialize some variables
query := "INSERT INTO Bounces (.....) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
result, err := db.Exec(query, ......)
//error checking
bounceId,_ = result.LastInsertId()
//return
}