go操作mysql数据库(database/sql)

一、database/sql 包对数据库的连接

  • 提供了 sql.Open 函数用于打开数据库连接。
  • 通过 sql.DB 类型管理连接池,支持高效地处理并发请求。
  1. 连接池管理

    • sql.DB 维护一组活跃的数据库连接,这些连接可以被多个goroutine共享。
    • 当需要执行数据库操作时,sql.DB 会从连接池中分配一个可用连接,操作完成后将连接返回到池中。
  2. 并发安全

    • sql.DB 是并发安全的,多个goroutine可以同时使用它来执行数据库操作。
  3. 连接复用

    • 连接池中的连接可以被复用,避免了频繁创建和销毁连接的开销。
  4. 连接限制

    • 通过配置 SetMaxOpenConnsSetMaxIdleConns,可以控制连接池的大小和空闲连接的数量。

二、sql.DB 的连接池机制

1. 连接池的工作流程

  • 获取连接 : 当调用 db.Querydb.Exec 等方法时,sql.DB 会从连接池中获取一个可用连接。如果池中没有可用连接且未达到最大连接数限制,则会创建一个新连接。
  • 释放连接: 当数据库操作完成后,连接会被返回到连接池中,供后续操作复用。
  • 空闲连接管理 : 如果连接池中有空闲连接,它们会保持打开状态,直到超过最大空闲时间(通过 SetConnMaxIdleTime 设置)或达到最大空闲连接数(通过 SetMaxIdleConns 设置)。

2. 连接池的配置

  • SetMaxOpenConns : 设置连接池中最大打开的连接数。默认值为 0,表示无限制。

    db.SetMaxOpenConns(10)

  • SetMaxIdleConns : 设置连接池中最大空闲连接数。默认值为 2

    db.SetMaxIdleConns(5)

  • SetConnMaxLifetime : 设置连接的最大存活时间。超过该时间的连接会被关闭并重新创建。默认值为 0,表示无限制。

    db.SetConnMaxLifetime(time.Hour)

  • SetConnMaxIdleTime : 设置连接的最大空闲时间。超过该时间的空闲连接会被关闭。默认值为 0,表示无限制。

    db.SetConnMaxIdleTime(30 * time.Minute)


三、sql.DB 的优势

  1. 高效利用资源

    • 通过复用连接,减少了创建和销毁连接的开销,提高了性能。
  2. 支持高并发

    • 多个goroutine可以同时使用 sql.DB 执行数据库操作,适合高并发场景。
  3. 自动管理连接

    • sql.DB 自动管理连接的创建、复用和销毁,开发者无需手动处理。
  4. 灵活的配置

    • 通过设置连接池参数,可以根据应用需求优化连接池的行为。

四、注意事项

  1. 连接泄漏

    • 如果未正确关闭 sql.Rowssql.Stmt,可能会导致连接泄漏。确保使用 defer rows.Close()defer stmt.Close()
  2. 连接池大小

    • 连接池的大小应根据应用的并发需求和数据库的负载能力进行合理配置。过大的连接池可能导致数据库资源耗尽,过小的连接池可能导致性能瓶颈。
  3. 长连接问题

    • 如果连接池中的连接长时间保持打开状态,可能会导致数据库服务器的资源占用过高。通过设置 SetConnMaxLifetimeSetConnMaxIdleTime 可以缓解这一问题。

五、示例代码

go 复制代码
package main 
 
import (
	"database/sql"
	"fmt"
	"log"
	"time"
 
	_ "github.com/go-sql-driver/mysql"
)
 
func main() {
	// 数据库连接信息 
	dsn := "username:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True"
 
	// 打开数据库连接 
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		log.Fatalf("Failed to connect to database: %v", err)
	}
	defer db.Close()
 
	// 配置连接池 
	db.SetMaxOpenConns(10)
	db.SetMaxIdleConns(5)
	db.SetConnMaxLifetime(time.Hour)
	db.SetConnMaxIdleTime(30 * time.Minute)
 
	// 测试连接 
	err = db.Ping()
	if err != nil {
		log.Fatalf("Failed to ping database: %v", err)
	}
	fmt.Println("Successfully connected to the database!")
 
	// 执行查询 
	rows, err := db.Query("SELECT id, name FROM users")
	if err != nil {
		log.Fatalf("Failed to execute query: %v", err)
	}
	defer rows.Close()
 
	for rows.Next() {
		var id int 
		var name string 
		err = rows.Scan(&id, &name)
		if err != nil {
			log.Fatalf("Failed to scan row: %v", err)
		}
		fmt.Printf("ID: %d, Name: %s\n", id, name)
	}
 
	if err = rows.Err(); err != nil {
		log.Fatalf("Error during rows iteration: %v", err)
	}
}

sql.DB 是Go语言中用于管理数据库连接池的核心类型。它通过复用连接、支持高并发和自动管理连接生命周期,显著提高了数据库操作的性能和效率。通过合理配置连接池参数,可以进一步优化应用与数据库的交互行为。

六、database/sql数据库的增删改查

  • 查询与执行

    • 支持执行SQL查询(QueryQueryRow)和更新操作(Exec)。
    • 提供了 sql.Rowssql.Row 类型用于处理查询结果。
  • 事务管理

    • 支持事务操作(BeginCommitRollback),确保数据一致性。
  • 预处理语句

    • 通过 sql.Stmt 类型支持预处理语句,提高执行效率。
  • 错误处理

    • 提供了丰富的错误类型(如 sql.ErrNoRows),便于开发者处理异常情况。

​编辑


七、database/sql 数据库的增删改查代码

1. 打开数据库连接

使用 sql.Open 函数打开数据库连接:

