golang高性能LSM键值存储引擎插件库moss的使用

golang高性能LSM键值存储引擎插件库moss的使用

moss简介

moss是一个简单、快速、可持久化的有序键值集合实现,100%使用Golang编写。moss代表"memory-oriented sorted segments"(面向内存的排序段)。

Build Status Coverage Status GoDoc Go Report Card

主要特性

  • 有序键值集合API
  • 100% Go实现
  • 键范围迭代器
  • 快照提供隔离读取
  • 通过批处理API进行原子变更
  • 合并操作允许对写密集型用例进行读-计算-写优化(例如更新计数器)
  • 并发读写不会相互阻塞
  • 子集合允许原子性地分组多个相关集合
  • 可选的高级API以避免额外的内存拷贝
  • 可选的底层存储实现"mossStore",使用追加写入设计和mmap()读取,具有可配置的压缩策略
  • mossStore支持以只读方式导航到之前的提交点,并支持回滚到之前的提交点
  • 可选持久化钩子,允许高级用户提供底层存储实现的回写缓存
  • 事件回调允许监控异步任务
  • 单元测试
  • 模糊测试
  • moss存储诊断工具:mossScope

许可证

Apache 2.0

使用示例

基本用法

import "github.com/couchbase/moss"

// 创建新集合
c, err := moss.NewCollection(moss.CollectionOptions{})
c.Start()
defer c.Close()

// 创建批处理
batch, err := c.NewBatch(0, 0)
defer batch.Close()

// 添加键值对
batch.Set([]byte("car-0"), []byte("tesla"))
batch.Set([]byte("car-1"), []byte("honda"))

// 执行批处理
err = c.ExecuteBatch(batch, moss.WriteOptions{})

// 获取快照
ss, err := c.Snapshot()
defer ss.Close()

ropts := moss.ReadOptions{}

// 从快照读取
val0, err := ss.Get([]byte("car-0"), ropts) // val0 == []byte("tesla").
valX, err := ss.Get([]byte("car-not-there"), ropts) // valX == nil.

// 也可以直接从集合读取
val1, err := c.Get([]byte("car-1"), ropts) // val1 == []byte("honda").

持久化存储

// 打开持久化存储集合
store, collection, err := moss.OpenStoreCollection(directoryPath,
    moss.StoreOptions{}, moss.StorePersistOptions{})

设计原理

设计类似于(大大简化的)LSM树,具有一堆已排序的不可变键值数组或"段"。

为了合并下一批键值变更,首先将传入的键值条目排序到一个不可变的"段"中,然后原子性地推送到段堆栈的顶部。

对于读取器,堆栈中较高的段将遮蔽较低段中相同键的条目。

一个异步goroutine(“合并器”)将不断合并N个排序段以保持堆栈高度较低。

在最佳情况下,剩余的一个大的排序段将在内存使用上高效,并且对于二分查找和范围迭代也高效。

当堆栈高度>1时,使用N路堆合并实现迭代。

在此设计中,每当需要"修改"堆栈时,通过写时复制方法将段堆栈视为不可变。因此,多个读取器和写入器不会相互阻塞,获取快照也是类似的廉价操作,只需克隆堆栈。

限制和注意事项

注意:批处理中的键必须是唯一的。也就是说,不支持myBatch.Set(“x”,“foo”); myBatch.Set(“x”,“bar”)。不自然满足此要求的应用程序可以维护自己的map[key]val数据结构以确保此唯一性约束。

最大键长度为2^24(使用24位跟踪键长度)。

最大值长度为2^28(使用28位跟踪值长度)。

每个键值操作的元数据开销为16字节。

读取性能特征大致为O(log N)用于键值检索。

写入性能特征大致为O(M log M),其中M是调用ExecuteBatch()时批处理中的变更数。

错误处理

请注意,moss的后台goroutine可能会遇到错误,例如在可选持久化操作期间。为了收到这些情况的通知,您的应用程序可以提供(强烈推荐)一个可选的CollectionOptions.OnError回调函数,moss将调用该函数。

日志记录

请参阅可选的CollectionOptions.Log回调函数和CollectionOptions.Debug标志。

