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)
}
性能优化建议
- 对于大量写入操作,使用批量(Batch)而不是单独Put
- 长时间运行的读操作考虑使用快照(Snapshot)
- 合理设置缓存大小
- 定期压缩数据库以减少碎片
注意事项
- Coffer不是分布式数据库,仅适合单机使用
- 数据库文件不能同时被多个进程访问
- 大事务会占用较多内存
- 记得及时关闭数据库和迭代器
Coffer是一个简单但功能完整的键值存储解决方案,适合需要ACID事务但不想依赖外部数据库的Golang应用场景。