go嵌入式数据库NutsDB

NutsDB简介

NutsDB是纯Go语言编写一个简单、高性能、内嵌型、持久化的key-value数据库。

安装

go get -u github.com/xujiajun/nutsdb

简单使用

    package main
    import (
        "log"
        "github.com/xujiajun/nutsdb"
    )
    func main() {
        opt := nutsdb.DefaultOptions
        opt.Dir = "/tmp/nutsdb" //这边数据库会自动创建这个目录文件
        db, err := nutsdb.Open(opt)
        if err != nil {
            log.Fatal(err)
        }
        defer db.Close()
        ...
    }
  • 要打开数据库需要使用nutsdb.Open()这个方法。
  • Dir 代表数据库存放数据的目录

事务

读写事务

    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.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, 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)
    }

使用键值对

    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)
    }

    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 != nil {
            return err
        }
        return nil
    }); err != nil {
        log.Fatal(err)
    }

    if err := db.View(
    func(tx *nutsdb.Tx) error {
        key := []byte("name1")
        bucket: = "bucket1"
        if e, err := tx.Get(bucket, key); err != nil {
            return err
        } else {
            fmt.Println(string(e.Value)) // "val1-modify"
        }
        return nil
    }); err != nil {
        log.Println(err)
    }

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

TTL(存活时间)

NusDB支持TTL(存活时间)的功能,可以对指定的bucket里的key过期时间的设置。使用tx.Put这个方法的使用ttl参数就可以了。

    // 如果设置 ttl = 0 or Persistent, 这个key就会永久不删除
    // 这边 ttl = 60 , 60s之后就会过期。
    if err := tx.Put(bucket, key, val, 60); err != nil {
        return err
    }

扫描

前缀扫描

对于前缀的扫描,我们可以用PrefixScan 方法, 使用参数 limitNum 来限制返回的结果的数量,比方下面例子限制100个entries:

    if err := db.View(
        func(tx *nutsdb.Tx) error {
            prefix := []byte("user_")
            // 限制 100 entries 返回 
            if entries, err := tx.PrefixScan(bucket, prefix, 100); err != nil {
                return err
            } else {
                keys, es := nutsdb.SortedEntryKeys(entries)
                for _, key := range keys {
                    fmt.Println(key, string(es[key].Value))
                }
            }
            return nil
        }); err != nil {
            log.Fatal(err)
    }

范围扫描

对于范围的扫描,我们可以用 RangeScan 方法.

    if err := db.View(
        func(tx *nutsdb.Tx) error {
            // 假设用户key从 user_0000000 to user_9999999.
            // 执行区间扫描类似这样一个start和end作为主要参数.
            start := []byte("user_0010001")
            end := []byte("user_0010010")
            bucket:= []byte("user_list)
            if entries, err := tx.RangeScan(bucket, start, end); err != nil {
                return err
            } else {
                keys, es := nutsdb.SortedEntryKeys(entries)
                for _, key := range keys {
                    fmt.Println(key, string(es[key].Value))
                }
            }
            return nil
        }); err != nil {
        log.Fatal(err)
    }

数据库备份

对于数据库的备份,你可以调用 db.Backup()方法,只要提供一个备份的文件目录地址即可。这个方法执行的是一个热备份,不会阻塞到数据库其他的只读事务操作,对写(读写)事务会有影响。

    err = db.Backup(dir)
    if err != nil {
       ...
    }

List

RPush: 从指定bucket里面的指定队列key的右边入队一个或者多个元素val

LPush: 从指定bucket里面的指定队列key的左边入队一个或者多个元素val。

LPop : 从指定bucket里面的指定队列key的左边出队一个元素,删除并返回。

LPeek: 从指定bucket里面的指定队列key的左边出队一个元素返回不删除。

RPop:  从指定bucket里面的指定队列key的右边出队一个元素,删除并返回。

RPeek: 从指定bucket里面的指定队列key的右边出队一个元素返回不删除。

LRange: 返回指定bucket里面的指定队列key列表里指定范围内的元素。 start 和 end 偏移量都是基于0的下标,即list的第一个元素下标是0(list的表头),第二个元素下标是1,以此类推。

LRem: 从指定bucket里面的指定的key的列表里移除前 count 次出现的值为 value 的元素。count > 0: 从头往尾移除值为 value 的元素。count < 0: 从尾往头移除值为 value 的元素。count = 0: 移除所有值为 value 的元素。

LSet: 设置指定bucket的指定list的index位置的的值为value。

LSize: 返回指定bucket下指定key列表的size大小

实例:

    if err := db.Update(
        func(tx *nutsdb.Tx) error {
                bucket := "bucketForList"
            key := []byte("myList")
            if size,err := tx.LSize(bucket, key); err != nil {
                return err
            } else {
                fmt.Println("myList size is ",size)
            }
            return nil
        }); err != nil {
        log.Fatal(err)
    }

Set

SAdd: 添加一个指定的member元素到指定bucket的里的指定集合key中。

SAreMembers: 返回多个成员member是否是指定bucket的里的指定集合key的成员。

SCard: 返回指定bucket的指定的集合key的基数 (集合元素的数量)。

SDiffByOneBucket: 返回一个集合与给定集合的差集的元素。这两个集合都在一个bucket中。

SDiffByTwoBuckets: 返回一个集合与给定集合的差集的元素。这两个集合分别在不同bucket中。

SHasKey:判断是否指定的集合在指定的bucket中。

SIsMember: 返回成员member是否是指定bucket的存指定key集合的成员。

SMembers: 返回指定bucket的指定key集合所有的元素。

SMoveByOneBucket: 将member从source集合移动到destination集合中,其中source集合和destination集合均在一个bucket中。

SMoveByTwoBuckets: 将member从source集合移动到destination集合中。其中source集合和destination集合在两个不同的bucket中。

SPop: 从指定bucket里的指定key的集合中移除并返回一个或多个随机元素。

SRem: 在指定bucket里面移除指定的key集合中移除指定的一个或者多个元素。

SUnionByOneBucket: 返回指定一个bucket里面的给定的两个集合的并集中的所有成员。

SUnionByTwoBuckets: 返回指定两个bucket里面的给定的两个集合的并集中的所有成员。

实例

    key1 := []byte("mySet1") // 集合1
    key2 := []byte("mySet2") // 集合2
    bucket := "bucketForSet"
    if err := db.Update(
        func(tx *nutsdb.Tx) error {
            return tx.SAdd(bucket, key1, []byte("a"), []byte("b"), []byte("c"))
        }); err != nil {
        log.Fatal(err)
    }
    if err := db.Update(
        func(tx *nutsdb.Tx) error {
            return tx.SAdd(bucket, key2, []byte("c"), []byte("d"))
        }); err != nil {
        log.Fatal(err)
    }
    if err := db.View(
        func(tx *nutsdb.Tx) error {
            if items, err := tx.SDiffByOneBucket(bucket, key1, key2); err != nil {
                return err
            } else {
                fmt.Println("SDiffByOneBucket:", items)
                for _, item := range items {
                    fmt.Println("item", string(item))
                }
                //item a
                //item b
            }
            return nil
        }); err != nil {
        log.Fatal(err)
    }

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×