O Redis é um banco de dados NoSQL escrito em C. O Remote Dictionary Server é um banco de dados chave-valor cujo o armazenamento é feito na memória, fazendo com que a escrita e a leitura sejam rápidos.

Mas qual a diferença entre ele o cache? O que acontece quando o banco cai? Haverá perda total dos dados? O objetivo desse artigo é falar um pouco desse banco de dados e demonstrar o seu uso prático utilizando um pequeno projeto open source, o redis-collections.

Considerando que as ferramentas de caches, como memcache e infinitspam, também possui esse comportamento de chave-valor, a primeira dúvida normal de um desenvolvedor é saber qual a diferença entre os dois. A primeira diferença está na serialização dos objetos valores: enquanto os caches escritos em Java são serializados de forma binária, com o Kryo ou o java.io.Serializable, por exemplo, para o redis tudo é texto. Um problema da serialização binária é a impossibilidade de realizar alterações de forma manual como em um texto. Ao usar o Kryo ou o java.io.Serializable, caso haja uma alteração significativa dentro da estrutura do objeto, não será mais possível deserializar tal estrutura no novo objeto, ou seja, ao retornar para a estrutura anterior ou descartar as informações. A razão disso é que os binários tendem a ser mais rápido para leitura e escrita e mais compactos, e os caches não são feitos para perdurar por longo período de tempo, A grande maioria dura enquanto o programa está rodando.

Nota: Entre de uma vez por todas no mercado de desenvolvimento em Java com os Cursos DevMedia.

Outra diferença está no tipo de valores: enquanto o cache trata apenas chave e valor, sendo que o valor é um objeto, no redis existem diferentes estruturas de dados no lado do valor. São eles:

  • String chave e String valor;
  • List: uma coleção de String ordenado de acordo com a ordem de inserção, semelhante a coleção java.util.LinkedList;
  • Set: coleção única de string não ordenadas, semelhante ao java.util.Set;
  • Sorted Set: similar ao set, por não permitir valores duplicados, mas cada elemento está associado para um valor flutuante, chamado de score. Esses elementos são sempre ordenados por esse campo score;
  • Hashser: Composto de campos e seus respectivos valores (ambos são String), semelhante ao Map.

Além desses que são os mais conhecidos, existem também o Bit arrays e HyperLog que não serão abordados nesse artigo.

Uma vez explicando as diferenças básicas entre o Redis e um cache, o próximo passo será a instalação do banco que é bastante simples:

  1. Realizar o Download aqui;
  2. Descompartar, abrir no terminal do redis e em seguida compilar o redis, conforme o código abaixo:
    
    tar zxvf redis-versao.x.tar.gz
    cd redis-versao.x
    make
  3. Uma vez compilado, entrar na pasta src e executar o servidor, conforme o código abaixo:
    
    cd src
    ./redis-server

Pronto, o Redis está rodando. Para realizar alguns testes, basta executar o cliente que já vem com o ele. Para isso, basta ir em REDIS_HOME/src e em seguida executar o comando ./redis-cli.

Para saber mais sobre os comandos, acesse o Site da redis

Uma vez instalado e testado, o nosso objetivo agora será a utilização de algumas estruturas de dados em cima do redis. Nosso objetivo será a utilização e a implementação de java.util.List, java.util.Set, java.util.Queue e java.util.Map, além de mais três estruturas: o conceito de chave e valor (semelhante ao cache), uma para contador e outra para fazer Ranking, sorted Set.

Ao idealizar a API, vamos ter o mesmo código da Listagem 1.


public interface keyValueRedisStructure<T> {

    T get(String key);
    
    void set(String key, T bean);

    List<T> multiplesGet(Iterable<String> keys);

    void delete(String key);
}


public interface CountStructure<T extends Number> {

    T get();
    
    T increment();
    
    T increment(T count);
    
    T decrement();
    
    T decrement(T count);
    
    void delete();
    
    void expires(int ttlSeconds);
    
    void persist();
}

public interface ListStructure <T> extends Expirable {

    List<T> get(String key);
    
    void delete(String key);
}

public interface MapStructure <T> extends Expirable{


    Map<String, T> get(String key);
    
    void delete(String key);
    
}


public interface QueueStructure <T> extends Expirable {


    Queue<T> get(String key);
    
    void delete(String key);

}


public interface RankingStructure<T extends Number> extends Expirable {

    ScoresPoint<T> create(String key);

    void delete(String key);
}
Listagem 1. Definição das estruturas

Uma vez vendo a estrutura, uma das dúvidas é o Expirable, devido a ideia em cima da chave. Conseguimos definir o tempo de vida em segundos do respectivo valor e para anular esse tempo de expiração existe o comando persist. Vale lembrar que mesmo sendo em memória, o redis de tempos em tempos realiza backup das informações e, dessa forma, caso o banco caia as informações serão mantidas. Como dito anteriormente, o redis trata como a chave e os valores como String. Dessa forma, para seralizar e desearlizar o objetivo precisar ser em texto. Por ser mais “leve”, a estrutura escolhida será o JSON utilizando o framework de leitura e escrita de JSON do Google - o GSON, mas poderia ser qualquer outra forma, por exemplo, o XML tem campos separados por pipe “|” etc. E para se comunicar com o redis será utilizado o Jedis.

Porque java.util?

