MCPcopy Index your code
hub / github.com/nutsdb/nutsdb

github.com/nutsdb/nutsdb @v2.0.0 sqlite

repository ↗ · DeepWiki ↗ · release v2.0.0 ↗
1,748 symbols 10,914 edges 142 files 596 documented · 34%
README
<img src="https://user-images.githubusercontent.com/6065007/141310364-62d7eebb-2cbb-4949-80ed-5cd20f705405.png">

NutsDB GoDoc Go Report Card Go codecov License Mentioned in Awesome Go

English | 简体中文

NutsDB 是一个用纯 Go 编写的简单、快速、可嵌入且持久的键/值存储。

它支持完全可序列化的事务以及 List、Set、SortedSet 等多种数据结构。 所有操作都发生在 Tx 内部。 Tx 代表一个事务,可以是只读的,也可以是读写的。 只读事务可以读取给定存储桶和给定键的值或迭代一组键值对。 读写事务可以从数据库中读取、更新和删除键。

我们可以在NutsDB的文档网站了解更多:NutsDB Documents

欢迎对NutsDB感兴趣的加群、一起开发,具体看这个issue:https://github.com/nutsdb/nutsdb/issues/116 。

关注nutsdb公众号

nutsdb公众号

公告

📢 注意:从v0.9.0开始,DefaultOptions 里面的 defaultSegmentSize 做了调整从原来的 8MB 变成了 256MB,如果你原来设置 256MB 不用改,如果原来使用的是默认值的,需要手动改成 8MB,不然原来的数据不会解析。这边的大小调整原因是从 v0.9.0 开始有对文件描述符的缓存(详解见PR https://github.com/nutsdb/nutsdb/issues/164 ),所以需要用户看下自己的文件描述符数量,有不清楚可以提issue或者群里问。

nutsdb v1.0.0 之后,由于底层数据存储协议的变化,旧版本的数据不兼容。 请在使用新版本之前重写数据。 并且当前的 Bucket 需要手动创建。 详细请参见Bucket使用文档

学习资料

https://www.bilibili.com/video/BV1T34y1x7AS/

架构设计

nutsdb-架构图

目录

入门指南

安装

NutsDB的安装很简单,首先保证 Golang 已经安装好 (版本要求1.11以上). 然后在终端执行命令:

go get -u github.com/nutsdb/nutsdb

开启数据库

要打开数据库需要使用nutsdb.Open()这个方法。其中用到的选项(options)包括 Dir , EntryIdxModeSegmentSize,在调用的时候这些参数必须设置。官方提供了DefaultOptions的选项,直接使用nutsdb.DefaultOptions即可。当然你也可以根据需要自己定义。

例子:

package main

import (
    "log"

    "github.com/nutsdb/nutsdb"
)

func main() {
    db, err := nutsdb.Open(
        nutsdb.DefaultOptions,
        nutsdb.WithDir("/tmp/nutsdb"), // 数据库会自动创建这个目录文件
    )
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    ...
}

选项配置

  • Dir string

Dir 代表数据库存放数据的目录

  • EntryIdxMode EntryIdxMode

EntryIdxMode 代表索引entry的模式. EntryIdxMode 包括选项: HintKeyValAndRAMIdxModeHintKeyAndRAMIdxModeHintBPTSparseIdxMode

其中HintKeyValAndRAMIdxMode 代表纯内存索引模式(key和value都会被cache)。 HintKeyAndRAMIdxMode 代表内存+磁盘的索引模式(只有key被cache)。 HintBPTSparseIdxMode(v0.4.0之后的版本支持) 是专门节约内存的设计方案,单机10亿条数据,只要80几M内存。但是读性能不高,需要自己加缓存来加速。

  • RWMode RWMode

RWMode 代表读写模式. RWMode 包括两种选项: FileIO and MMap. FileIO 用标准的 I/O读写。 MMap 代表使用mmap进行读写。

  • SegmentSize int64

SegmentSize 代表数据库的数据单元,每个数据单元(文件)为SegmentSize,现在默认是8。注意:从大于0.8.0版本开始,默认SegmentSize变成256MB MB,这个可以自己配置。但是一旦被设置,下次启动数据库也要用这个配置,不然会报错。详情见 限制和警告

  • NodeNum int64

NodeNum 代表节点的号码.默认 NodeNum是 1. NodeNum 取值范围 [1,1023] 。

  • SyncEnable bool

SyncEnable 代表调用了 Sync() 方法. 如果 SyncEnable 为 false, 写性能会很高,但是如果遇到断电或者系统奔溃,会有数据丢失的风险。 如果 SyncEnable 为 true,写性能会相比false的情况慢很多,但是数据更有保障,每次事务提交成功都会落盘。

  • StartFileLoadingMode RWMode

StartFileLoadingMode 代表启动数据库的载入文件的方式。参数选项同RWMode

  • GCWhenClose bool

GCWhenClose 表示调用 db.Close() 时主动 GC。Nutsdb 预设不会立即在 db.Close() 时触发 GC.

  • CommitBufferSize int64

CommitBufferSize 表示为事务预分配的内存大小。Nutsdb 将预分配内存以减少内存分配的次数。

  • ErrorHandler ErrorHandler

ErrorHandler 处理事务执行期间发生的错误。

  • LessFunc LessFunc

LessFunc 表示对 key 进行排序的函数。Nutsdb 默认按字典序对 key 进行排序。

  • MergeInterval time.Duration

MergeInterval 表示自动化 Merge 的间隔,0 表示不触发自动化 Merge,默认间隔为 2 小时。

  • MaxBatchCount int64

MaxBatchCount 表示批量写入的最大条数。

  • MaxBatchSize int64

MaxBatchSize 表示批量写入的最大字节数。

  • HintKeyAndRAMIdxCacheSize int

HintKeyAndRAMIdxCacheSize 表示 HintKeyAndRAMIdxMode 模式下的缓存大小。设置为 0 时禁用缓存。

  • TTLConfig TTLConfig

TTLConfig 表示 TTL 扫描和批处理行为的配置(默认值:BatchSize=100, BatchTimeout=1s, QueueSize=1000)。

  • EnableHintFile bool

EnableHintFile 表示是否启用提示文件功能。

  • EnableMergeV2 bool

EnableMergeV2 表示是否启用 Merge V2 算法。

  • ListImpl ListImplementationType

ListImpl 指定 List 数据结构的实现类型(默认:ListImplBTree)。

  • EnableWatch bool

EnableWatch 表示是否启用监视功能。如果 EnableWatch 为 true,将启用监视功能。监视功能默认禁用。

  • Clock ttl.Clock

Clock 为 TTL 计算提供时间操作。如果为 nil,默认使用 RealClock。

默认选项

推荐使用默认选项的方式。兼顾了持久化+快速的启动数据库。当然具体还要看你场景的要求。

以下配置是比较保守的方式。 如果你对写性能要求比较高,可以设置SyncEnable等于false,RWMode改成MMap,写性能会得到极大提升,缺点是可能会丢数据(例如遇到断电或者系统奔溃)

var DefaultOptions = func() Options {
    return Options{
        EntryIdxMode:              HintKeyValAndRAMIdxMode,
        SegmentSize:               defaultSegmentSize,
        NodeNum:                   1,
        RWMode:                    FileIO,
        SyncEnable:                true,
        CommitBufferSize:          4 * MB,
        MergeInterval:             2 * time.Hour,
        MaxBatchSize:              (15 * defaultSegmentSize / 4) / 100,
        MaxBatchCount:             (15 * defaultSegmentSize / 4) / 100 / 100,
        HintKeyAndRAMIdxCacheSize: 0,
        TTLConfig:                 ttl.DefaultConfig(),
        EnableHintFile:            false,
        EnableMergeV2:             false,
        ListImpl:                  ListImplementationType(ListImplBTree),
        EnableWatch:               false,
        Clock:                     ttl.NewRealClock(),
    }
}()

使用事务

NutsDB为了保证隔离性,防止并发读写事务时候数据的不一致性,同一时间只能执行一个读写事务,但是允许同一时间执行多个只读事务。 从v0.3.0版本开始,NutsDB遵循标准的ACID原则。(参见限制和警告

读写事务

err := db.Update(
    func(tx *nutsdb.Tx) error {
    ...
    return nil
})

只读事务

err := db.View(
    func(tx *nutsdb.Tx) error {
    ...
    return nil
})

手动管理事务

从上面的例子看到 DB.View()DB.Update() 这两个是数据库调用事务的主要方法。他们本质上是基于 DB.Begin()方法进行的包装。他们可以帮你自动管理事务的生命周期,从事务的开始、事务的执行、事务提交或者回滚一直到事务的安全的关闭为止,如果中间有错误会返回。所以一般情况下推荐用这种方式去调用事务

这好比开车有手动挡和自动挡一样, DB.View()DB.Update()等于提供了自动档的效果。

如果你需要手动去开启、执行、关闭事务,你会用到DB.Begin()方法开启一个事务,tx.Commit() 方法用来提交事务、tx.Rollback()方法用来回滚事务

例子:

//开始事务
tx, err := db.Begin(true)
if err != nil {
    return err
}

bucket := "bucket1"
key := []byte("foo")
val := []byte("bar")

// 使用事务
if err = tx.Put(bucket, key, val, nutsdb.Persistent); err != nil {
    // 回滚事务
    tx.Rollback()
} else {
    // 提交事务
    if err = tx.Commit(); err != nil {
        tx.Rollback()
        return err
    }
}

使用buckets

buckets中文翻译过来是桶的意思,你可以理解成类似mysql的table表的概念,也可以理解成命名空间,或者多租户的概念。 所以你可以用他存不同的key的键值对,也可以存相同的key的键值对。所有的key在一个bucket里面不能重复。

例子:


key := []byte("key001")
val := []byte("val001")

bucket001 := "bucket001"
if err := db.Update(
    func(tx *nutsdb.Tx) error {
        if err := tx.Put(bucket001, key, val, 0); err != nil {
            return err
        }
        return nil
    }); err != nil {
    log.Fatal(err)
}

bucket002 := "bucket002"
if err := db.Update(
    func(tx *nutsdb.Tx) error {
        if err := tx.Put(bucket002, key, val, 0); err != nil {
            return err
        }
        return nil
    }); err != nil {
    log.Fatal(err)
}

这边注意下,这个bucket和你使用数据结构有关,不同数据索引结构,用同一个bucket,也是不同的。比如你定义了一个bucket,命名为bucket_foo,比如你要用list这个数据结构,使用 tx.RPush加数据,必须对应他的数据结构去从这个bucket_foo查询或者取出,比如用 tx.RPoptx.LRange 等,不能用tx.Get(和GetAll、Put、Delete、RangeScan等同一索引类型)去读取这个bucket_foo里面的数据,因为索引结构不同。其他数据结构如SetSorted Set同理。

下面说明下迭代buckets 和 删除bucket。它们都用到了ds

ds表示数据结构,支持如下: * DataStructureSet * DataStructureSortedSet * DataStructureBPTree * DataStructureList

目前支持的EntryIdxMode如下:

  • HintKeyValAndRAMIdxMode
  • HintKeyAndRAMIdxMode

迭代buckets

IterateBuckets支持迭代指定ds的迭代。


if err := db.View(
    func(tx *nutsdb.Tx) error {
        return tx.IterateBuckets(nutsdb.DataStructureBPTree, "*", func(bucket string) bool {
            fmt.Println("bucket: ", bucket)
            // true: continue, false: break
            return true
        })
    }); err != nil {
    log.Fatal(err)
}

删除bucket

DeleteBucket支持删除指定的bucket,需要两个参数dsbucket


if err := db.Update(
    func(tx *nutsdb.Tx) error {
        return tx.DeleteBucket(nutsdb.DataStructureBPTree, bucket)
    }); err != nil {
    log.Fatal(err)
}

使用键值对

基本操作

将key-value键值对保存在一个bucket, 你可以使用 tx.Put 这个方法:

  • 添加数据

if err := db.Update(
    func(tx *nutsdb.Tx) error {
    key := []byte("name1")
    val := []byte("val1")
    bucket := "bucket1"
    if err := tx.Put(bucket, key, val, 0); err != nil {
        return err
    }
    return nil
}); err != nil {
    log.Fatal(err)
}

  • 更新数据

上面的代码执行之后key为"name1"和value值"val1"被保存在命名为bucket1的bucket里面。

如果你要做更新操作,你可以仍然用tx.Put方法去执行,比如下面的例子把value的值改成"val1-modify":

```golang if err := db.Update( func(tx *nutsdb.Tx) error { key := []byte("name1") val := []byte("val1-modify") // 更新值 bucket := "bucket1" if err := tx.Put(bucket, key, val, 0); err != ni

Extension points exported contracts — how you extend this code

Component (Interface)
(no doc) [7 implementers]
component.go
RWManager (Interface)
RWManager represents an interface to a RWManager. [3 implementers]
internal/fileio/rwmanager.go
Clock (Interface)
Clock provides time operations for TTL calculations. All timestamps are in milliseconds for internal consistency. [2 implementers]
internal/ttl/clock.go
ListStructure (Interface)
ListStructure defines the interface for List storage implementations. It supports multiple implementations: BTree, Doubl [2 …
internal/data/list.go
ErrorHandler (Interface)
An ErrorHandler handles an error occurred during transaction.
options.go
ILogger (Interface)
(no doc) [2 implementers]
internal/utils/logger.go
IndexType (Interface)
(no doc)
index.go
WatchingFunc (FuncType)
(no doc)
watcher.go

Core symbols most depended-on inside this repo

GetTestBytes
called by 964
internal/testutils/test_utils.go
Run
called by 368
watcher.go
Update
called by 284
db.go
View
called by 199
db.go
Close
called by 192
hint_collector.go
Open
called by 167
db.go
append
called by 153
examples/k-v/append/main.go
Put
called by 123
tx_btree.go

Shape

Function 910
Method 701
Struct 104
TypeAlias 18
Interface 9
FuncType 6

Languages

Go100%

Modules by API surface

db_test.go111 symbols
tx_btree_test.go54 symbols
sorted_set.go49 symbols
internal/data/btree.go47 symbols
tx.go43 symbols
merge_v2_test.go42 symbols
db.go42 symbols
tx_btree.go41 symbols
internal/data/list.go41 symbols
internal/data/list_benchmark_test.go40 symbols
options.go33 symbols
watch_manager.go31 symbols

Dependencies from manifests, versioned

github.com/RussellLuo/timingwheelv0.0.0-2022021815271 · 1×
github.com/edsrzf/mmap-gov1.2.0 · 1×
github.com/gofrs/flockv0.13.0 · 1×
github.com/kr/textv0.2.0 · 1×
github.com/pmezard/go-difflibv1.0.0 · 1×
github.com/rogpeppe/go-internalv1.9.0 · 1×
github.com/tidwall/btreev1.8.1 · 1×
github.com/xujiajun/gorouterv1.2.0 · 1×

For agents

$ claude mcp add nutsdb \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact