golang支持ACID事务的简单键值数据库插件库Coffer的使用

Golang支持ACID事务的简单键值数据库插件库Coffer的使用

Coffer是一个简单的ACID键值数据库,在中等甚至低延迟情况下,它试图在不失去数据库ACID属性的前提下提供更高的吞吐量。

特性

  • 高吞吐量
  • 可容忍的延迟
  • 高可靠性

ACID特性:

  • 良好的持久性
  • 强制隔离
  • 原子操作
  • 一致性事务

快速开始

下面是一个使用Coffer的简单示例:

package main

import (
	"fmt"

	"github.com/claygod/coffer"
)

const curDir = "./"

func main() {
	// 初始化数据库
	db, err, wrn := coffer.Db(curDir).Create()
	switch {
	case err != nil:
		fmt.Println("Error:", err)
		return
	case wrn != nil:
		fmt.Println("Warning:", err)
		return
	}
	if !db.Start() {
		fmt.Println("Error: not start")
		return
	}
	defer db.Stop()

	// 写入数据
	if rep := db.Write("foo", []byte("bar")); rep.IsCodeError() {
		fmt.Sprintf("Write error: code `%d` msg `%s`", rep.Code, rep.Error)
		return
	}

	// 读取数据
	rep := db.Read("foo")
	if rep.IsCodeError() {
		fmt.Sprintf("Read error: code `%v` msg `%v`", rep.Code, rep.Error)
		return
	}
	fmt.Println(string(rep.Data))
}

事务处理示例

Coffer支持自定义事务处理器,以下是两个示例:

不使用参数的事务处理器

func HandlerExchange(arg []byte, recs map[string][]byte) (map[string][]byte, error) {
	if arg != nil {
		return nil, fmt.Errorf("Args not null.")
	} else if len(recs) != 2 {
		return nil, fmt.Errorf("Want 2 records, have %d", len(recs))
	}
	recsKeys := make([]string, 0, 2)
	recsValues := make([][]byte, 0, 2)
	for k, v := range recs {
		recsKeys = append(recsKeys, k)
		recsValues = append(recsValues, v)
	}
	out := make(map[string][]byte, 2)
	out[recsKeys[0]] = recsValues[1]
	out[recsKeys[1]] = recsValues[0]
	return out, nil
}

使用参数的事务处理器

func HandlerDebit(arg []byte, recs map[string][]byte) (map[string][]byte, error) {
	if arg == nil || len(arg) != 8 {
		return nil, fmt.Errorf("Invalid Argument: %v.", arg)
	} else if len(recs) != 1 {
		return nil, fmt.Errorf("Want 1 record, have %d", len(recs))
	}
	delta := bytesToUint64(arg)
	var recKey string
	var recValue []byte
	for k, v := range recs {
		recKey = k
		recValue = v
	}
	if len(recValue) != 8 {
		return nil, fmt.Errorf("The length of the value in the record is %d bytes, but 8 bytes are needed", len(recValue))
	}
	curAmount := bytesToUint64(recValue)
	newAmount := curAmount + delta
	if curAmount > newAmount {
		return nil, fmt.Errorf("Account overflow. There is %d, a debit of %d.", curAmount, delta)
	}
	return map[string][]byte{recKey: uint64ToBytes(newAmount)}, nil
}

主要API方法

  • Start() - 启动数据库
  • Stop() - 停止数据库
  • StopHard() - 强制停止数据库
  • Write() - 写入单个记录
  • WriteList() - 写入多个记录
  • Read() - 读取单个记录
  • ReadList() - 读取多个记录
  • Delete() - 删除单个记录
  • DeleteListStrict() - 严格模式删除多个记录
  • DeleteListOptional() - 可选模式删除多个记录
  • Transaction() - 执行事务
  • Count() - 获取记录数
  • RecordsList() - 获取所有键列表
  • RecordsListWithPrefix() - 获取具有指定前缀的键列表
  • RecordsListWithSuffix() - 获取具有指定后缀的键列表

注意:所有名称包含"Unsafe"的请求通常可以在数据库运行或停止(不运行)时执行。

配置选项

创建数据库时可以配置以下参数:

Db(dirPath).
	BatchSize(batchSize).
	LimitRecordsPerLogfile(limitRecordsPerLogfile).
	FollowPause(100*time.Second).
	LogsByCheckpoint(1000).
	AllowStartupErrLoadLogs(true).
	MaxKeyLength(maxKeyLength).
	MaxValueLength(maxValueLength).
	MaxRecsPerOperation(1000000).
	RemoveUnlessLogs(true).
	LimitMemory(100 * 1000000).
	LimitDisk(1000 * 1000000).
	Handler("handler1", &handler1).
	Handler("handler2", &handler2).
	Handlers(map[string]*handler).
	Create()

错误代码

