理解和观察数据库连接池的变化是通过实际操作和代码示例进行的最佳方法之一。下面我将为你提供一个详细的步骤,通过编写代码和执行命令来观察 xorm
的数据库连接池的行为。
步骤一:准备工作
确保你已经安装了以下工具和环境:
- Go 编程环境:确保你已经正确安装了 Go,并设置好了工作环境。
- MySQL 数据库:在本地或远程服务器上安装 MySQL 数据库,并准备好连接信息(如用户名、密码、主机地址、端口号和数据库名称)。
步骤二:编写代码
以下是一个简单的 Go 程序,使用 xorm
库来执行数据库操作,并观察连接池的变化。
package main
import (
"fmt"
"log"
"time"
"xorm.io/xorm"
_ "github.com/go-sql-driver/mysql"
)
type User struct {
Id int64
Name string
}
func main() {
// 创建数据库引擎
engine, err := xorm.NewEngine("mysql", "username:password@tcp(localhost:3306)/dbname?charset=utf8mb4")
if err != nil {
log.Fatalf("Failed to create engine: %v", err)
}
defer engine.Close()
// 设置连接池参数
engine.SetMaxOpenConns(10)
engine.SetMaxIdleConns(5)
engine.SetConnMaxLifetime(60 * time.Second)
// 创建表
err = engine.Sync2(new(User))
if err != nil {
log.Fatalf("Failed to sync table: %v", err)
}
// 启动多个并发 goroutine 进行数据库操作
for i := 1; i <= 20; i++ {
go func(uid int) {
session := engine.NewSession()
defer session.Close()
// 执行数据库操作(插入记录)
_, err := session.Insert(&User{Name: fmt.Sprintf("User%d", uid)})
if err != nil {
log.Printf("Failed to insert record: %v", err)
} else {
log.Printf("Inserted record for User%d", uid)
}
// 模拟一些操作时间
time.Sleep(2 * time.Second)
}(i)
}
// 等待所有 goroutine 完成
time.Sleep(30 * time.Second)
}
步骤三:运行代码
- 编译和运行:在命令行中执行以下命令,编译并运行上述代码:
go run main.go
- 观察数据库连接池的变化:
这将显示当前 MySQL 实例中所有活动连接的信息,包括连接的 ID、状态、执行的 SQL 查询等。
-
- 打开 MySQL 命令行或其他 MySQL 客户端工具。
- 使用以下命令观察当前的数据库连接状态:
SHOW PROCESSLIST;
步骤四:观察结果
通过上述步骤,你可以观察到以下现象:
- 连接池的创建和管理:在程序运行时,可以看到
SHOW PROCESSLIST;
返回的连接数逐渐增加,直到达到SetMaxOpenConns
设置的上限。 - 连接的复用:完成操作后,连接并不会立即关闭,而是被归还到连接池中,供后续操作复用。
- 连接生命周期:如果设置了
SetConnMaxLifetime
参数,连接可能会在达到最大生命周期后被关闭和重新创建。
通过这些实际操作,你可以更深入地理解和调整 xorm
中数据库连接池的行为,以便优化应用程序的性能和资源使用。
实战检验
package main
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"log"
"time"
"xorm.io/xorm"
)
type User struct {
Id int64
Name string
}
func main() {
// 创建数据库引擎
engine, err := xorm.NewEngine("mysql", "root:@tcp(localhost:3306)/blog?charset=utf8mb4")
if err != nil {
log.Fatalf("Failed to create engine: %v", err)
}
defer engine.Close()
// 设置连接池参数
engine.SetMaxOpenConns(3)
engine.SetMaxIdleConns(1)
engine.SetConnMaxLifetime(10000 * time.Second)
// 创建表
err = engine.Sync2(new(User))
if err != nil {
log.Fatalf("Failed to sync table: %v", err)
}
// 启动多个并发 goroutine 进行数据库操作
for i := 1; i <= 10000; i++ {
go func(uid int) {
session := engine.NewSession()
defer session.Close()
/**
engine.SetMaxOpenConns(3)
engine.SetMaxIdleConns(1)
*/
/**
会瞬间启动一万个协程,瞬间执行完插入一万条。
show processlist 的结果:由于瞬间插入完,所以会从3个query会话,快速下降到idle设置的1个sleep会话
猜想: 插入完可能就直接提交了当前语句的事务(哪怕没被close),当前session就会被其他操作语句使用,所以就不会妨碍别的语句的执行
*/
_, err := session.Insert(&User{Name: fmt.Sprintf("User%d", uid)})
fmt.Println(err)
time.Sleep(10 * time.Second)
/**
会瞬间启动一万个协程,每隔10秒,插入3条记录
show processlist 的结果:由于启动了事务,该操作一直占用链接。阻塞插完,先从3个query会话,执行语句完,快速变成3个sleep会话,10秒后提交会话,轮到下一批操作。期间一直保持3个会话
由于启动了事务,该事务会一直占用该session,直到事务提交或回滚,别的操作语句在此期间无法使用该session
*/
//if err := session.Begin(); err == nil {
// _, err := session.Insert(&User{Name: fmt.Sprintf("User%d", uid)})
// if err != nil {
// log.Printf("Failed to insert record: %v", err)
// session.Rollback()
// } else {
// log.Printf("Inserted record for User%d", uid)
// time.Sleep(10 * time.Second)
// session.Commit()
// }
//} else {
// log.Printf("Failed to begin session: %v", err)
//}
}(i)
}
// 等待所有 goroutine 完成
time.Sleep(60 * time.Second)
}