我正在实现一个FUSE文件系统,该系统在其后端使用sqlite3数据库。我不打算更改数据库后端,因为我的文件系统使用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
子句来使行顺序定义明确,但我担心这可能会对性能产生很大影响,尤其是在读取大型目录的小块时 - 每次读取一个块时都必须对目录进行排序。
答案 0 :(得分:2)
此问题的正确解决方案是使用order by
。如果您担心订单的性能,请使用用于order by
的列的索引。
在我看来,最简单的方法是删除表创建中的without rowid
选项。然后您只需访问该表:
SELECT fileno, name
FROM dirents
WHERE dirno = ?
ORDER BY rowid
LIMIT -1 OFFSET ?;
我意识到这会为每一行添加额外的字节,但它有一个很好的用途 - 确保您的查询正确。
实际上,此表的最佳索引是dirno, rowid, fileno, name
。给定where
子句,无论如何,您都要进行全表扫描,除非您有索引。
答案 1 :(得分:0)
如果我向ORDER BY name
查询添加SELECT
子句,sqlite3会为查询生成几乎相同的(除了散列Noop
)字节码,但保证行顺序:
sqlite> EXPLAIN SELECT fileno, name FROM dirents WHERE dirno = ? LIMIT -1 OFFSET ?;
addr opcode p1 p2 p3 p4 p5 comment
---- ------------- ---- ---- ---- ------------- -- -------------
0 Init 0 21 0 00
1 Integer -1 1 0 00
2 Variable 2 2 0 00
3 MustBeInt 2 0 0 00
4 SetIfNotPos 2 2 0 00
5 Add 1 2 3 00
6 SetIfNotPos 1 3 -1 00
7 OpenRead 1 3 0 k(2,nil,nil) 02
8 Variable 1 4 0 00
9 IsNull 4 19 0 00
10 Affinity 4 1 0 D 00
11 SeekGE 1 19 4 1 00
12 IdxGT 1 19 4 1 00
13 IfPos 2 18 1 00
14 Column 1 2 5 00
15 Column 1 1 6 00
16 ResultRow 5 2 0 00
17 DecrJumpZero 1 19 0 00
18 Next 1 12 0 00
19 Close 1 0 0 00
20 Halt 0 0 0 00
21 Transaction 0 0 3 0 01
22 TableLock 0 3 0 dirents 00
23 Goto 0 1 0 00
sqlite> EXPLAIN SELECT fileno, name FROM dirents WHERE dirno = ? ORDER BY name LIMIT -1 OFFSET ?;
addr opcode p1 p2 p3 p4 p5 comment
---- ------------- ---- ---- ---- ------------- -- -------------
0 Init 0 22 0 00
1 Noop 0 0 0 00
2 Integer -1 1 0 00
3 Variable 2 2 0 00
4 MustBeInt 2 0 0 00
5 SetIfNotPos 2 2 0 00
6 Add 1 2 3 00
7 SetIfNotPos 1 3 -1 00
8 OpenRead 2 3 0 k(2,nil,nil) 02
9 Variable 1 4 0 00
10 IsNull 4 20 0 00
11 Affinity 4 1 0 D 00
12 SeekGE 2 20 4 1 00
13 IdxGT 2 20 4 1 00
14 IfPos 2 19 1 00
15 Column 2 2 5 00
16 Column 2 1 6 00
17 ResultRow 5 2 0 00
18 DecrJumpZero 1 20 0 00
19 Next 2 13 0 00
20 Close 2 0 0 00
21 Halt 0 0 0 00
22 Transaction 0 0 3 0 01
23 TableLock 0 3 0 dirents 00
24 Goto 0 1 0 00
所以我想我会选择ORDER BY name
条款。