Persistência de dados com Redis

Conhecendo o Redis

Redis é um banco de dados para persistência de dados no modelo Key/Value escrito em C ANSI para sistemas POSIX, com uma interface de rede embutida. Isso é o que diz no site.

O que quer dizer que Redis é um "Key/Value storage engine", parecido com o memcached mas persistente. Suporta dados em String e Intergers, Lists, Sets e Ordered Sets além de Hash Tables (que suportam apenas Strings e Intergers). Usa operações atômicas (uma alteração por vez), e suporta get/set, push/pop, add/remove, union, intersect, diffs e muito mais, incluindo opções de ordenação.

Ele mantêm, por padrão, todos os dados em memória, e salva snapshots para o disco eventualmente de forma assíncrona, ou escrevendo cada mudança para um "Append Only File" (o que é mais interessante). Ele é capaz de refazer o AOF em background. Também tem suporte trivial a replicação master-slave.

O Redis é insamente rápido para escritas e leituras(muito mesmo!), tem bibliotecas para "tipo" todas as linguagens, é desenvolvido em ritmo alucinante e não funciona bem no windows.

A prioridade para próxima versão é o projeto Redis Cluster.

Instalação e Configuração do Redis

Sendo um sistema de evolução muito rápido fique de olho no código-fonte no no site oficial e nos branches do github e pegue a ultima versão. Aqui vou usar a versão 2.0 do github, seguem passos para instalação:

$ git clone git://github.com/antirez/redis.git
$ cd redis
$ make

Pronto o sistema está construído. Você pode agora configurar o servidor redis pelo arquivo redis.conf. Alguns detalhes interessantes da configuração:

Uma boa opção é usar o Redis com AOF (append only file), a cada escrita ele vai adicionar esse dado ao log que será a forma de persistência. Basta usar a opção "appendonly" como "yes".

Importante novidade no Redis é o uso de memoria virtual (VM), como o Redis mantém tudo em memoria isso limita a capacidade de armazenamento a memoria disponível. Usando VM o Redis pode armazenar mais dados que a memoria disponível fazendo paginação de forma inteligente.

Para ativar a VM use a opção "vm-enable" como "yes". Você pode definir o máximo de memoria para a VM usando a opção "vm-maxmemory", se esse valor for zero o sistema vai manter apenas as chaves em memoria e todo os dados em disco. Tem ainda a opção do número de threads trabalhando na paginação com "vm-max-threads", você pode usar seu numero de "cores" (+2) ou 0 para ter blocking de leitura.

Usar paginação pode ser mais lento, mas ainda assim é bem rápido e permite uma capacidade maior de armazenamento. Claro, só vai ficar mais lento se usar o sistema até ser necessário paginar.

O ideal é usar o sistema com AOF para maior consistência e durabilidade (não há impacto na performance) e usar VM com um número razoável de threads, para caso seu sistema cresça bastante você não ter nenhum susto.

Você tem ainda a opção "slaveof" para torná-lo slave de outro servidor e pode definir a opção "requirepass" para usar autenticação.

Para iniciar o servidor basta executar:

$ ./redis-server

Feitas as configurações você pode rodar os testes de sistema(demora bastante!):

$ make test

E tem também um utilitário de benchmark, bom para testar diferentes configurações:

./redis-benchmark -n 1000 -q

Usando Redis

Com o servidor iniciado (./redis-server) você pode testar o redis usando o shell interativo, basta lançá-lo usando:

$ ./redis-cli

Você pode passar para o cli as operações que quer testar, ex:

$ ./redis-cli set foo "bar"
$ ./redis-cli get foo

Ou apenas usar o redis-cli para entrar em um prompt interativo.

Primeiro vamos ver um pouco dos tipos de dados do Redis.

String: O tipo mais básico, não tem mistério, são strings ou inteiros. É, inteiros também.

redis> set foo "bar"
OK
redis> get foo
"bar"
redis> set hello "Hello, "
OK
redis> append hello "Redis!"
(integer) 13
redis> mget foo hello
1."bar"
2."Hello, Redis!"
redis> substr hello 2 4
"llo"
redis> set um 1
OK
redis> incr um
(integer) 2
redis> incrby um 5
(integer) 7
redis> decr um
(integer) 6
redis> get um
"6"

