Performance 如何有效地在文件中插入字节片?
为了好玩,我正在建立一个简单的键值存储。现在,我正在寻找一种在文件中插入切片的有效方法 我目前的做法是:Performance 如何有效地在文件中插入字节片?,performance,file,go,Performance,File,Go,为了好玩,我正在建立一个简单的键值存储。现在,我正在寻找一种在文件中插入切片的有效方法 我目前的做法是: 找到所需的偏移量 将所需插入点之前的块存储在缓冲区中 将字节片附加到该缓冲区 附加文件的其余部分 写入磁盘 问题是: 并不是说整个文件都可以放在内存中 这是低效的 我查看了可用的库,遗憾的是,我找到的最匹配的os.WriteAt覆盖了以下块。例如: import "os" func main() { pathToFile := "./tmp" bufferToWri
- 找到所需的偏移量
- 将所需插入点之前的块存储在缓冲区中
- 将字节片附加到该缓冲区
- 附加文件的其余部分
- 写入磁盘
- 并不是说整个文件都可以放在内存中
- 这是低效的
os.WriteAt
覆盖了以下块。例如:
import "os"
func main() {
pathToFile := "./tmp"
bufferToWrite := []byte{255, 255, 255, 255, 255}
f, _ := os.OpenFile(pathToFile, os.O_CREATE|os.O_RDWR, os.PermMode)
defer f.Close()
f.Write(bufferToWrite)
因此,此时tmp
的内容将是(在$:xxd-g1-btmp
之后):
11111111111111111111111(x)11111111111111111
让我们尝试插入偏移量为3(x)的对象:
输出将是:
111111111111111111111110000000000000000
我希望它是:
11111111111111111111111 00000000 00000000 11111111111111111111111
有什么想法吗?您可以从Git复制pack文件格式,而不是发明自己的文件格式 基本思想是有一个索引文件和一个数据文件。当您想插入一个切片时,只需将其附加到数据文件中。然后更新通常较小的索引文件。请注意,包文件不是为实时更新而设计的,而是伴随着单个对象文件
或者看看Berkeley DB文件格式。编写一个新文件然后替换旧文件要容易得多(通常更安全、更高效),而不是试图插入数据并移动整个文件的其余部分。我有点担心每次用户希望插入新文件(键、值)时都会频繁创建新文件或者更新一个现有的,因为我认为这会增加这些操作的延迟太多。虽然我不是专家,但我知道磁盘上的I/O最好保持在最低限度?问题在于将值“插入”到文件中的概念——常规文件没有插入操作。您所做的任何“插入”都将平均重写50%的文件,如果在重写过程中出错,您将丢失数据。如果您需要经常在存储中的任意点插入内容,您可能正在寻找某种类型的数据库。@JimB谢谢-这正是我正在尝试做的(构建数据库)。这只是为了好玩,虽然它不应该成为生产准备;这只是为了学习,然后你需要从一些用于数据库存储的数据结构开始。B树、索引文件等。谢谢罗兰,我会调查的。我会把你的答案写进一对夫妇中——现在就把它打开,这样人们仍然可以发表他们的建议。现在我想的是有一个如下的索引文件:[index:4字节][offset:2字节],其中
offset
是条目在另一个文件中的位置。这比处理价值观要好得多。你怎么看?忘了这一点:我想稍后允许用户提供一个比较器函数,根据它他们可以对其key:value
进行排序。现在,index
将只是一个uint32
,我根据相应值的二进制表示来计算。您的数据结构取决于键和值的大小。对于值,我通常保留32位,对于偏移量保留64位。但是,这样一个索引项的总大小应该是二的幂,所以我会选择{offset uint64;size uint32;reserved uint32}
@erwanaron:有无数的出版物和课程涵盖这些内容。在随机尝试创建数据库格式之前做一些研究可能是好的,因为这是一个非常广泛的主题。@JimB我不能向学术论文提出关于设计或良好实践的问题。如果你不想回答,那很好,但正如你所说,这是一个如此广泛的话题。。。我看不出向经验丰富、知识渊博的人征求意见有什么害处。目标不是构建下一个MySQL。只是为了让我学习并获得更多使用低级I/O的经验。
bufferToInsert := []byte{0, 0}
f.WriteAt(bufferToInsert, 3)
}