性能

可以运行go test -bench=.进行一些基本性能测试。

每个性能测试通常会输出如下内容:

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
spec: {numItems:1000000 keySize:20 valSize:100 batchSize:100 randomLoad:false noCopyValue:false accesses:[]}
     open || time:     0 (ms) |        0 wop/s |        0 wkb/s |        0 rop/s |        0 rkb/s || cumulative:        0 wop/s |        0 wkb/s |        0 rop/s |        0 rkb/s
     load || time:   840 (ms) |  1190476 wop/s |   139508 wkb/s |        0 rop/s |        0 rkb/s || cumulative:  1190476 wop/s |   139508 wkb/s |        0 rop/s |        0 rkb/s
    drain || time:   609 (ms) |        0 wop/s |        0 wkb/s |        0 rop/s |        0 rkb/s || cumulative:   690131 wop/s |    80874 wkb/s |        0 rop/s |        0 rkb/s
    close || time:     0 (ms) |        0 wop/s |        0 wkb/s |        0 rop/s |        0 rkb/s || cumulative:   690131 wop/s |    80874 wkb/s |        0 rop/s |        0 rkb/s
   reopen || time:     0 (ms) |        0 wop/s |        0 wkb/s |        0 rop/s |        0 rkb/s || cumulative:   690131 wop/s |    80874 wkb/s |        0 rop/s |        0 rkb/s
     iter || time:    81 (ms) |        0 wop/s |        0 wkb/s | 12344456 rop/s |  1446616 rkb/s || cumulative:   690131 wop/s |    80874 wkb/s | 12344456 rop/s |  1446616 rkb/s
    close || time:     2 (ms) |        0 wop/s |        0 wkb/s |        0 rop/s |        0 rkb/s || cumulative:   690131 wop/s |    80874 wkb/s | 12344456 rop/s |  1446616 rkb/s
total time: 1532 (ms)
file size: 135 (MB), amplification: 1.133
BenchmarkStore_numItems1M_keySize20_valSize100_batchSize100-8

测试包含多个阶段:

  • open - 打开全新的moss存储实例
  • load - 加载N个顺序键的时间
  • drain - 加载后持久化完成的额外时间
  • close - 关闭moss存储实例的时间
  • reopen - 重新打开moss存储实例的时间(OS/文件系统缓存仍然有效)
  • iter - 顺序迭代键值项的时间
  • access - 执行各种访问模式的时间,如随机或顺序读写

文件大小测量是在最终压缩后进行的,放大率是与原始键值大小的简单比较计算。


更多关于golang高性能LSM键值存储引擎插件库moss的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang高性能LSM键值存储引擎插件库moss的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Go语言高性能LSM键值存储引擎moss使用指南

moss是一个用Go语言实现的高性能LSM(Log-Structured Merge-Tree)键值存储引擎插件库,由Couchbase开发并开源。它具有高性能、低延迟和低内存开销的特点。

主要特性

  • 基于LSM树结构设计
  • 支持并发读写操作
  • 内存占用低
  • 支持快照和迭代器
  • 可配置的合并策略
  • 纯Go实现,无外部依赖

基本使用

安装

go get github.com/couchbase/moss

创建和打开存储

package main

import (
	"fmt"
	"github.com/couchbase/moss"
)

func main() {
	// 创建或打开存储
	store, err := moss.NewStore("/tmp/mossStore", moss.StoreOptions{})
	if err != nil {
		panic(err)
	}
	defer store.Close()

	// 确保存储已准备好
	err = store.Open()
	if err != nil {
		panic(err)
	}
	
	fmt.Println("Moss store opened successfully")
}

基本CRUD操作

// 创建集合(类似于表)
coll, err := store.NewCollection(moss.CollectionOptions{})
if err != nil {
    panic(err)
}
defer coll.Close()

// 写入数据
batch, err := coll.NewBatch(0, 0)
if err != nil {
    panic(err)
}

batch.Set([]byte("key1"), []byte("value1"))
batch.Set([]byte("key2"), []byte("value2"))

err = coll.ExecuteBatch(batch, moss.WriteOptions{})
if err != nil {
    panic(err)
}

