所以最近我作为一个个人项目在Python中制作我自己的数据库,主要是因为我讨厌与大多数数据库混淆,我需要一些易于设置,便携且易于研究大型数据集的东西。
我现在发现自己陷入了一个问题,一种从DB文件中删除一行的有效方法(实际上只是一个文本文件)。我发现这样做的方法是在它之前的行之后写出所有内容,然后truncate
文件(我会采取更好的方法来做这些建议)。当我需要在它之前的行之后写入内容时,问题就出现了,因为一次完成所有操作可能会立即将数百万行写入RAM。代码如下:
ln = 11 # Line to be deleted
with open("test.txt", "r+") as f:
readlinef = f.readline
for i in xrange(ln):
line = readlinef()
length, start = (len(line), f.tell()-len(line))
f.seek(0, 2)
chunk = f.tell() - start+length
f.seek(start+length, 0)
# How to make this buffered?
data = f.read(chunk)
f.seek(start, 0)
f.write(data)
f.truncate()
现在可以一次读取所有数据,我如何使最后一个代码块以缓冲方式工作?每次在它之前写入一大块新数据时,开始位置都会切换,我想知道最有效和最快速(执行时间)的方法是什么。
提前致谢。
修改
我已经决定按照这里提出的建议,但出于好奇的考虑,我找到了一种读取和写入块的方法。它如下:
with open("test.txt", "r+") as f:
readlinef = f.readline
for i in xrange(ln):
line = readlinef()
start, length = (f.tell()-len(line), len(line))
readf = f.read
BUFFER_SIZE = 1024 * 1024
x = 0
chunk = readf(BUFFER_SIZE)
while chunk:
f.seek(start, 0)
f.write(chunk)
start += BUFFER_SIZE
f.seek(start+length+(x*BUFFER_SIZE), 0)
chunk = readf(BUFFER_SIZE)
f.truncate()
答案 0 :(得分:2)
回答你的问题“我该怎么做?”关于指数和真空。
免责声明:这是一个非常简单的示例,与现有的DBMS无法比较,我强烈反对。
基本理念:
对于数据库中的每个表,保留各种文件,一些用于对象ID(行ID,记录ID)和一些(页面文件)与实际数据。我们假设每条记录的长度都是可变的。
每条记录都有一个表唯一的OID。这些存储在oid文件中。我们将表命名为“test”,将oid文件命名为“test.oidX”。 oid文件中的每条记录都是固定长度的,每个oid文件都是固定长度的。
现在如果“test.oid1”读取:
0001:0001:0001:0015 #oid:pagefile:position:length
0002:0001:0016:0100
0004:0002:0001:0001
这意味着记录1在页面文件1中,位置1并且具有长度15.记录2在页面文件1中位于长度为100的位置16,等等。
现在,当您要删除记录时,只需触摸oid文件即可。例如。删除记录2,编辑为:
0001:0001:0001:0015
0000:0001:0016:0100 #0000 indicating empty cell
0004:0002:0001:0001
甚至不用打扰你的页面文件。
这将在您的页面文件中创建漏洞。现在,您需要实现一些“维护”例程,该例程可以移动页面文件中的块等,这些块可以在用户请求时运行,也可以在DBMS没有其他任何操作时自动执行。根据您使用的锁定策略,您可能需要锁定相关记录或整个表格。
此外,当您插入新记录并找到足够大的孔时,您可以将其插入那里。
如果您的oid文件也应该用作索引(慢速插入,快速查询),您将需要重建它(当然在插入时,可能在删除时)。
对oid文件的操作应该很快,因为它们是固定长度和固定长度的记录。
这只是一个非常基本的想法,不涉及搜索树,散列等主题等。
答案 1 :(得分:1)
您可以采用与(有效)memmove
相同的方式执行此操作:在源范围和目标范围之间来回搜索:
count = (size+chunksize-1) // chunk size
for chunk in range(count):
f.seek(start + chunk * chunksize + deleted_line_size, 0)
buf = f.read(chunksize)
f.seek(start + chunk * chunksize, 0)
f.write(buf)
使用临时文件和shutil
使其更加简单 - 尽管您期望得到它,但它实际上可能更快。 (这是写作的两倍,但是寻求的却少得多,而且大多是块对齐的写作。)例如:
with tempfile.TemporaryFile('w') as ftemp:
shutil.copyfileobj(ftemp, f)
ftemp.seek(0, 0)
f.seek(start, 0)
shutil.copyfileobj(f, ftemp)
f.truncate()
但是,如果你的文件足够大以适应你的虚拟内存空间(它们可能位于64位的土地上,但可能不在32位的土地上),那么{{1}可能更简单一些该文件让OS / libc负责工作:
mmap