如何制作一个缓冲的作家?

时间:2014-01-06 19:50:57

标签: python database io buffer writer

所以最近我作为一个个人项目在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()

2 个答案:

答案 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