关于后端:推荐一个超级轻量级KV存储🔥

Hi (๑╹◡╹)ノ”,各位Gopher自己最近又用Go造了一个轮子,一个超级轻量级KV存储引擎,欢送👏🏻各位Gopher进行评测。

主页介绍:https://bottle.ibyte.me (PC成果更加😋)

我的项目地址:https://github.com/auula/bottle

特 性

  • 嵌入的存储引擎
  • 数据能够加密存储
  • 能够自定义实现存储加密器
  • 即便数据文件被拷贝,也保障存储数据的平安
  • 将来索引数据结构也能够反对自定义实现

简 介

首先要阐明的是Bottle是一款KV嵌入式存储引擎,并非是一款KV数据库,我晓得很多人看到了KV认为是数据库,当然不是了,很多人会把这些搞混同掉,KV
存储能够用来存储很多货色,而并非是数据库这一畛域。能够这么了解数据库是一台汽车,那么Bottle是一台车的发动机。能够简略了解Bottle是一个对操作系统文件系统的KV抽象化封装,能够基于Bottle
做为存储层,在Bottle层之上封装一些数据结构和对外服务的协定就能够实现一个数据库。

本我的项目性能实现齐全基于 bitcask 论文所实现,另外本我的项目所用到一些常识和卡内基梅隆大学CMU 15-445: Database Systems课程内容很靠近,这门课由数据库畛域的大牛Andy Pavlo讲授,有感兴趣的敌人能够去看看这套课,如果感觉不错你能够给我按一颗小星谢谢。

装置Bottle

你只须要在你的我的项目中装置Bottle模块即可应用:

go get -u github.com/auula/bottle

根本API

如何操作一个Bottle实例代码:

package main

import (
    "fmt"
    "github.com/auula/bottle"
)

func init() {
    // 通过默认配置关上一个存储实例
    err := bottle.Open(bottle.DefaultOption)
    // 并且解决一下可能产生的谬误
    if err != nil {
        panic(err)
    }
}

// Userinfo 测试数据结构
type Userinfo struct {
    Name  string
    Age   uint8
    Skill []string
}

func main() {

    // PUT Data
    bottle.Put([]byte("foo"), []byte("66.6"))

    // 如果转成string那么就是字符串
    fmt.Println(bottle.Get([]byte("foo")).String())

    // 如果不存在默认值就是0
    fmt.Println(bottle.Get([]byte("foo")).Int())

    // 如果不胜利就是false
    fmt.Println(bottle.Get([]byte("foo")).Bool())

    // 如果不胜利就是0.0
    fmt.Println(bottle.Get([]byte("foo")).Float())

    user := Userinfo{
        Name:  "Leon Ding",
        Age:   22,
        Skill: []string{"Java", "Go", "Rust"},
    }

    var u Userinfo

    // 通过Bson保留数据对象,并且设置超时工夫为5秒,TTL超时能够不设置看需要
    bottle.Put([]byte("user"), bottle.Bson(&user), bottle.TTL(5))

    // 通过Unwrap解析出构造体
    bottle.Get([]byte("user")).Unwrap(&u)

    // 打印取值
    fmt.Println(u)

    // 删除一个key
    bottle.Remove([]byte("foo"))

    // 敞开解决一下可能产生的谬误
    if err := bottle.Close(); err != nil {
        fmt.Println(err)
    }
}

加密器

数据加密器是针对数据的value记录的,也就是针对字段级别的区块加密,并非是把整个文件加密一遍,那样设计会带来性能耗费,所以采纳区块数据段形式加密的形式。

上面例子是通过bottle.SetEncryptor(Encryptor,[]byte)
函数去设置数据加密器并且配置16位的数据加密秘钥。

func init() {
    bottle.SetEncryptor(bottle.AES(), []byte("1234567890123456"))
}

你也能够自定义去实现数据加密器的接口:

// SourceData for encryption and decryption
type SourceData struct {
    Data   []byte
    Secret []byte
}

// Encryptor used for data encryption and decryption operation
type Encryptor interface {
    Encode(sd *SourceData) error
    Decode(sd *SourceData) error
}

