如果数据库足够小,则可以使用文件系统。这种方法的优点是技术含量低,并且几乎不需要任何代码就可以在任何地方使用。如果键由可打印的字符组成并且不包含/
,则可以将它们用作文件名:
put () { key=$1; value=$2; printf %s "$value" >"datastore.db/$key"; }
get () { key=$1; cat "datastore.db/$key"; }
remove () { key=$1; rm "datastore.db/$key"; }
要容纳任意密钥,请使用密钥的校验和作为文件名,并可以选择存储密钥的副本(除非您对无法列出密钥或知道给定条目的密钥感到满意)。
put () {
key=$1; value=$2; set $(printf %s "$key" | sha1sum); sum=$1
printf %s "$key" >"datastore.db/$sum.key"
printf %s "$value" >"datastore.db/$sum.value"
}
get () {
key=$1; set $(printf %s "$key" | sha1sum); sum=$1
cat "datastore.db/$1.value"
}
remove () {
key=$1; set $(printf %s "$key" | sha1sum); sum=$1
rm "datastore.db/$1.key" "datastore.db/$1.value"
}
请注意,上面的玩具实现不是全部内容:它们没有任何有用的事务属性,例如原子性。但是,基本文件系统操作(例如文件创建和重命名)是原子的,并且可以构建上述功能的原子版本。
这些直接文件系统的实现仅适用于小型数据库(最多数千个文件)的典型文件系统。除此之外,大多数文件系统都很难应付大型目录。您可以使用分层布局使方案适用于较大的数据库。例如,不是将所有文件存储在一个目录中,而是根据文件名的前几个字符将它们存储在单独的子目录中。例如,这就是git的作用:它的对象(由SHA-1哈希索引)存储在名为的文件中.git/objects/01/2345679abcdef0123456789abcdef01234567
。使用语义分层的程序的其他例子是Web缓存代理Wwwoffle和polipo ; 都将在URL处找到的页面的缓存副本存储在名为www.example.com/HASH
其中HASH是URL哈希的某种编码。¹
效率低下的另一个原因是,大多数文件系统在存储小文件时会浪费大量空间-在典型的文件系统上,每个文件最多浪费2kB的空间,而与文件大小无关。
如果选择使用真实数据库,则无需放弃透明文件系统访问的便利。有几种FUSE文件系统可以访问数据库,包括Berkeley DB(使用Jeff Garzik的dbfs),Oracle(使用Oracle DBFS),MySQL(使用mysqlfs)等。
¹
对于类似的URL http://unix.stackexchange.com/questions/21943/standard-key-value-datastore-for-unix
,Polipo使用文件unix.stackexchange.com/M0pPbpRufiErf4DLFcWlhw==
,文件内添加了一个标头,以明文形式表示实际的URL;文件名是URL的MD5哈希(二进制)的base64编码。Wwwoffle使用文件http/unix.stackexchange.com/DM0pPbpRufiErf4DLFcWlhw
; 文件名是MD5哈希的本地编码,并且伴随文件http/unix.stackexchange.com/UM0pPbpRufiErf4DLFcWlhw
包含URL。