MST
星途 面试题库

面试题:Go数据库事务处理与连接池优化

在Go语言中,使用database/sql包实现一个银行转账的事务操作,从一个账户向另一个账户转账一定金额。同时,阐述如何优化数据库连接池以提高系统性能,包括连接池参数的设置依据等。
34.3万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

1. Go语言中使用database/sql包实现银行转账事务操作示例代码

package main

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

func transfer(db *sql.DB, fromAccountID, toAccountID int, amount float64) error {
    // 开启事务
    tx, err := db.Begin()
    if err != nil {
        return err
    }

    // 从转出账户扣除金额
    _, err = tx.Exec("UPDATE accounts SET balance = balance -? WHERE id =? AND balance >=?", amount, fromAccountID, amount)
    if err != nil {
        // 回滚事务
        tx.Rollback()
        return err
    }

    // 向转入账户增加金额
    _, err = tx.Exec("UPDATE accounts SET balance = balance +? WHERE id =?", amount, toAccountID)
    if err != nil {
        // 回滚事务
        tx.Rollback()
        return err
    }

    // 提交事务
    err = tx.Commit()
    if err != nil {
        return err
    }

    return nil
}

假设数据库中有accounts表,包含idbalance字段,在实际使用时调用如下:

func main() {
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/bank")
    if err != nil {
        panic(err.Error())
    }
    defer db.Close()

    fromAccountID := 1
    toAccountID := 2
    amount := 100.0

    err = transfer(db, fromAccountID, toAccountID, amount)
    if err != nil {
        fmt.Println("转账失败:", err)
    } else {
        fmt.Println("转账成功")
    }
}

2. 优化数据库连接池以提高系统性能

  • 连接池参数设置依据
    • 最大空闲连接数(MaxIdleConns):设置连接池中保持的最大空闲连接数。如果应用程序在一段时间内有突发的数据库请求高峰,但平均请求量相对稳定,可根据平均请求量来设置此参数。例如,应用程序平均每秒处理10个数据库请求,并且每个请求处理时间约为0.1秒,那么理论上至少需要1个连接来处理这些请求。考虑到一些额外开销,可设置MaxIdleConns为5 - 10个连接,这样可以在请求量较小时保持一定数量的空闲连接,避免频繁创建和销毁连接。
    • 最大打开连接数(MaxOpenConns):决定连接池可以同时打开的最大连接数。这取决于数据库服务器的资源(如CPU、内存、网络带宽等)以及应用程序的并发请求量。例如,数据库服务器是一台具有8核CPU和16GB内存的机器,假设每个数据库连接占用约10MB内存,并且考虑到数据库服务器本身及其他可能的负载,可估算出最多能支持的连接数。假设可用内存为10GB,那么最多可支持10 * 1024 / 10 = 1024个连接,但实际应用中要预留一定的资源给其他操作,可设置MaxOpenConns为500 - 800左右。如果应用程序并发请求量非常高,也需要适当增大此参数,但不能超过数据库服务器的承载能力。
    • 连接最大存活时间(ConnMaxLifetime):设置连接在连接池中可以存活的最长时间。如果数据库服务器有一些配置(如连接超时时间),需要确保ConnMaxLifetime设置小于数据库服务器的连接超时时间,以避免出现无效连接。另外,如果数据库服务器在一段时间不活动后会关闭连接(如MySQL的wait_timeout参数),那么ConnMaxLifetime应设置为小于wait_timeout,例如设置为wait_timeout的80%,这样可以在连接被数据库服务器关闭前,由连接池主动重新创建连接,保证应用程序的正常运行。

在Go语言中设置连接池参数示例:

db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/bank")
if err != nil {
    panic(err.Error())
}
// 设置最大空闲连接数
db.SetMaxIdleConns(10)
// 设置最大打开连接数
db.SetMaxOpenConns(100)
// 设置连接最大存活时间
db.SetConnMaxLifetime(time.Minute * 5)