上面代码就是内置AES加密器的实现代码,实现bottle.Encryptor
接口即可,数据源为bottle.SourceData 构造体字段:

// AESEncryptor Implement the Encryptor interface
type AESEncryptor struct{}

// Encode source data encode
func (AESEncryptor) Encode(sd *SourceData) error {
    sd.Data = aesEncrypt(sd.Data, sd.Secret)
    return nil
}

// Decode source data decode
func (AESEncryptor) Decode(sd *SourceData) error {
    sd.Data = aesDecrypt(sd.Data, sd.Secret)
    return nil
}

具体的加密器实现代码能够查看encrypted.go

散列函数

如果你须要自定义实现散列函数,实现bottle.Hashed 接口即可:

type Hashed interface {
    Sum64([]byte) uint64
}

而后通过内置的bottle.SetHashFunc(hash Hashed) 设置即可实现你的散列函数配置。

索引大小

索引预设置的大小很大水平上会影响你的程序存取和读取数据的速度,如果在初始化的时候可能预计出程序运行时须要的索引大小,并且在初始化的时候配置好,能够减小程序在运行过程中带来的运行数据迁徙和扩容带来的性能问题。

func init() {
    // 设置索引大小 
    bottle.SetIndexSize(1000)
}

配置信息

你也能够不应用默认配置,你能够应用内置的bottle.Option 的构造体初始化你存储引擎,配置实例如下:

func init() {
        // 自定义配置信息
        option := bottle.Option{
        // 工作目录
        Directory:       "./data",
        // 算法开启加密
        Enable:          true,
        // 自定义秘钥,能够应用内置的秘钥
        Secret:          bottle.Secret,
        // 自定义数据大小,存储单位是kb
        DataFileMaxSize: 1048576,
    }
    // 通过自定义配置信息
    bottle.Open(option)
}

当然也能够应用内置的bottle.Load(path string) 函数加载配置文件启动Bottle
,配置文件格式为yaml,可配置项如下:

# Bottle config options
Enable: TRUE
Secret: "1234567890123456"
Directory: "./testdata"
DataFileMaxSize: 536870912

须要留神的是内置的加密器实现的秘钥必须是16
位,如果你是自定义实现的加密器可通过bottle.SetEncryptor(Encryptor,[]byte)
设置你自定义的加密器,那这个秘钥位数将不受限制。

数据目录

因为bottle设计就是基于但过程的程序,所以每个存储实例对应是一个数据目录,data为日志合并构造数据目录,index为索引数据版本。

日志合并构造数据目前版本是每次数据启动时候进行合并,默认是data数据文件夹下的所有数据文件占用总和超过1GB就会触发一次合并,合并之后没有用的数据被抛弃。

当然如果未达到脏数据合并要求,数据文件会以启动时候配置的大小进行归档,每个数据有版本号,并且被设置为只读挂载,过程工作目录构造如下:

./testdata
├── data
│   └── 1.data
└── index
    ├── 1646378326.index
    └── 1646378328.index

2 directories, 3 files

当存储引擎开始工作的时候,这个目录下的所以文件夹和文件只能被这个过程操作,保障数据安全。

后续保护

  • Bottle目前不反对多数据存储分区,后续版本会引入一个Bucket概念,将来能够把指定的数据存储到指定的分区中,来升高并发的时候索引锁的颗粒度。
  • 后续将引入零拷贝技术,以后文件操作很大水平上依赖于操作系统,以后文件必须sync能力保证数据一致性。
  • 脏数据合并能够在运行中进行合并整顿,基于信号量的形式告诉垃圾回收工作线程。

其余信息

如果你发现了bug欢送提issue或者发动pull request,我收到了音讯会尽快回复你,另外欢送各位Gopher提出本人意见,或者奉献做本人的代码也是能够的,另外咱们也十分欢送大家进行存储相干技术交换,不错的话别忘去Github点一个Thanks♪(・ω・)ノ。

更具体的问题能够查看:https://github.com/auula/bottle

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理