// 读取数据
val, err := coll.Get([]byte("key1"), moss.ReadOptions{})
if err != nil {
    panic(err)
}
fmt.Printf("key1: %s\n", val)

// 删除数据
batchDel, err := coll.NewBatch(0, 0)
if err != nil {
    panic(err)
}

batchDel.Del([]byte("key2"))
err = coll.ExecuteBatch(batchDel, moss.WriteOptions{})
if err != nil {
    panic(err)
}

高级功能

使用快照

// 创建快照
snapshot, err := coll.Snapshot()
if err != nil {
    panic(err)
}
defer snapshot.Close()

// 从快照读取
val, err := snapshot.Get([]byte("key1"), moss.ReadOptions{})
if err != nil {
    panic(err)
}
fmt.Printf("Snapshot read key1: %s\n", val)

迭代器使用

iter, err := snapshot.StartIterator(nil, nil, moss.IteratorOptions{})
if err != nil {
    panic(err)
}
defer iter.Close()

for {
    key, val, err := iter.Current()
    if err == moss.ErrIteratorDone {
        break
    }
    if err != nil {
        panic(err)
    }
    fmt.Printf("Key: %s, Value: %s\n", key, val)
    err = iter.Next()
    if err == moss.ErrIteratorDone {
        break
    }
    if err != nil {
        panic(err)
    }
}

合并策略配置

// 自定义合并策略
store, err := moss.NewStore("/tmp/mossStoreAdvanced", moss.StoreOptions{
    CollectionOptions: moss.CollectionOptions{
        MergeOperator: &moss.DefaultMergeOperator{},
        MergePolicy: moss.NewMergePolicy(moss.MergePolicyOptions{
            MaxMergeSegments: 10,
            MaxSegmentSize:   1024 * 1024, // 1MB
        }),
    },
})
if err != nil {
    panic(err)
}

性能优化建议

  1. 批量操作:尽量使用批量操作而不是单条操作
  2. 合理配置合并策略:根据数据特点调整合并策略
  3. 使用快照:长时间运行的查询使用快照而非直接访问集合
  4. 控制WAL大小:适当调整Write-Ahead Log大小
  5. 内存限制:对于内存敏感场景,设置合理的内存限制

实际应用示例

package main

import (
	"fmt"
	"log"
	"time"
	
	"github.com/couchbase/moss"
)

func main() {
	// 初始化存储
	store, err := moss.NewStore("/tmp/mossDemo", moss.StoreOptions{})
	if err != nil {
		log.Fatal(err)
	}
	defer store.Close()
	
	if err := store.Open(); err != nil {
		log.Fatal(err)
	}
	
	coll, err := store.NewCollection(moss.CollectionOptions{})
	if err != nil {
		log.Fatal(err)
	}
	defer coll.Close()
	
	// 批量写入性能测试
	start := time.Now()
	batchSize := 10000
	batch, _ := coll.NewBatch(batchSize, batchSize*10)
	
	for i := 0; i < batchSize; i++ {
		key := fmt.Sprintf("key-%d", i)
		value := fmt.Sprintf("value-%d", i)
		batch.Set([]byte(key), []byte(value))
	}
	
	if err := coll.ExecuteBatch(batch, moss.WriteOptions{}); err != nil {
		log.Fatal(err)
	}
	
	elapsed := time.Since(start)
	fmt.Printf("Inserted %d items in %s\n", batchSize, elapsed)
	
	// 读取测试
	readStart := time.Now()
	for i := 0; i < 100; i++ {
		key := fmt.Sprintf("key-%d", i%batchSize)
		if val, err := coll.Get([]byte(key), moss.ReadOptions{}); err == nil {
			_ = val // 使用val
		} else {
			log.Printf("Error reading key %s: %v", key, err)
		}
	}
	fmt.Printf("Read 100 items in %s\n", time.Since(readStart))
}

moss是一个轻量级但功能强大的键值存储引擎,特别适合需要高性能和低延迟的场景。它的纯Go实现使其易于集成到各种Go应用中,而无需处理CGO的复杂性。

回到顶部