go 复制代码
import (
	"database/sql"
	_ "github.com/go-sql-driver/mysql" // 导入MySQL驱动 
)
 
func main() {
	dsn := "username:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True"
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		log.Fatalf("Failed to connect to database: %v", err)
	}
	defer db.Close()
 
	err = db.Ping()
	if err != nil {
		log.Fatalf("Failed to ping database: %v", err)
	}
	fmt.Println("Successfully connected to the database!")
}

2. 执行查询

go 复制代码
// 查询多行数据 
rows, err := db.Query("SELECT id, name FROM users")
if err != nil {
	log.Fatalf("Failed to execute query: %v", err)
}
defer rows.Close()
 
for rows.Next() {
	var id int 
	var name string 
	err = rows.Scan(&id, &name)
	if err != nil {
		log.Fatalf("Failed to scan row: %v", err)
	}
	fmt.Printf("ID: %d, Name: %s\n", id, name)
}
 
// 查询单行数据 
var id int 
var name string 
err = db.QueryRow("SELECT id, name FROM users WHERE id = ?", 1).Scan(&id, &name)
if err != nil {
	log.Fatalf("Failed to query row: %v", err)
}
fmt.Printf("ID: %d, Name: %s\n", id, name)

3. 执行更新操作

使用 Exec 执行插入、更新和删除操作:

go 复制代码
// 插入数据 
result, err := db.Exec("INSERT INTO users (name) VALUES (?)", "John")
if err != nil {
	log.Fatalf("Failed to insert data: %v", err)
}
lastInsertID, err := result.LastInsertId()
if err != nil {
	log.Fatalf("Failed to get last insert ID: %v", err)
}
fmt.Printf("Inserted ID: %d\n", lastInsertID)
 
// 更新数据 
result, err = db.Exec("UPDATE users SET name = ? WHERE id = ?", "Jane", 1)
if err != nil {
	log.Fatalf("Failed to update data: %v", err)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
	log.Fatalf("Failed to get rows affected: %v", err)
}
fmt.Printf("Rows affected: %d\n", rowsAffected)
 
// 删除数据 
result, err = db.Exec("DELETE FROM users WHERE id = ?", 1)
if err != nil {
	log.Fatalf("Failed to delete data: %v", err)
}
rowsAffected, err = result.RowsAffected()
if err != nil {
	log.Fatalf("Failed to get rows affected: %v", err)
}
fmt.Printf("Rows affected: %d\n", rowsAffected)

4. 事务管理

使用 BeginCommitRollback 管理事务:

lua 复制代码
tx, err := db.Begin()
if err != nil {
	log.Fatalf("Failed to begin transaction: %v", err)
}
 
// 在事务中执行操作 
_, err = tx.Exec("UPDATE users SET name = ? WHERE id = ?", "Alice", 2)
if err != nil {
	tx.Rollback()
	log.Fatalf("Failed to update data: %v", err)
}
 
_, err = tx.Exec("DELETE FROM users WHERE id = ?", 3)
if err != nil {
	tx.Rollback()
	log.Fatalf("Failed to delete data: %v", err)
}
 
err = tx.Commit()
if err != nil {
	log.Fatalf("Failed to commit transaction: %v", err)
}
fmt.Println("Transaction committed successfully!")

5. 事务中的预处理

lua 复制代码
tx, err := db.Begin()
if err != nil {
    log.Fatalf("Failed to begin transaction: %v", err)
}
 
stmt, err := tx.Prepare("UPDATE users SET age = ? WHERE id = ?")
if err != nil {
    tx.Rollback()
    log.Fatalf("Failed to prepare statement: %v", err)
}
defer stmt.Close()
 
_, err = stmt.Exec(31, 1)
if err != nil {
    tx.Rollback()
    log.Fatalf("Failed to execute statement: %v", err)
}
 
err = tx.Commit()
if err != nil {
    log.Fatalf("Failed to commit transaction: %v", err)
}
fmt.Println("Transaction committed successfully!")

八、database/sql 包的最佳实践

  1. 使用连接池

    • 通过 SetMaxOpenConnsSetMaxIdleConns 配置连接池大小,优化性能。
  2. 处理错误

    • 始终检查并处理数据库操作的错误,避免程序崩溃。
  3. 释放资源

    • 使用 defer 确保关闭 sql.Rowssql.Stmtsql.DB,防止资源泄漏。
  4. 预处理语句

    • 对于频繁执行的SQL语句,使用 sql.Stmt 提高效率。
  5. 事务管理

    • 在需要保证数据一致性的操作中使用事务。

database/sql 包是Go语言中操作关系型数据库的核心工具。它提供了统一的接口,支持连接管理、查询执行、事务处理和错误处理等功能。通过合理使用连接池、预处理语句和事务,可以显著提高数据库操作的性能和可靠性

相关推荐
newerp1 天前
Go net/http 标准库基础
后端·程序员·go
2651940511264801 天前
01-整体架构与高可用
go
程序员爱钓鱼1 天前
Go if 判断详解
前端·后端·go
名字还没想好☜2 天前
Go 的 unsafe.Pointer 实战:零拷贝 []byte↔string 转换与三条铁律
开发语言·后端·golang·go·unsafe
阿里云云原生2 天前
阿里云联合 Datadog,补齐 Go 可观测性最后短板
云原生·go
可观测性用观测云2 天前
零码改造!Go 语言应用上报观测云完整最佳实践
go
斐波那契数列2 天前
参考react 实现一个golang gui
go
程序员爱钓鱼2 天前
Go 运算符详解
后端·面试·go
angryshan2 天前
goLang配置断点流程
go·bug
名字还没想好☜3 天前
Go 的 sync.Cond 实战:用条件变量做等待/通知,比忙轮询省 CPU
开发语言·数据库·后端·golang·go