Se o seu banco de dados for pequeno o suficiente, você poderá usar o sistema de arquivos. A vantagem dessa abordagem é que ela é de baixa tecnologia e funciona em todos os lugares com muito pouco código. Se as chaves forem compostas de caracteres imprimíveis e não contiverem /
, você poderá usá-las como nomes de arquivos:
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"; }
Para acomodar chaves arbitrárias, use uma soma de verificação da chave como nome do arquivo e, opcionalmente, armazene uma cópia da chave (a menos que você esteja satisfeito em não poder listar as chaves ou dizer qual é a chave para uma determinada entrada).
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"
}
Observe que as implementações de brinquedos acima não são a história completa: elas não têm nenhuma propriedade transacional útil , como atomicidade . As operações básicas do sistema de arquivos, como criação e renomeação de arquivos, são atômicas, e é possível construir versões atômicas das funções acima.
Essas implementações diretas para o sistema de arquivos são adequadas para sistemas de arquivos típicos apenas para bancos de dados pequenos, até alguns milhares de arquivos. Além desse ponto, a maioria dos sistemas de arquivos tem dificuldade em lidar com grandes diretórios. Você pode adaptar o esquema a bancos de dados maiores usando um layout em camadas. Por exemplo, em vez de armazenar todos os arquivos em um diretório, armazene-os em subdiretórios separados com base nos primeiros caracteres de seus nomes. Isto é o que o git faz, por exemplo: seus objetos, indexados por hashes SHA-1, são armazenados em arquivos chamados .git/objects/01/2345679abcdef0123456789abcdef01234567
. Outros exemplos de programas que usam camadas semânticas são os proxies de cache da web Wwwoffle e polipo ; ambos armazenam a cópia em cache de uma página encontrada em uma URL em um arquivo chamado www.example.com/HASH
, em que HASH é alguma codificação de algum hash da URL.¹
Outra fonte de ineficiência é que a maioria dos sistemas de arquivos desperdiça muito espaço ao armazenar arquivos pequenos - há um desperdício de até 2kB por arquivo em sistemas de arquivos típicos, independentemente do tamanho do arquivo.
Se você optar por um banco de dados real, não precisará renunciar à conveniência do acesso transparente ao sistema de arquivos. Existem vários sistemas de arquivos FUSE para acessar bancos de dados incluindo o Berkeley DB (com fuse-dbfs ), MySQL (com mysqlfs ), etc.
Para um URL como http://unix.stackexchange.com/questions/21943/standard-key-value-datastore-for-unix
, Polipo usa o arquivo unix.stackexchange.com/M0pPbpRufiErf4DLFcWlhw==
, com um cabeçalho adicionado dentro do arquivo indicando o URL real em texto não criptografado; o nome do arquivo é a codificação base64 do hash MD5 (em binário) da URL. Wwwoffle usa o arquivo http/unix.stackexchange.com/DM0pPbpRufiErf4DLFcWlhw
; o nome do arquivo é uma codificação caseira do hash MD5 e um arquivo complementar http/unix.stackexchange.com/UM0pPbpRufiErf4DLFcWlhw
contém o URL.