peterbourgon/diskv
GitHub: peterbourgon/diskv
diskv 是一个基于 Go 的磁盘持久化键值存储库,将任意数据以 key-value 形式写入文件系统并支持缓存、压缩和有序索引。
Stars: 1457 | Forks: 108
# 什么是 diskv?
Diskv (disk-vee) 是一个用 Go 语言编写的简单、持久化的 key-value 存储。
它从一个极其简单的 API 开始,通过 key 将任意数据存储到文件系统中,并在此基础上构建了
多层提升性能的抽象。最终的结果是一个概念上简单,但高性能的、基于磁盘的存储系统。
[][2]
# 安装说明
安装 [Go 1][3],可以选择[从源码安装][4]或[使用预编译二进制文件][5]。
然后,
```
$ go get github.com/peterbourgon/diskv/v3
```
# 用法
```
package main
import (
"fmt"
"github.com/peterbourgon/diskv/v3"
)
func main() {
// Simplest transform function: put all the data files into the base dir.
flatTransform := func(s string) []string { return []string{} }
// Initialize a new diskv store, rooted at "my-data-dir", with a 1MB cache.
d := diskv.New(diskv.Options{
BasePath: "my-data-dir",
Transform: flatTransform,
CacheSizeMax: 1024 * 1024,
})
// Write three bytes to the key "alpha".
key := "alpha"
d.Write(key, []byte{'1', '2', '3'})
// Read the value back out of the store.
value, _ := d.Read(key)
fmt.Printf("%v\n", value)
// Erase the key+value from the store (and the disk).
d.Erase(key)
}
```
更复杂的示例可以在 "examples" 子目录中找到。
# 原理
## 基本思想
本质上,diskv 是一个将 key (`string`) 映射到任意数据 (`[]byte`) 的 map。
数据会被写入到磁盘上的单个文件中,文件名与 key 相同。
key 决定了该文件的存储位置,这是通过用户提供的
`TransformFunc` 实现的,该函数接收一个 key 并返回一个切片 (`[]string`),
对应于该 key 文件将要存储的路径列表。最简单的
TransformFunc,
```
func SimpleTransform (key string) []string {
return []string{}
}
```
会将所有的 key 放在同一个基础目录中。这个设计灵感来源于
[Redis diskstore][6];在 content-addressable-storage 示例中提供了一个模拟默认 diskstore
行为的 TransformFunc。
**请注意**,您的 TransformFunc 应该确保一个有效的 key 不会
转换为另一个有效 key 的子集。也就是说,不应该存在
构造出解析为目录名称的有效 key 的可能。举个具体的例子,
如果您的 TransformFunc 每隔 3 个字符进行分割,那么
```
d.Write("abcabc", val) // OK: written to /abc/abc/abcabc
d.Write("abc", val) // Error: attempted write to /abc/abc, but it's a directory
```
这将会在即将发布的 diskv 版本中得到解决。
diskv 背后最重要的设计原则可能是,您的数据
始终以扁平的形式存在于磁盘上。diskv 绝不会做任何
阻止您通过常见的 UNIX 命令行工具来访问、复制、备份
或以其他方式与您的数据进行交互的事情。
## 高级路径转换
如果您需要对写入磁盘的文件名进行更多控制,或者想在 key 名称中支持斜杠或特殊字符,您可以使用
AdvancedTransform 属性。您必须提供一个函数,该函数返回
一个特殊的 PathKey 结构,它是路径和文件名的分解。返回的字符串
必须清除任何斜杠或特殊字符:
```
func AdvancedTransformExample(key string) *diskv.PathKey {
path := strings.Split(key, "/")
last := len(path) - 1
return &diskv.PathKey{
Path: path[:last],
FileName: path[last] + ".txt",
}
}
// If you provide an AdvancedTransform, you must also provide its
// inverse:
func InverseTransformExample(pathKey *diskv.PathKey) (key string) {
txt := pathKey.FileName[len(pathKey.FileName)-4:]
if txt != ".txt" {
panic("Invalid file found in storage folder!")
}
return strings.Join(pathKey.Path, "/") + pathKey.FileName[:len(pathKey.FileName)-4]
}
func main() {
d := diskv.New(diskv.Options{
BasePath: "my-data-dir",
AdvancedTransform: AdvancedTransformExample,
InverseTransform: InverseTransformExample,
CacheSizeMax: 1024 * 1024,
})
// Write some text to the key "alpha/beta/gamma".
key := "alpha/beta/gamma"
d.WriteString(key, "¡Hola!") // will be stored in "/alpha/beta/gamma.txt"
fmt.Println(d.ReadString("alpha/beta/gamma"))
}
```
## 添加缓存
内存缓存层是通过将 BasicStore
的功能与简单的 map 结构相结合来提供的,并会根据需要使其保持最新。
由于 Go 中的 map 结构不是线程安全的,因此它被与 RWMutex 结合使用,以提供安全的并发访问。
## 添加顺序
diskv 是一个 key-value 存储,因此本质上是无需的。可以通过传入满足
diskv.Index 接口的内容,将一个排序系统注入到 store 中。(提供了一个使用 Google 的
[btree][7] 包的默认实现。)基本上,diskv 会维护一个有序的(通过用户提供的
Less 函数)key 索引,该索引可以被查询。
## 添加压缩
在创建 store 时,可以传入实现了 diskv.Compression 接口的对象,
以便所有的写入和读取操作都通过压缩/解压缩 pipeline 进行过滤。提供了几种使用标准库压缩算法的
默认实现。请注意,缓存的是压缩后的数据;
每次读取时都需要承担解压缩的成本。
## 流式传输
diskv 现在还提供了 ReadStream 和 WriteStream 方法,以便高效地处理非常大的
数据。
# 未来计划
* 需要大量健壮的测试:庞大的数据集等……
* 更彻底的基准测试
* 您对我未曾想到的用例的建议
# 致谢与贡献
最初的想法、设计和实现:[Peter Bourgon](https://github.com/peterbourgon)
其他合作者:[Javier Peletier](https://github.com/jpeletier) ([Epic Labs](https://www.epiclabs.io))
标签:EVTX分析, Go, Ruby工具, 存储系统, 日志审计, 本地存储, 键值存储