golang无需修改代码即可使用预编译SQL语句插件库prep的使用

Golang无需修改代码即可使用预编译SQL语句插件库prep的使用

Prep是一个能够自动发现Go包中所有SQL语句并为数据库连接添加预编译语句支持的库。它允许你几乎不需要修改现有代码就能享受到预编译SQL语句带来的性能优势。

组成部分

Prep包含两部分:

  1. 一个命令行工具,用于查找代码中的所有SQL语句
  2. 一个包,使用找到的SQL语句为你的代码添加预编译SQL语句支持

使用方法

生成应用程序中使用的SQL语句列表

假设有以下示例代码:

func main() {
	db, err := sql.Open("mysql", "user:pass@tcp(localhost:3306)/mysql")
	if err != nil {
		panic(err)
	}
	const query = `SELECT CONCAT("Hello ", ?, "!")`
	var s string
	if err := db.QueryRow(query, "World").Scan(&s); err != nil {
		panic(err)
	}
	fmt.Println(s)
}

运行以下命令生成SQL语句列表:

$ prep -f github.com/hexdigest/prepdemo
$ cat prepared_statements.go

生成的prepared_statements.go文件内容如下:

//go:generate prep -f github.com/hexdigest/prepdemo

package main

var prepStatements = []string{
	"SELECT CONCAT(\"Hello \", ?, \"!\")",
}

使用预编译语句

修改主程序以使用预编译语句:

func main() {
	sqlDB, err := sql.Open("mysql", "root:root@tcp(localhost:3306)/mysql")
	if err != nil {
		panic(err)
	}

	db, err := prep.NewConnection(sqlDB, prepStatements)
	if err != nil {
		panic(err)
	}

	const query = `SELECT CONCAT("Hello ", ?, "!")`
	var s string
	if err := db.QueryRow(query, "World").Scan(&s); err != nil {
		panic(err)
	}
	fmt.Println(s)
}

关键行是:

db, err := prep.NewConnection(sqlDB, prepStatements)

这行代码会为你的数据库连接添加预编译语句支持。生成的代码已经包含了//go:generate指令,所以更新语句列表只需运行:

$ go generate

性能基准测试

以下是一些基准测试结果:

$ go test -bench=.
BenchmarkPostgresWithoutPreparedStatements-4   	   20000	     59941 ns/op	    1183 B/op	      32 allocs/op
BenchmarkPostgresWithPreparedStatements-4      	   50000	     41560 ns/op	    1021 B/op	      26 allocs/op
BenchmarkMySQLWithoutPreparedStatements-4      	   50000	     26454 ns/op	     827 B/op	      23 allocs/op
BenchmarkMySQLWithPreparedStatements-4         	  200000	      9509 ns/op	     634 B/op	      19 allocs/op
PASS
ok  	github.com/hexdigest/prep	7.884s

从结果可以看出,使用预编译语句后性能有显著提升。


更多关于golang无需修改代码即可使用预编译SQL语句插件库prep的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang无需修改代码即可使用预编译SQL语句插件库prep的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用prep库实现Golang预编译SQL语句

prep是一个Golang库,它允许你在不修改现有代码的情况下为SQL查询添加预编译语句支持。这个库通过拦截数据库操作来自动处理预编译语句,提供了性能优化和安全防护。

基本使用

首先安装prep库:

go get github.com/go-gorp/gorp/v3
go get github.com/hexdigest/prep

简单示例

package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/go-sql-driver/mysql"
	"github.com/hexdigest/prep"
)

func main() {
	// 1. 正常连接数据库
	db, err := sql.Open("mysql", "user:password@/dbname")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// 2. 使用prep包装原始db连接
	preppedDB := prep.New(db)

	// 3. 像平常一样使用数据库,但会自动使用预编译语句
	var name string
	err = preppedDB.QueryRow("SELECT name FROM users WHERE id = ?", 1).Scan(&name)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("User name: %s\n", name)
}

高级特性

批量插入示例

func batchInsert(db *sql.DB) error {
	preppedDB := prep.New(db)
	
	tx, err := preppedDB.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	stmt, err := tx.Prepare("INSERT INTO users(name, age) VALUES(?, ?)")
	if err != nil {
		return err
	}
	defer stmt.Close()

	users := []struct {
		Name string
		Age  int
	}{
		{"Alice", 25},
		{"Bob", 30},
		{"Charlie", 35},
	}

	for _, user := range users {
		_, err = stmt.Exec(user.Name, user.Age)
		if err != nil {
			return err
		}
	}

	return tx.Commit()
}

配置选项

prep库提供了一些配置选项:

preppedDB := prep.New(db,
	prep.WithTracer(myTracer),      // 添加跟踪器
	prep.WithMaxStmtCacheSize(100), // 设置最大缓存语句数
	prep.WithLogger(myLogger),      // 自定义日志
)

工作原理

prep库通过以下方式工作:

  1. 拦截所有数据库调用
  2. 自动将原始SQL转换为预编译语句
  3. 缓存预编译语句以提高性能
  4. 透明地处理参数绑定

性能考虑

使用prep库的注意事项:

  1. 对于高频简单查询,预编译可能带来轻微性能开销
  2. 对于复杂查询或批量操作,预编译能显著提高性能
  3. 语句缓存可以减少重复编译的开销

最佳实践

  1. 对于Web应用,建议全局使用一个prep包装的DB实例
  2. 长期运行的语句应该手动Prepare然后重用
  3. 监控语句缓存命中率以优化MaxStmtCacheSize

prep库的优势在于无需重写现有代码即可获得预编译语句的好处,包括:

  • 防止SQL注入
  • 提高重复查询性能
  • 统一的SQL参数处理

这个库特别适合已有项目优化或需要快速增加SQL安全性的场景。

回到顶部