peterbourgon/diskv

GitHub: peterbourgon/diskv

diskv 是一个基于 Go 的磁盘持久化键值存储库,将任意数据以 key-value 形式写入文件系统并支持缓存、压缩和有序索引。

Stars: 1457 | Forks: 108

# 什么是 diskv? Diskv (disk-vee) 是一个用 Go 语言编写的简单、持久化的 key-value 存储。 它从一个极其简单的 API 开始,通过 key 将任意数据存储到文件系统中,并在此基础上构建了 多层提升性能的抽象。最终的结果是一个概念上简单,但高性能的、基于磁盘的存储系统。 [![构建状态](https://drone.io/github.com/peterbourgon/diskv/status.png)][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工具, 存储系统, 日志审计, 本地存储, 键值存储