golang纯Go实现的SQLite数据库操作插件库Sqinn-Go的使用

Golang纯Go实现的SQLite数据库操作插件库Sqinn-Go的使用

Sqinn

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

1 回复

更多关于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
}

注意事项

  1. 性能考虑:由于Sqinn-Go通过子进程通信,性能可能不如直接使用CGO的SQLite驱动。但对于大多数应用场景已经足够。

  2. 错误处理:务必检查所有数据库操作的错误返回。

  3. 资源释放:确保及时关闭数据库连接、语句和结果集。

  4. 并发安全:Sqinn-Go实例不是并发安全的,需要在多个goroutine中使用时添加同步机制。

  5. SQLite特性:Sqinn-Go支持大多数SQLite特性,但某些高级功能可能不可用。

Sqinn-Go为Go开发者提供了一个不依赖CGO的SQLite解决方案,特别适合需要跨平台部署或静态链接的场景。虽然性能上可能略有牺牲,但换来了更好的可移植性和部署便利性。

回到顶部