技术解析

如何修改一个大文件?
0
2021-06-11 21:49:53
idczone
假设我有一个 1G 的大文件,我要修改中间某个地方的 1 个 byte,这个时候可以用 file.seek 定位到具体的位置,再用 append 模式写。
但如果我要删除一段内容,再添加一段新的内容呢?(内容的长度不确定),这个时候是怎么做的?
一个编辑器如何做到这个功能?
一个像 sqlite 这样涉及到大量读写的数据库系统又是如何做到的?
照 posix 现有的 api 来看要么只能全读内存再调美国服务器用 fsync 写入。或者用 mmap 这种底层的 api 提高写入速度。有相关的书籍介绍这个问题的吗?
WAL 是不是解决这个问题用的?

从块文件的层面去考虑的话,长度改变后面的全部都要重新写一遍。
文件系统可能允许文件分为多个块或者簇,它们可以变长的话可能可以解决。
sqlite 说是有并发问题的…… 应当也是全读全写?可能有 padding 空间?可能允许乱序?

做不到,文本编辑器要么是重新写一遍,要么是用一个新文件替换到原有文件

感谢,我纠结了一天也没在网上找到合适的方案。原来的确是我的想法有问题。

数据库会尽量避免改变长度。可以看下 LSM 树的原理,将磁盘随机写操作转化为顺序写操作,相比 B+树的存储结构好理解一些。

长度不确定没发 in place, 关系型数据库都字段都是固定长度的。

数据库原理没有学好就是这样的。
数据库文件本身内部会有数据库软件的分页,每个分页的长度固定,然后数据库软件会在文件前部保持一个对页的索引,这样数据库的 IO 就只需要读写一个页(如果不够长会重新分配页,一般在文件尾部直接增长)。效率比重写一大截文件好很多。
但是,如果你是一个普通的二进制文件,那么你只能把后半截保存下来然后删掉重写……

有个办法是长度对齐,空余的填 0 。
然后写入就是一块一块的写入。自己计算末尾 0 的个数。
有的游戏存档是这样做的,虽然没到 1g 这么大,但是这样都是相同长度,应该是因为程序写起来方便吧。

是的,感谢指点。我确实这块没学好,赶紧学习去。如果按你这样说的,sqlite 岂不是数据库越大,性能越差?但看起来好像不是这样的,他们也没用 lsm,只是用 b 树。

这个还是会越写越慢吧?中间插入后面全部要改。

sqlite 之类的,创建一个大一些的文件,写入的数据都是从文件中预定一段然后写入,并不是顺序的写入。

是一段固定长度的 0,写入的时候就写这么长,覆盖写入。

可以用 "r+" 打开文件,seek 到要修改的地方。https://es2q.com/blog/2019/02/22/modify_file_without_rewrite/

仔细再看各位大佬的讨论,这个问题没那么简单的。

抱歉没写清楚,我回复的是 #10

1 个 G 不是大文件吧,现在内存条再怎么涨价也就 300 块钱 8G,买一个插上

SQLite 数据库确实越大性能越差,不知道你这个看起来不是这样是哪来的……BTree 就是维护的 Pager 索引,也不知道你说的只用 b 树是哪来的。

不是抬杠,官网说了最大支持 10tb,他们也确实用的是 b tree 。
写代码不能有这种想法,万一是 10g 100g 1t 的文件呢?

万一 1PB 呢

数据地带为您的网站提供全球顶级IDC资源
在线咨询
专属客服