面试题答案
一键面试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
表,包含id
和balance
字段,在实际使用时调用如下:
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%,这样可以在连接被数据库服务器关闭前,由连接池主动重新创建连接,保证应用程序的正常运行。
- 最大空闲连接数(MaxIdleConns):设置连接池中保持的最大空闲连接数。如果应用程序在一段时间内有突发的数据库请求高峰,但平均请求量相对稳定,可根据平均请求量来设置此参数。例如,应用程序平均每秒处理10个数据库请求,并且每个请求处理时间约为0.1秒,那么理论上至少需要1个连接来处理这些请求。考虑到一些额外开销,可设置
在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)