Introdução ao Redis
O Redis (REmote DIctionary Server) é um banco de dados open-source que armazena dados na memória (RAM). Isso permite que ele realize operações de leitura e escrita com latência extremamente baixa, ideal para cenários de alta demanda por desempenho.
Ele utiliza a estrutura de armazenamento chave/valor para o armazenamento de dados. Como valor a ser armazenado e ele oferece várias estruturas nativas, como strings, listas, conjuntos (sets), hashes, e outros, o que dá a ele muita flexibilidade para diferentes necessidades.
Por conta dessa velocidade e versatilidade, o Redis é frequentemente usado para cache de dados, armazenamento de sessões, filas leves, pub/sub, contadores em tempo real, entre outros casos onde é necessário acessar dados com agilidade.
Antes de começar a usar o Redis, precisamos instalá-lo. A seguir veremos como fazer isso no Linux/Debian.
apt install redis-server
Com o Redis instalado, o próximo passo é iniciar o serviço para que ele comece a rodar. A seguir mostramos como fazer isso em sistemas Linux baseados em Debian/Ubuntu, e como verificar se o servidor está ativo.
sudo service redis-server start
Com o servidor Redis em execução, o próximo passo é conectar a ele via linha de comando usando o redis-cli, a interface oficial de terminal. A seguir veja como fazer isso e testar sua conexão de forma simples.
redis-cli -h localhost -p 6379
Se você estiver usando o Redis através de uma aplicação, como costuma acontecer, pode ser útil monitorar em tempo real os comandos que chegam ao servidor. Para isso, basta usar:
redis-cli MONITOR
Esse comando instrui o Redis a “streamar” todos os comandos que estiver processando, assim, você verá no terminal todas as operações sendo executadas (SET, GET, etc). Isso é especialmente útil para debugar sua aplicação, entender o fluxo de chamadas ao Redis, detectar acessos inesperados ou problemas de lógica. E é importante resaltar que isso impacta a perfomance, já que escrever no console adiciona um overhead que pode ser alto.
Adicionando e recuperando valores com Redis
Uma vez conectado ao servidor, podemos usar o comando SET <chave> <valor> para armazenar uma string no Redis. Esse comando associa a chave <chave> ao valor <valor>. Se a chave já existir, seu valor será substituído pelo novo, ou seja, o SET sobrescreve o conteúdo anterior.
SET nome "Rodrigo"
SET cor "#0000FF"
SET idade 25
Para recuperar o valor armazenado em uma chave, utilizamos o comando GET <valor> , que retorna exatamente o conteúdo associado a ela.
GET nome
// "Rodrigo
GET cor
// "#0000FF"
GET idade
// "25"
Também é possível inserir várias chaves de uma só vez utilizando o comando MSET <chave1> <valor1> <chave2> <valor2> .... e assim por diante. Esse comando grava todos os pares de chave e valor em uma única operação, o que torna o processo mais eficiente quando precisamos registrar diversos dados ao mesmo tempo.
MSET nome "Rodrigo" cor "#FF00FF" idade 26
Também podemos recuperar vários valores de uma só vez usando o comando MGET <chave1> <chave2> ... e assim por diante. Ele retorna os valores na mesma ordem em que as chaves foram informadas.
MGET nome idade cor
// 1) "Rodrigo"
// 2) "26"
// 3) "#FF00FF"
Também é possível listar as chaves armazenadas no Redis utilizando o comando KEYS <padrao> , que retorna todas as chaves que correspondem ao padrão informado.
KEYS *
// 1) nome
// 2) contador
O caractere * funciona como um coringa que representa qualquer sequência de caracteres. Por isso, ao usá-lo no comando KEYS, todas as chaves existentes serão retornadas, o que pode gerar uma lista muito grande dependendo do volume de dados. Por esse motivo, é fundamental adotar um padrão de nomeação das chaves para facilitar a organização e a recuperação das informações. Por exemplo, ao armazenar em cache os dados de uma tabela de clientes, podemos criar chaves que sigam um formato consistente, como o exemplo abaixo:
clientes::all -> todos os registros
clientes::1 -> dados do cliente com id 1
clientes::2 -> dados do cliente com id 2
.....
Assim, podemos listar todas as chaves relacionadas ao cache da tabela de clientes utilizando o padrão clientes::* no comando KEYS, que retorna apenas as entradas que seguem essa convenção de nomeação.
Seguindo a mesma lógica, também podemos organizar o cache de páginas adotando um padrão específico para essas chaves.
site::home -> página inicial
site::contato -> página de contato
site::produtos::eletronicos -> página de listagem de produtos da categoria eletrônicos
site::produtos::brinquedos -> página de listagem de produtos da categoria brinquedos
Para listar essas entradas utilizando o comando KEYS, podemos usar o padrão site::* para recuperar todas as chaves relacionadas ao site, ou site::produtos::* caso queiramos trazer apenas as chaves das diferentes categorias de produtos.
Esse padrão é simples de aplicar: definimos um identificador que representa o tipo de dado que estamos armazenando e depois concatenamos os parâmetros necessários para identificar cada entrada, como os argumentos usados nos métodos da aplicação ou os query params enviados na URL.
Quando o banco possui muitas chaves, o uso do comando KEYS deixa de ser interessante, pois ele retorna tudo de uma vez e pode impactar o desempenho. Nessas situações, o ideal é utilizar o comando SCAN, que percorre as chaves de forma incremental por meio de um cursor. A primeira chamada deve ser feita usando o cursor 0; se o retorno trouxer outro número, esse valor deve ser usado na chamada seguinte até que o SCAN retorne 0 novamente, indicando que a iteração terminou. Além disso, podemos usar o argumento MATCH para filtrar por um padrão específico e o COUNT para definir a quantidade de resultados que desejamos receber por chamada.
SCAN 0 MATCH nome* COUNT 2
// 1) "2"
// 2) 1) "nome2"
// 2) "nome6"
SCAN 2 MATCH nome* COUNT 2
// 1) "1"
// 2) 1) "nome1"
// 2) "nome4"
SCAN 1 MATCH nome* COUNT 2
// 1) "0"
// 2) 1) "nome5"
// 2) "nome7"
// 3) "nome3"
Deletando
Para remover um ou mais itens do Redis, utilizamos o comando DEL, seguido da chave ou das chaves que desejamos excluir: DEL <chave> [<chave2> …]. O comando DEL também deleta outros valores que não são strings, como listas, hashes, etc, que serão apresentados mais adiante.
DEL nome
DEL nome cor idade
Outros comandos
O Redis também disponibiliza diversos comandos para manipulação de dados, permitindo operações como incrementar ou decrementar valores numéricos, concatenar conteúdos e alterar trechos específicos de uma string.
Incrementar/Decrementar
Quando o valor associado a uma chave é numérico, ainda que internamente seja guardado como uma string, o Redis permite aumentar ou diminuir esse número em 1 utilizando os comandos INCR <chave> e DECR <chave>.
SET contador 0
INCR contador
INCR contador
GET contador
// "2"
DECR contador
GET contador
// "1"
Também é possível aumentar ou reduzir o valor de uma chave por uma quantidade específica utilizando os comandos INCRBY <chave> <valor> e DECRBY <chave> <valor>.
SET contador 0
INCRBY contador 5
INCRBY contador 5
GET contador
// "10"
DECRBY contador 5
GET contador
// "5"
APPEND
O comando APPEND <chave> <valor> permite adicionar uma nova string ao final do valor já armazenado em uma chave. Se a chave ainda não existir, o Redis cria a chave e armazena apenas o valor fornecido.
SET numeros_sorteados 10
APPEND numeros_sorteados ", 5"
GET numeros_sorteados
// "10, 5"
APPEND numeros_sorteados ", 26"
GET numeros_sorteados
"10, 5, 26"
GETRANGE/SETRANGE
O Redis permite extrair apenas uma parte de uma string por meio do comando GETRANGE <chave> <pos-inicio> <pos-fim>. A indexação começa em zero e também é possível usar índices negativos para acessar posições contando a partir do final da string.
SET nome "Rodrigo"
GETRANGE nome 0 2
// "Rod"
GETRANGE nome -2 -1
// "go
O comando SETRANGE <chave> <offset> <valor> permite modificar parte de uma string já existente. O offset indica a posição inicial onde a alteração deve ocorrer. Apenas os caracteres correspondentes ao tamanho do novo valor são substituídos; o restante da string original permanece inalterado após essa posição.
SET nome "Rodrigo"
SETRANGE nome 3 "ney"
GET nome
// "Rodneyo"
Perceba que o último “o” da string original não foi alterado. Isso acontece porque, ao inserir “ney” a partir do offset 3, apenas três caracteres, exatamente o tamanho da nova string, são substituídos. Como "Rodrigo" possui sete caracteres, o último permanece intacto.
Listas
O Redis também permite armazenar dados usando a estrutura de listas, que funcionam de maneira semelhante a listas ligadas. Com esse tipo de dado, é possível implementar filas e pilhas de forma simples e eficiente, aproveitando operações rápidas de inserção e remoção nas extremidades.
Adicionando elementos em uma lista
Para criar uma lista no Redis, basta inserir o primeiro elemento usando os comandos LPUSH <chave> <valor> ou RPUSH <chave> <valor>. O LPUSH adiciona o valor no início da lista (lado esquerdo), enquanto o RPUSH insere o elemento no final (lado direito). Se a chave ainda não existir, ela será criada automaticamente como uma nova lista.
LPUSH nomes "Rodrigo"
LPUSH nomes "João"
RPUSH nomes "Antonio"
RPUSH nomes "Fabiane"
//Resultado da lista -> { "João", "Rodrigo", "Antonio", "Fabiane"}
Visualizando os elementos da lista
Para verificar quantos elementos uma lista contém, utilizamos o comando LLEN <chave>. Já para visualizar os valores armazenados, podemos usar o comando LRANGE <chave> <indice-inicio> <indice-fim>. Tanto o índice inicial quanto o final podem ser negativos, permitindo acessar posições contando a partir do final da lista.
LLEN nomes
// (integer) 4
LRANGE nomes 0 -1
// 1) "João"
// 2) "Rodrigo"
// 3) "Antonio"
// 4) "Fabiane"
Também é possível acessar um elemento específico da lista informando sua posição. Para isso, utilizamos o comando LINDEX <chave> <valor>, lembrando que a contagem começa em 0, assim como em arrays tradicionais. Ele retorna a posição da primeira ocorrência do valor, já que uma lista pode conter valores repetidos.
LINDEX nomes 2
// "Antonio"
Também podemos descobrir a posição de um determinado elemento na lista utilizando o comando LPOS <chave> <valor>, que retorna o índice da primeira ocorrência encontrada.
LPOS nomes "Antonio"
// (integer) 2
Removendo elementos
Também é possível remover elementos tanto do início quanto do fim de uma lista usando os comandos LPOP <chave> e RPOP <chave>, respectivamente. Ambos os comandos removem exatamente um elemento e retornam o valor que foi retirado da lista.
LRANGE nomes 0 -1
// 1) "João"
// 2) "Rodrigo"
// 3) "Antonio"
// 4) "Fabiane"
LPOP nomes
// "João
RPOP nomes
// "Fabiane"
LRANGE nomes 0 -1
// 1) "Rodrigo"
// 2) "Antonio"
Também é possível “aparar” uma lista, removendo elementos das extremidades utilizando o comando LTRIM <chave> <pos-inicio> <pos-fim>, que mantém apenas os itens dentro do intervalo informado e descarta todo o restante.
LRANGE nomes 0 -1
// 1) "João"
// 2) "Rodrigo"
// 3) "Antonio"
// 4) "Fabiane"
LTRIM nomes 1 2
LRANGE nomes 0 -1
// 1) "Rodrigo"
// 2) "Antonio"
Sets
O Redis também disponibiliza a estrutura Set(conjuntos), que permite armazenar múltiplos valores, sem ordem definida e sem aceitar elementos duplicados. Diferente das listas, os sets não garantem qualquer tipo de ordenação, e cada valor só pode aparecer uma única vez.
Para inserir elementos, chamados de membros, em um set, utilizamos o comando SADD <chave> <valor> [<valor> ...]. Sempre que o primeiro membro é adicionado, o Redis cria automaticamente a estrutura do set associada à chave.
SADD nomes "Rodrigo"
SADD nomes "Carlos" "Juliana" "Maria" "Afonso"
SADD nomes "Rodrigo" //não irá adicionar um segundo "Rodrigo" pois o set já contem este valor
Para visualizar todos os membros de um set, utilizamos o comando SMEMBERS <chave>.
SMEMBERS nomes
// 1) "Carlos"
// 2) "Rodrigo"
// 3) "Juliana"
// 4) "Maria"
// 5) "Afonso"
Para remover um membro de um set, utilizamos o comando SREM <chave> <valor>, que exclui o valor informado caso ele exista no conjunto.
SREM nomes "Juliana"
SMEMBERS nomes
// 1) "Rodrigo"
// 2) "Maria"
// 3) "Carlos"
// 4) "Afonso"
Para verificar se um determinado valor está presente em um set, utilizamos o comando SISMEMBER <chave> <valor>, que retorna 1 quando o valor existe no conjunto e 0 quando não existe.
SISMEMBER nomes "Rodrigo"
//(integer) 1
SISMEMBER nomes "Pafuncio"
(integer) 0
Podemos unir dois ou mais sets utilizando o comando SUNION <chave> [<chave> …]. Esse comando não modifica nenhum dos sets envolvidos; ele apenas retorna a combinação de todos os elementos, garantindo que não haja valores duplicados, mesmo que um item apareça em mais de um set.
SADD grupo1 "Rodrigo" "Julia" "Anderson"
SADD grupo2 "Lucia" "Mariana" "Julia" "Rodrigo"
SUNION grupo1 grupo2
// 1) "Mariana"
// 2) "Julia"
// 3) "Rodrigo"
// 4) "Lucia"
// 5) "Anderson"
Assim como podemos unir dois ou mais sets, também é possível obter apenas os elementos em comum utilizando o comando SINTER <chave> [<chave> …]. Da mesma forma, para recuperar a diferença entre sets, ou seja, os valores que estão presentes somente no primeiro set informado, usamos o comando SDIFF <chave> [<chave> …].
SINTER grupo1 grupo2
// 1) "Julia"
// 2) "Rodrigo"
SDIFF grupo1 grupo2
//1) "Anderson"
Hash
O Redis também permite armazenar dados no formato de hash, que funciona como um conjunto de pares chave/valor, semelhante a um objeto ou mapa em linguagens de programação. Cada par é chamado de campo, e tanto a leitura quanto a escrita desses campos podem ser feitas individualmente dentro do mesmo hash.
Adicionando valores ao Hash
Para inserir dados em um hash utilizamos o comando HSET <chave> <campo> <valor> (podendo incluir vários pares campo/valor na mesma chamada). Esse mesmo comando serve tanto para criar um novo hash quanto para adicionar novos campos ou atualizar valores de campos que já existem.
HSET livros:1 titulo "O Senhor dos Anéis: A Sociedade do Anel" autor "J.R.R. Tolkien" genero "Fantasia"
HSET livros:1 titulo "O Senhor dos Anéis: As Duas Torres"
HSET livros:2 titulo "Vinte Mil Léguas Submarinas" autor "Júlio Verne"
HSET livros:2 genero "Aventura"
Recuperando valores do Hash
Para recuperar um valor armazenado dentro de um hash, utilizamos o comando HGET <chave> <campo>. Esse comando retorna apenas o conteúdo do campo solicitado, permitindo acessar cada informação do hash de forma individual.
HGET livros:1 titulo
// "O Senhor dos Anéis: As Duas Torres"
HGET livros:1 autor
// "J.R.R. Tolkien"
Para obter todos os campos e seus respectivos valores de um hash, utilizamos o comando HGETALL <chave>. Esse comando retorna o hash completo, exibindo cada campo seguido do seu valor.
HGETALL livros:1
// 1) "titulo"
// 2) "O Senhor dos Anéis: As Duas Torres"
// 3) "autor"
// 4) "J.R.R. Tolkien"
// 5) "genero"
// 6) "Fantasia"
Para visualizar apenas os nomes dos campos armazenados em um hash, utilizamos o comando HKEYS <chave>. Esse comando retorna exclusivamente a lista de campos, sem incluir seus valores.
HKEYS livros:1
// 1) “titulo”
// 2) “autor”
// 3) “genero”
Para verificar se um determinado campo existe dentro de um hash, utilizamos o comando HEXISTS <chave> <campo>. Esse comando retorna 1 quando o campo está presente no hash e 0 quando o campo não existe.
HEXISTS livros:1 titulo
// (integer) 1
HEXISTS livros:1 paginas
// (integer) 0
Deletando um campo
Podemos remover um campo específico de um hash utilizando o comando HDEL <chave> <campo>.
HDEL livros:1 genero
HKEYS livros:1
// 1) "titulo"
// 2) "autor"
pub/sub
Pub/Sub é um modelo de comunicação baseado em mensagens que permite a interação entre diferentes componentes de um sistema distribuído. Nesse padrão, um publisher publica uma mensagem em um tópico, e essa mensagem é imediatamente enviada (broadcast) para todos os subscribers atualmente inscritos nesse tópico. Diferentemente de sistemas baseados em filas, o Pub/Sub envia uma cópia da mensagem para cada assinante do tópico. Além disso, é importante destacar que as mensagens no Pub/Sub são efêmeras: se não houver assinantes conectados no momento da publicação, a mensagem simplesmente se perde.
Para inscrever-se em um tópico, basta utilizar o comando SUBSCRIBE <topico>, que coloca o cliente em modo de escuta para todas as mensagens publicadas naquele canal.
SUBSCRIBE chat
// 1) "subscribe"
// 2) "chat"
// 3) (integer) 1
Uma forma simples de testar o funcionamento é abrir dois ou mais consoles e realizar a inscrição no tópico chat em cada um deles. Assim, todos os consoles receberão as mensagens publicadas nesse canal.
Em um console separado, podemos publicar uma mensagem em um tópico utilizando o comando PUBLISH <topico> <mensagem>.
PUBLISH chat "Oi, alguem quer tc?"
Ao ser publicada a mensagem, nos demais consoles que estavam inscritos no tópico chat, a seguinte mensagem será exibida automaticamente:
1) "message"
2) "chat"
3) "Oi, alguem quer tc?"
Também podemos nos inscrever em tópicos utilizando padrões, por meio do comando PSUBSCRIBE <padrao> .
PSUBSCRIBE chat.*
Esta foi uma introdução sobre o Redis, um banco de dados extremamente versátil que, embora seja amplamente utilizado como sistema de cache, também oferece recursos poderosos para diversas outras finalidades. Além das estruturas e comandos apresentados aqui, o Redis disponibiliza muitas outras funcionalidades que podem ser exploradas na documentação oficial.
Embora o uso mais comum do Redis seja por meio da integração com nossa linguagem de programação favorita, enviando comandos através de uma API, esse é um assunto para uma próxima postagem. Por hoje, ficamos apenas com esta introdução. T++!