As coleções dentro do java.util certamente são conhecidas pela grande maioria dos desenvolvedores Java. Dessa forma, não existiria dificuldade destes para a utilizarem.

A grande diferença entre as implementações dentro do JDK e a dessas coleções é que as últimas serão implementadas no modo lazy. Enquanto as implementações de List, como ArrayList e LinkedList, já possuem as informações, por exemplo, o RedisList usará o Jedis, a API de comunicação com o redis para realizar uma operação (seja inserir, verificar o tamanho ou retornar os valores dentro do redis). Desse é dito que um RedisList é igual ao outro se ele tiver a mesma chave.

Convenção do namespace

Por convenção, o redis utiliza o conceito de namespace que funciona como um prefixo da chave. O seu formato é: namespace:chave.

Por exemplo, para registrar informações do usuário na seção seria users:nickname.

Cliente do Redis

O Jedis é o client para o redis feito para o Java e com ele é possível realizar todas as operações em cima das chaves. O seu uso é bastante simples, conforme mostra o código abaixo:


Jedis jedis = new Jedis("localhost");
jedis.set("foo", "bar");
String value = jedis.get("foo");

No exemplo acima foi criada uma conexão para o redis na nossa máquina localhost e, em seguida, foram feitas duas operações simples: Setar o bar dentro da chave foo e retornar o valor dentro da chave “foo”.

O Redis possui uma arquitetura distribuída, ou seja, pode-se trabalhar com clusters. Para fazer o client com nós dentro do Jedis, é necessário utilizar o comando da Listagem 3.


Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
//O jedis cluster irá descobrir os outros nós automaticamente.
jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7379));
JedisCluster jc = new JedisCluster(jedisClusterNodes);
jc.set("foo", "bar");
String value = jc.get("foo");
Listagem 3. Client do Jedis

Criando as estruturas

A base para a criação das estruturas é o RedisStrutureBuilder, sendo que, entre os métodos, o único campo obrigatório é o Jedis, que como dito anteriormente, é a nossa comunicação entre a nossa aplicação e o Redis. Acompanhe o código da Listagem 4.


ListStructure<ProductCart> shippingCart = RedisStrutureBuilder.ofList(jedis, 
ProductCart.class).withNameSpace("list_producs").build(); 
List<ProductCart> fruitsCarts =  shippingCart.get(FRUITS); 
fruitsCarts.add(banana); 
ProductCart banana = fruitsCarts.get(0);
 
 
User otaviojava = new User("otaviojava"); 
User felipe = new User("ffrancesquini"); 
SetStructure<User> socialMediaUsers = RedisStrutureBuilder.ofSet(RedisConnection.JEDIS, 
User.class).withNameSpace("socialMedia").build(); 
Set<User> users = socialMediaUsers.createSet("twitter"); 
users.add(otaviojava); 
users.add(otaviojava); 
users.add(felipe); 
users.add(otaviojava); 
users.add(felipe);
//haverá apenas um objeto otaviojava e um felipe, já que ele impede a 
duplicação da lista
//lembrando que os métodos de equals e hascode dos objetos não serão 
utilizados, o que será considerado é a String do objeto gerado.
 
 
Species mammals = new Species("lion", "cow", "dog"); 
Species fishes = new Species("redfish", "glassfish"); 
Species amphibians = new Species("crododile", "frog"); 
 
MapStructure<Species> zoo = = 
RedisStrutureBuilder.ofMap(RedisConnection.JEDIS, Species.class)
.withNameSpace("animalZoo").build(); 
 
Map<String, Species> vertebrates = zoo.get("vertebrates"); 
vertebrates.put("mammals", mammals); 
vertebrates.put("mammals", mammals); 
vertebrates.put("fishes", fishes); 
vertebrates.put("amphibians", amphibians);
 
QueueStructure<LineBank> serviceBank =  
RedisStrutureBuilder.ofQueue(RedisConnection.JEDIS, 
LineBank.class).withNameSpace("serviceBank").build(); 
QueueStructure<LineBank> serviceBank =  
RedisStrutureBuilder.ofQueue(RedisConnection.JEDIS, 
LineBank.class).withNameSpace("serviceBank").build(); 
 
Queue<LineBank> lineBank = serviceBank.get("createAccount"); 
lineBank.add(new LineBank("Otavio", 25)); 
LineBank otavio = lineBank.poll();
Listagem 4. RedisStrutureBuilder

Com isso, foi discutido nesse artigo um pouco mais sobre o redis e um exemplo prático utilizando implementações das coleções Java para o redis. Salientamos a diferença entre o redis o cache. Os caches apresentados acima, por usarem formato binário e usarem o Kryo ou o java.io.Serializable acabam sendo mais compactos e rápidos na leitura e escrita. No entanto, uma grande mudança no objeto acaba impossibilitando a recuperação das informações antes da mudança, sendo imprescindível a eliminação das informações anteriores, afinal. É esse o objetivo do cache: um bloco de memória de acesso rápido de armazenamento temporário. Já o redis é um banco de dados chave valor cujo o armazenamento fica na memória. No entanto, de tempos em tempos é feito um autobackup das informações no disco com o intuito de, caso aconteça uma queda, não haja perdas das informações. E o outro fator é que o redis está no seu armazenamento utilizando String tanto na chave como no valor e possui algumas estruturas na parte do valor.