Golang中初始化与保持预处理语句的最佳实践是什么

Golang中初始化与保持预处理语句的最佳实践是什么

大家好,我是Go语言的新手。我想知道是否有推荐的方法在应用程序启动时初始化多个预处理语句,并在多个HTTP处理程序中重复使用它们。
1 回复

更多关于Golang中初始化与保持预处理语句的最佳实践是什么的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,初始化并复用预处理语句的最佳实践是使用全局变量或依赖注入来管理这些语句。以下是一个示例,展示如何在应用启动时初始化多个预处理语句,并在多个HTTP处理程序中复用它们。

首先,定义一个结构体来封装预处理语句,并使用sync.Once确保它们只初始化一次。然后,在HTTP处理程序中通过依赖注入或全局实例来访问这些语句。

package main

import (
	"database/sql"
	"fmt"
	"log"
	"net/http"
	"sync"

	_ "github.com/go-sql-driver/mysql" // 导入MySQL驱动
)

// StmtManager 管理所有预处理语句
type StmtManager struct {
	once sync.Once
	db   *sql.DB

	// 定义预处理语句字段
	GetUserStmt    *sql.Stmt
	InsertUserStmt *sql.Stmt
	UpdateUserStmt *sql.Stmt
}

// InitStmts 初始化所有预处理语句,使用sync.Once确保只执行一次
func (sm *StmtManager) InitStmts() error {
	var initErr error
	sm.once.Do(func() {
		// 初始化每个预处理语句
		sm.GetUserStmt, initErr = sm.db.Prepare("SELECT id, name FROM users WHERE id = ?")
		if initErr != nil {
			return
		}
		sm.InsertUserStmt, initErr = sm.db.Prepare("INSERT INTO users (name) VALUES (?)")
		if initErr != nil {
			return
		}
		sm.UpdateUserStmt, initErr = sm.db.Prepare("UPDATE users SET name = ? WHERE id = ?")
		if initErr != nil {
			return
		}
	})
	return initErr
}

// CloseStmts 关闭所有预处理语句,通常在应用退出时调用
func (sm *StmtManager) CloseStmts() {
	if sm.GetUserStmt != nil {
		sm.GetUserStmt.Close()
	}
	if sm.InsertUserStmt != nil {
		sm.InsertUserStmt.Close()
	}
	if sm.UpdateUserStmt != nil {
		sm.UpdateUserStmt.Close()
	}
}

var stmtManager *StmtManager

func init() {
	// 初始化数据库连接
	db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/dbname")
	if err != nil {
		log.Fatal("Failed to open database:", err)
	}
	stmtManager = &StmtManager{db: db}
	if err := stmtManager.InitStmts(); err != nil {
		log.Fatal("Failed to initialize statements:", err)
	}
}

// GetUserHandler HTTP处理程序示例,使用GetUserStmt
func GetUserHandler(w http.ResponseWriter, r *http.Request) {
	id := r.URL.Query().Get("id")
	var name string
	err := stmtManager.GetUserStmt.QueryRow(id).Scan(&id, &name)
	if err != nil {
		http.Error(w, "User not found", http.StatusNotFound)
		return
	}
	fmt.Fprintf(w, "User: %s, ID: %s", name, id)
}

// InsertUserHandler HTTP处理程序示例,使用InsertUserStmt
func InsertUserHandler(w http.ResponseWriter, r *http.Request) {
	name := r.URL.Query().Get("name")
	result, err := stmtManager.InsertUserStmt.Exec(name)
	if err != nil {
		http.Error(w, "Insert failed", http.StatusInternalServerError)
		return
	}
	lastID, _ := result.LastInsertId()
	fmt.Fprintf(w, "Inserted user with ID: %d", lastID)
}

func main() {
	defer stmtManager.CloseStmts() // 确保在应用退出时关闭语句

	http.HandleFunc("/user", GetUserHandler)
	http.HandleFunc("/user/insert", InsertUserHandler)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

在这个示例中:

  • StmtManager 结构体使用 sync.Once 来保证预处理语句只在应用启动时初始化一次。
  • 预处理语句作为全局变量 stmtManagerinit 函数中初始化,确保在HTTP服务器启动前完成。
  • 每个HTTP处理程序(如 GetUserHandlerInsertUserStmt)直接复用这些语句,避免重复准备。
  • main 函数中使用 defer 调用 CloseStmts 来清理资源。

这种方法提高了性能,因为预处理语句只需编译一次,并可在多个goroutine中安全使用(假设数据库驱动支持并发)。注意替换数据库连接字符串为实际值,并根据需要添加错误处理。

回到顶部