Coffer使用以下错误代码:

  • Ok - 操作成功完成
  • Error - 未完成或未完全完成,但可以继续工作
  • ErrRecordLimitExceeded - 每个操作的记录限制超出
  • ErrExceedingMaxValueSize - 值太长
  • ErrExceedingMaxKeyLength - 键太长
  • ErrExceedingZeroKeyLength - 键太短
  • ErrHandlerNotFound - 未找到处理器
  • ErrParseRequest - 日志请求准备失败
  • ErrResources - 资源不足
  • ErrNotFound - 未找到键
  • ErrReadRecords - 事务读取记录错误
  • ErrHandlerReturn - 处理器返回错误
  • ErrHandlerResponse - 处理器返回不完整回复
  • Panic - 未完成,无法继续使用数据库
  • PanicStopped - 应用已停止
  • PanicWAL - 操作日志出错

性能基准

  • BenchmarkCofferWriteParallel32LowConcurent-4: 100000 12933 ns/op
  • BenchmarkCofferTransactionSequence-4: 2000 227928 ns/op
  • BenchmarkCofferTransactionPar32NotConcurent-4: 100000 4132 ns/op
  • BenchmarkCofferTransactionPar32HalfConcurent-4: 100000 4199 ns/op

依赖项

Coffer是一个功能强大且灵活的键值数据库,特别适合需要ACID事务保证的应用场景。


更多关于golang支持ACID事务的简单键值数据库插件库Coffer的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang支持ACID事务的简单键值数据库插件库Coffer的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Coffer - Golang的ACID键值数据库插件库

Coffer是一个轻量级的Golang键值数据库库,支持ACID事务特性。下面我将介绍Coffer的基本使用方法和示例代码。

安装Coffer

首先安装Coffer库:

go get github.com/coffer-org/coffer

基本使用

1. 打开数据库

package main

import (
	"github.com/coffer-org/coffer"
	"log"
)

func main() {
	// 打开数据库(如果不存在则创建)
	db, err := coffer.Open("mydb.coffer")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
	
	// 使用数据库...
}

2. 基本CRUD操作

// 写入数据
err = db.Put([]byte("key1"), []byte("value1"))
if err != nil {
	log.Fatal(err)
}

// 读取数据
value, err := db.Get([]byte("key1"))
if err != nil {
	log.Fatal(err)
}
fmt.Printf("Value for key1: %s\n", value)

// 删除数据
err = db.Delete([]byte("key1"))
if err != nil {
	log.Fatal(err)
}

3. 使用ACID事务

// 开始事务
tx, err := db.Begin(true) // true表示可写事务
if err != nil {
	log.Fatal(err)
}

// 事务操作
err = tx.Put([]byte("txKey1"), []byte("txValue1"))
if err != nil {
	tx.Rollback()
	log.Fatal(err)
}

err = tx.Delete([]byte("oldKey"))
if err != nil {
	tx.Rollback()
	log.Fatal(err)
}

// 提交事务
err = tx.Commit()
if err != nil {
	log.Fatal(err)
}

4. 批量操作

batch := db.NewBatch()
defer batch.Cancel()

err = batch.Put([]byte("batchKey1"), []byte("batchValue1"))
if err != nil {
	log.Fatal(err)
}

err = batch.Put([]byte("batchKey2"), []byte("batchValue2"))
if err != nil {
	log.Fatal(err)
}

// 执行批量操作
err = batch.Commit()
if err != nil {
	log.Fatal(err)
}

5. 迭代器使用

iter := db.NewIterator()
defer iter.Close()

for iter.First(); iter.Valid(); iter.Next() {
	key := iter.Key()
	value := iter.Value()
	fmt.Printf("Key: %s, Value: %s\n", key, value)
}

if err := iter.Error(); err != nil {
	log.Fatal(err)
}

高级特性

1. 使用前缀搜索

prefix := []byte("user_")
iter := db.NewIteratorWithPrefix(prefix)
defer iter.Close()

for iter.First(); iter.Valid(); iter.Next() {
	key := iter.Key()
	value := iter.Value()
	fmt.Printf("User Key: %s, Value: %s\n", key, value)
}

2. 设置和获取元数据

// 设置元数据
err = db.SetMeta([]byte("app_version"), []byte("1.0.0"))
if err != nil {
	log.Fatal(err)
}

// 获取元数据
version, err := db.GetMeta([]byte("app_version"))
if err != nil {
	log.Fatal(err)
}
fmt.Printf("App version: %s\n", version)

3. 备份和恢复

// 备份数据库
err = db.Backup("mydb_backup.coffer")
if err != nil {
	log.Fatal(err)
}

// 从备份恢复
err = coffer.Restore("mydb_backup.coffer", "mydb_restored.coffer")
if err != nil {
	log.Fatal(err)
}

性能优化建议

  1. 对于大量写入操作,使用批量(Batch)而不是单独Put
  2. 长时间运行的读操作考虑使用快照(Snapshot)
  3. 合理设置缓存大小
  4. 定期压缩数据库以减少碎片

注意事项

  1. Coffer不是分布式数据库,仅适合单机使用
  2. 数据库文件不能同时被多个进程访问
  3. 大事务会占用较多内存
  4. 记得及时关闭数据库和迭代器

Coffer是一个简单但功能完整的键值存储解决方案,适合需要ACID事务但不想依赖外部数据库的Golang应用场景。

回到顶部