golang纯Go实现的SQLite数据库操作插件库Sqinn-Go的使用
Golang纯Go实现的SQLite数据库操作插件库Sqinn-Go的使用
Sqinn-Go是一个用于访问SQLite数据库的Go库,它不需要使用cgo。它在底层使用了Sqinn,通过启动Sqinn作为子进程(使用os/exec
),并通过stdin/stdout/stderr与Sqinn通信。
安装
$ go get -u github.com/cvilsmeier/sqinn-go/sqinn
使用示例
下面是一个简单的使用示例,展示了如何创建表、插入数据和查询数据:
import "github.com/cvilsmeier/sqinn-go/sqinn"
// 简单的sqinn-go使用示例,为了简洁省略了错误处理
func main() {
// 启动sqinn,程序退出时终止
sq := sqinn.MustLaunch(sqinn.Options{})
defer sq.Terminate()
// 打开数据库,使用完毕后关闭
sq.MustOpen("./users.db")
defer sq.Close()
// 创建表
sq.MustExecOne("CREATE TABLE users (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR)")
// 插入用户
sq.MustExecOne("INSERT INTO users (id, name) VALUES (1, 'Alice')")
sq.MustExecOne("INSERT INTO users (id, name) VALUES (2, 'Bob')")
// 查询用户
rows := sq.MustQuery("SELECT id, name FROM users ORDER BY id", nil, []byte{sqinn.ValInt, sqinn.ValText})
for _, row := range rows {
fmt.Printf("id=%d, name=%s\n", row.Values[0].AsInt(), row.Values[1].AsString())
}
// 输出:
// id=1, name=Alice
// id=2, name=Bob
}
指定Sqinn路径
如果Sqinn二进制文件不在PATH路径中,可以通过以下方式指定路径:
// 从环境变量获取
sq := sqinn.MustLaunch(sqinn.Options{
SqinnPath: os.Getenv("SQINN_PATH"),
})
// 或者直接设置路径
sq := sqinn.MustLaunch(sqinn.Options{
SqinnPath: "/path/to/sqinn",
})
优缺点
优点
- 不需要在开发机器上安装gcc
- 支持Go交叉编译
- 比cgo构建速度更快(示例程序1秒 vs 3秒)
- 比cgo生成的二进制文件更小(示例程序2MB vs 10MB)
缺点
- 没有内置的连接池
- Sqinn-Go不是Golang的
database/sql
驱动 - Sqinn只覆盖了SQLite C API的一个子集
性能
性能测试显示Sqinn-Go的性能与cgo解决方案相当,具体取决于使用场景。以下是基准测试结果(单位为毫秒,数值越小越好):
mattn crawshaw sqinn
simple/insert 2901 2140 1563
simple/query 2239 1287 1390
complex/insert 2066 1817 1683
complex/query 1458 1129 1338
many/N=10 97 78 134
many/N=100 246 194 276
many/N=1000 1797 1240 1436
large/N=2000 119 87 341
large/N=4000 361 322 760
large/N=8000 701 650 1531
concurrent/N=2 1332 865 951
concurrent/N=4 1505 989 1207
concurrent/N=8 2347 1557 2044
测试
要运行测试,首先需要下载并安装Sqinn:
$ cd /tmp
$ curl -sL https://github.com/cvilsmeier/sqinn/releases/download/v1.1.27/dist-linux.zip >> dist-linux.zip && unzip dist-linux.zip
$ export SQINN_PATH=/tmp/sqinn
然后获取并测试Sqinn-Go:
$ go get -v -u github.com/cvilsmeier/sqinn-go/sqinn
$ go test github.com/cvilsmeier/sqinn-go/sqinn
检查测试覆盖率:
$ go test github.com/cvilsmeier/sqinn-go/sqinn -coverprofile=./cover.out
$ go tool cover -func=./cover.out
$ go tool cover -html=./cover.out
并发性
虽然Sqinn/Sqinn-Go在非并发和并发环境下都表现良好,但单个Sqinn实例应该只从一个goroutine调用。例外是Exec和Query方法,这些方法是互斥的并且是goroutine安全的。但由于Sqinn本质上是单线程的,Exec和Query请求是一个接一个处理的。
许可证
这是免费且不受限制的软件,发布到公共领域。
更多关于golang纯Go实现的SQLite数据库操作插件库Sqinn-Go的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang纯Go实现的SQLite数据库操作插件库Sqinn-Go的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Sqinn-Go: 纯Go实现的SQLite操作库
Sqinn-Go是一个纯Go实现的SQLite数据库操作库,它不需要依赖CGO,而是通过Sqinn子进程与SQLite交互。这使得它成为跨平台部署的理想选择,特别是在需要静态链接或避免CGO依赖的场景中。
安装Sqinn-Go
首先安装Sqinn-Go库:
go get github.com/cvilsmeier/sqinn-go/sqinn
基本使用方法
1. 初始化数据库连接
package main
import (
"fmt"
"log"
"github.com/cvilsmeier/sqinn-go/sqinn"
)
func main() {
// 创建Sqinn实例
sq, err := sqinn.Launch(sqinn.Options{
SqinnPath: "sqinn", // sqinn可执行文件路径
})
if err != nil {
log.Fatalf("启动sqinn失败: %v", err)
}
defer sq.Terminate()
// 打开数据库连接
err = sq.Open("test.db")
if err != nil {
log.Fatalf("打开数据库失败: %v", err)
}
defer sq.Close()
// 执行SQL语句
_, err = sq.ExecOne("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")
if err != nil {
log.Fatalf("创建表失败: %v", err)
}
fmt.Println("数据库初始化完成")
}
2. 插入数据
// 插入单条数据
func insertUser(sq *sqinn.Sqinn, name string, age int) error {
_, err := sq.ExecOne("INSERT INTO users (name, age) VALUES (?, ?)", name, age)
return err
}
// 批量插入数据
func batchInsertUsers(sq *sqinn.Sqinn, users []struct{Name string; Age int}) error {
// 开始事务
err := sq.ExecOne("BEGIN")
if err != nil {
return err
}
// 确保事务要么提交要么回滚
defer func() {
if err != nil {
sq.ExecOne("ROLLBACK")
}
}()
// 批量插入
for _, user := range users {
_, err = sq.ExecOne("INSERT INTO users (name, age) VALUES (?, ?)", user.Name, user.Age)
if err != nil {
return err
}
}
// 提交事务
return sq.ExecOne("COMMIT")
}
3. 查询数据
// 查询所有用户
func queryAllUsers(sq *sqinn.Sqinn) ([]struct{ID int; Name string; Age int}, error) {
// 执行查询
rows, err := sq.Query("SELECT id, name, age FROM users ORDER BY id")
if err != nil {
return nil, err
}
defer rows.Close()
// 处理结果集
var users []struct{ID int; Name string; Age int}
for rows.Next() {
var id int
var name string
var age int
err := rows.Scan(&id, &name, &age)
if err != nil {
return nil, err
}
users = append(users, struct{ID int; Name string; Age int}{id, name, age})
}
return users, nil
}
// 查询单个用户
func queryUserById(sq *sqinn.Sqinn, id int) (struct{Name string; Age int}, error) {
row, err := sq.QueryRow("SELECT name, age FROM users WHERE id = ?", id)
if err != nil {
return struct{Name string; Age int}{}, err
}
defer row.Close()
var name string
var age int
err = row.Scan(&name, &age)
if err != nil {
return struct{Name string; Age int}{}, err
}
return struct{Name string; Age int}{name, age}, nil
}
4. 更新和删除数据
// 更新用户信息
func updateUser(sq *sqinn.Sqinn, id int, name string, age int) error {
_, err := sq.ExecOne("UPDATE users SET name = ?, age = ? WHERE id = ?", name, age, id)
return err
}
// 删除用户
func deleteUser(sq *sqinn.Sqinn, id int) error {
_, err := sq.ExecOne("DELETE FROM users WHERE id = ?", id)
return err
}
高级特性
1. 预处理语句
// 使用预处理语句提高性能
func batchInsertWithPreparedStatement(sq *sqinn.Sqinn, users []struct{Name string; Age int}) error {
// 准备语句
stmt, err := sq.Prepare("INSERT INTO users (name, age) VALUES (?, ?)")
if err != nil {
return err
}
defer sq.Finalize(stmt)
// 开始事务
err = sq.ExecOne("BEGIN")
if err != nil {
return err
}
defer func() {
if err != nil {
sq.ExecOne("ROLLBACK")
}
}()
// 批量执行
for _, user := range users {
_, err = sq.Exec(stmt, user.Name, user.Age)
if err != nil {
return err
}
}
// 提交事务
return sq.ExecOne("COMMIT")
}
2. 处理BLOB数据
// 插入BLOB数据
func insertBlob(sq *sqinn.Sqinn, id int, data []byte) error {
_, err := sq.ExecOne("INSERT INTO blobs (id, data) VALUES (?, ?)", id, data)
return err
}
// 查询BLOB数据
func queryBlob(sq *sqinn.Sqinn, id int) ([]byte, error) {
row, err := sq.QueryRow("SELECT data FROM blobs WHERE id = ?", id)
if err != nil {
return nil, err
}
defer row.Close()
var data []byte
err = row.Scan(&data)
return data, err
}
注意事项
-
性能考虑:由于Sqinn-Go通过子进程通信,性能可能不如直接使用CGO的SQLite驱动。但对于大多数应用场景已经足够。
-
错误处理:务必检查所有数据库操作的错误返回。
-
资源释放:确保及时关闭数据库连接、语句和结果集。
-
并发安全:Sqinn-Go实例不是并发安全的,需要在多个goroutine中使用时添加同步机制。
-
SQLite特性:Sqinn-Go支持大多数SQLite特性,但某些高级功能可能不可用。
Sqinn-Go为Go开发者提供了一个不依赖CGO的SQLite解决方案,特别适合需要跨平台部署或静态链接的场景。虽然性能上可能略有牺牲,但换来了更好的可移植性和部署便利性。