List: Listas no Redis não são arrays, mas Linked Lists. São mais simples e rápidas, não são associativas, aceitam Strings (que podem ser inteiros, lembra). São muito rápidas para acesso nas pontas e para adição de itens e não tão rápidas para acessos aleatórios (pelo índice).

redis> rpush nums 1
redis> rpush nums 2
redis> lpush nums 0
redis> lrange nums 0 2
1."0"
2."1"
3."2"
redis> lrange nums 0 1
1."0"
2."1"
redis> lrange nums 0 -1
1."0"
2."1"
3."2"

Explicando um pouco, "rpush" insere um item a direita na lista (final), "lpush" a esquerda (inicio) e "lrange" retorna parte da lista.

redis> rpush msgs "Oi"
redis> rpush msgs "Meu nome"
redis> rpush msgs "É"
redis> rpush msgs "Redis!"
redis>lrange msgs 0 -1
1."Oi"
2."Meu nome"
3."É"
4."Redis!"
redis> rpop msgs
"Redis!"
redis> rpop msgs
"É"
redis> lpop msgs
"Oi"

O comando "lpop" retorna o elemento a esquerda da lista (começo) e o remove, o "rpop" o mesmo para a direita (final).

Set: É uma coleção de Strings não ordenadas.

redis> sadd users "diogok"
redis> sadd users "voce"
redis> sadd users "ninguem"
redis> smembers users
1."ninguem"
2."voce"
3."diogok"
redis> sadd admins "ninguem"
redis> sinter users admins
1."ninguem"
redis> sunion users admins
1."ninguem"
2."diogok"
3."voce"
redis> sdiff users admins
1."diogok"
2."voce"
redis> sdiff admins users
(empty list or set)
redis> srem "ninguem"

A vantagem dos sets é permitir operações de grupos como uniões (sunion), intercessões (sinter) e diferenças (sdiff).

Sorted Set: Parecido com Sets porem ordenados.

redis> zadd log 2210 "login"
redis> zadd log 2215 "view email"
redis> zadd log 2225 "view twitter"
redis>zrange log 0 -1
redis>zrevrange log 0 -1
redis> zrem log 2210

Hash: É um tipo novo (>= 2.0) que define hash tables, que contém chaves e conteúdos string.

redis> hset diogok name "Diogo"
redis> hmset diogok email "manifesto at manifesto.blog.br" password "123"
redis> hget diogok name
"Diogo"
redis> hkeys diogok
1."name"
2."email"
3."password"
redis> hvals
1."Diogo"
2."manifesto at manifesto.blog.br"
3."123"
redis> hgetall diogok
1."name"
2."Diogo"
3."email"
4."manifesto at manifesto.blog.br"
5."password"
6."123"
redis> hset diogok counter 1
redis> hincrby diogok counter 3
redis> hget diogok counter
4
redis> hdel diogok counter

Conclusão

Isso cobre o básico do uso do Redis, agora é escolher a biblioteca para sua linguagem e consultar a documentação do Redis e a lista completa de comandos.

O Redis se mostra muito interessante por se extremamente rápido, principalmente na escrita, e seus tipos de dados como set e ordered list o torna muito prático para certas aplicaçções:

  • Os Ordered Set são ótimos para listas manterem ordem temporal (noticias, tweets, logs…)
  • Os Sets servem muito bem para traçar grafos e comparar listas (amigos em comum, aprovações…)
  • Lists são perfeitos para queues ou listas de processamentos (urls para minerar, mensagens para enviar, dados a processar…)
  • Hash Tables com Strings armazenam dados genéricos de forma mais compacta.
  • Cada um desses casos tem performance excelente!

O mais importante ao adotar o Redis(ou qualquer “nosql” na verdade) é entender a nova modelagem. Um sistema em memória não é bom entulhar com dados mal formatados, deve-se retirar periodicamente esses dados para uma armazenagem secundária. Deve-se pensar também numa estratégia consisa de chaves, ex:

redis> sadd users diogok
redis> hmset users:diogok name "diogo" web "manifesto.blog.br"
redis> sadd users voce
redis> hmset users:voce name "voce" web "google.com"
redis> sadd conhecidos:diogok voce
redis> sinter conhecidos:diogok conhecidos:voce
(nenhum conhecido em comum)

Você pode ver um clone do twitter com php+redis ou uma aplicação de notas com ruby+redis e ainda um motor de busca com python+redis como exemplo.