我正在实现一个使用sqlite3数据库作为后端的FUSE文件系统。我不打算更改数据库后端,因为我的文件系统使用sqlite3作为文件格式。文件系统必须实现的功能之一是readdir函数。此功能允许进程通过重复调用目录并获取接下来的几个目录项(与缓冲区可以容纳的目录数相同)来迭代读取目录的内容。返回的目录条目可以以任何顺序返回。我想通过以下查询实现此操作:

SELECT fileno, name FROM dirents WHERE dirno = ? LIMIT -1 OFFSET ?;


其中dirno是我正在读取的目录,而OFFSET ?是我已经返回的条目数。我想读取尽可能多的行以适合缓冲区(我无法预测计数,因为这些是可变长度记录,具体取决于文件名的长度),然后重置查询。

由于FUSE的无状态性质,因此不能选择一直打开查询并返回接下来的几行直到目录结束,因为我无法可靠地检测到进程是否过早地关闭了目录。

dirents表具有以下架构:

CREATE TABLE dirents (
    dirno INTEGER NOT NULL REFERENCES inodes(inum),
    fileno INTEGER NOT NULL REFERENCES inodes(inum),
    name TEXT NOT NULL,
    PRIMARY KEY (dirno, name)
) WITHOUT ROWID;




理论上,SELECT语句不按定义顺序产生行。在实践中,我可以假设当我用连续更大的SELECT值多次执行相同的准备好的OFFSET语句时,得到的结果与在单个查询中读取所有数据时得到的结果相同,即行顺序为每次相同的未指定顺序?当前的假设是在查询之间不修改数据库。

我可以假设当不同的查询修改其间的dirents表时,行顺序保持合理地相似吗?程序当然可以观察到一些故障(例如,目录条目出现两次),但是为了可用性(readdir的主要用户是ls命令),如果目录列表通常大部分正确,则非常有用。

如果我无法做出这些假设,那么什么是达到预期结果的更好方法?

我知道我可以抛出ORDER BY子句来使行顺序定义得很好,但是我担心这可能会对性能产生重大影响,尤其是在读取大目录中的小块内容时,必须对目录进行排序每次读取一个块。

最佳答案

解决此问题的正确方法是使用order by。如果您担心订单的执行情况,请在用于order by的列上使用索引。

我认为,最简单的方法是删除表创建中的without rowid选项。然后,您可以按以下方式访问该表:

SELECT fileno, name
FROM dirents
WHERE dirno = ?
ORDER BY rowid
LIMIT -1 OFFSET ?;


我意识到这会为每行添加额外的字节,但这是出于良好的目的-确保您的查询正确。

实际上,此表的最佳索引应在dirno, rowid, fileno, name上。给定where子句,除非有索引,否则您还是要进行全表扫描。

10-06 10:32
查看更多