Saltar a contenido

Redis

Propuesta didáctica

Seguimos trabajando el siguiente resultado de aprendizaje (RA):

  • RA7: Gestiona la información almacenada en bases de datos no relacionales, evaluando y utilizando las posibilidades que proporciona el sistema gestor.

Criterios de evaluación

  • CE7c: Se han identificado los elementos utilizados en estas bases de datos.
  • CE7d: Se han identificado distintas formas de gestión de la información según el tipo de base de datos no relacionales.
  • CE7e: Se han utilizado las herramientas del sistema gestor para la gestión de la información almacenada.

Contenidos

Bases de datos no relacionales:

  • Elementos de las bases de datos no relacionales.
  • Sistemas gestores de bases de datos no relacionales.
  • Herramientas de los sistemas gestores de bases de datos no relacionales para la gestión de la información almacenada.

Cuestionario inicial

  1. ¿Qué es Redis y cuáles son sus principales características como base de datos NoSQL?
  2. ¿Cuáles son las ventajas y desventajas de utilizar Redis como sistema de almacenamiento en memoria frente a otros sistemas de almacenamiento persistente?
  3. ¿Cómo se diferencian las bases de datos clave-valor de las bases de datos relacionales tradicionales?
  4. ¿Qué estructuras de datos admite Redis y cómo se utilizan en la gestión de información?
  5. ¿Cómo se implementan las operaciones CRUD?
  6. ¿Qué son los espacios de nombres y cuál es su propósito en la organización de claves?
  7. ¿Cómo se maneja la expiración de claves y qué comandos se utilizan para ello?
  8. ¿Cómo se implementan para garantizar la atomicidad de las operaciones?
  9. ¿Cómo se pueden representar y manipular estructuras de datos complejas, como JSON, dentro de Redis?

Programación de Aula (5h)

Seguimos con la penúltima unidad, y tras haber introducido los conceptos y elementos de los sistemas NoSQL, vamos a profundizar en el manejo de Redis, conociendo cómo funciona una solución clave-valor, con una duración estimada de 5 horas:

Sesión Contenidos Actividades Criterios trabajados
5 Redis. CRUD AC1207 CE7c, CE7d, CE7e
6 Listas AC1209 CE7c, CE7d, CE7e
7 Conjuntos AC1211 CE7c, CE7d, CE7e
8 JSON
9 Mensajería

Redis

Logo de Redis
Logo de Redis

Redis es una base de datos NoSQL open source en memoria, con un modelo de datos de clave-valor, que permite el cacheo de los datos y funcionar como un broker de mensajería. Soporta estructuras de datos como cadenas, hash, listas, conjuntos, conjuntos ordenados con consultas por rango, así como otras estructuras más específicas como mapas de bit, consultas radius, índices geospaciales, etc...

El nombre Redis proviene de las iniciales de Remote Dictionary Server (servidor de diccionario remoto), un tipo de servidor apto como memoria rápida para datos, permitiendo su uso como una base de datos en memoria y como de clave-valor.

También se ofrece como una opción de pago mediante Redis Lab, ofreciéndola como una solución gestionada donde no tenemos que preocuparnos de la infraestructura.

Características

Redis almacena los datos como pares clave-valor, donde:

  • Cada clave (key) es un identificador único (similar a una clave primaria en SQL)
  • Cada valor (value) puede ser de diferente tipos (número, cadena, lista, hash, etc...)

Este modelo es fundamentalmente diferente del modelo relacional:

Característica Redis (clave-valor) SQL (relacional)
Estructura Plana, no relacional Tabular, relaciones entre entidades
Esquema Flexible, sin esquema fijo Rígido, esquema predefinido
Consultas Basadas en claves Basadas en conjuntos (SELECT)
Transacciones Soporte básico con comandos MULTI/EXEC Completo soporte ACID

Cuando decimos que Redis es una base de datos en memoria, estamos indicando que el servidor almacena todos los datos en la memoria principal (RAM), ya que es la manera más rápida para almacenar y recuperar datos. Esto permite que Redis funcione como memoria caché y también como unidad de memoria principal, con independencia de si los datos permanecen en la base de datos por mucho o poco tiempo. Así pues, en un servidor Redis con su uso principal, los datos no se guardan en el disco duro, sino en la memoria principal.

Gracias a ser un almacén clave-valor, ofrece un alto rendimiento y puede escalarse fácilmente. Para cada entrada se crea una clave mediante la que se puede volver a solicitar la información en cuestión. Esto implica que las entradas no necesitan estar relacionadas entre sí ni tampoco consultarse con varias tablas diferentes, sino que la información está disponible de manera directa.

¿Y si falla el servidor o se va la luz? ¿Corremos el riesgo de perder todos los datos? Para evitar que eso ocurra, Redis puede, o bien hacer regularmente un duplicado de todos los datos en un disco duro secundario (persistencia RDB - Redis Database), o bien guardar en un archivo de registro todas las órdenes necesarias para llevar a cabo una reconstrucción (persistencia AOF - Append Only File).

Finalmente, respecto a la escalabilidad, como solución NoSQL, si toda la información no cabe es un único nodo, el sistema puede escalar horizontalmente añadiendo más nodos. Si cada nodo puede almacenar 64GB RAM, y tenemos 4 servidores, podríamos almacenar hasta 256GB de datos en nuestro servidor Redis.

Hablemos de tamaños

Un millón de cadenas almacenadas en Redis ocupan aproximadamente 85MB de almacenamiento.
Una instancia vacía de Redis utiliza 3 MB de RAM.
Los valores en Redis son de 64 bits.
En teoría, Redis puede almacenar 2^32 claves en una única instancia Redis. En la práctica, hay servidores de una única instancia con 250 millones de claves.

Puesta en marcha

Para poner en marcha Redis, bien podemos instalarlo en nuestro sistema, usar un contenedor Docker o utilizar la solución Redis cloud. Para ello, consulta la sección de Redis en la página de Entorno.

Uso del cliente redis-cli

Aunque hayamos visto que existe un interfaz gráfico, de momento vamos a centrarnos en el uso mediante la consola y acceder al cliente interactivo mediante redis-cli:

redis-cli
# 127.0.0.1:6379>

Si queremos conectarnos a una instancia remota, y con un usuario determinado (parámetro -u), realizaremos una conexión similar a la siguiente, donde al conectarnos siempre se nos indicará a que host estamos conectados:

redis-cli -u redis://usuario:password@redis-16677.c339.eu-west-3-1.ec2.redns.redis-cloud.com:16677
redis-16677.c339.eu-west-3-1.ec2.redns.redis-cloud.com:16677>

Al ejecutar un comando, siempre recibiremos una respuesta en la línea inferior. Por ejemplo, vamos a comprobar la conectividad haciendo PING:

127.0.0.1:6379> PING
# PONG

Si no hubiéramos podido conectarnos, habríamos recibido un mensaje de error. Mediante help aparecerá, además de la versión instalada, una lista de opciones de ayuda, así como la posibilidad de obtener más información de un comando concreto:

127.0.0.1:6379> help
# redis-cli 7.2.5
# To get help about Redis commands type:
#       "help @<group>" to get a list of commands in <group>
#       "help <command>" for help on <command>
#       "help <tab>" to get a list of possible help topics
#       "quit" to exit

CRUD

En este apartado vamos a simular que queremos crear un acortador de URLs, del tipo bit.ly, de manera que cuando un usuario pone una URL corta, accederíamos a la URL larga, lo que también nos permite llevar un contador de las visitas a cada dirección.

Usaremos los comandos SET y GET para asignar y recuperar un valor de una clave. SET siempre requiere dos parámetros, la clave y el valor, mientras que GET sólo permite recuperar por la clave:

SET aitor-bd aitor-medrano.github.io/bd
# OK
GET aitor-bd
# "aitor-medrano.github.io/bd"

Letra inicial

La primera letra de los comandos normalmente puede indicar el tipo de datos sobre el que trabaja, el módulo de Redis que emplea o un tipo de operación específica. En este caso, todas las operaciones que empiezan por M se consideran operaciones múltiples que operan sobre más de una clave o valor con el mismo comando.

Si queremos hacer más de una entrada de una sola vez usaremos MSET y MGET:

MSET aitor-iabd aitor-medrano.github.io/iabd aitor-blog aitor-medrano.github.io/iabd/blog
# OK
MGET aitor-bd aitor-iabd aitor-blog
# 1) "aitor-medrano.github.io/bd"
# 2) "aitor-medrano.github.io/iabd"
# 3) "aitor-medrano.github.io/iabd/blog"

Si queremos recuperar las claves existentes usaremos KEYS, al cual le podemos pasar * para mostrar todas o un prefijo para que muestre aquellas que lo cumplen (permite el uso de expresiones regulares):

KEYS aitor-*
# 1) "aitor-blog"
# 2) "aitor-bd"
# 3) "aitor-iabd"

SCAN & MATCH

Aunque KEYS permita recuperar todas las claves, el problema es que bloquea el servidor mientras las recorre. Esto puede provocar problemas serios en entornos de producción, ya que mientras Redis ejecuta el comando KEYS, el servidor queda bloqueado para atender otras operaciones. En bases de datos con millones de claves, esto puede significar interrupciones de servicio inaceptables.

Para evitar esto, se recomienda utilizan SCAN cursor [MATCH patrón] [COUNT cantidad] [TYPE tipo], el cual ofrece una alternativa eficiente, ya que funciona de manera incremental, devolviendo las claves en lotes. Veamos un ejemplo (a lo largo de la sesión iremos creando nuevas claves que ahora aparecen en el ejemplo):

SCAN 0
# 1) "7"            # El cursor para la siguiente iteración
# 2)  1) "visitas"
#     2) "pedro:visitados"
#     3) "sitiosweb"
#     4) "aitor-bd"
#     5) "noticias"
#     6) "contador"
#     7) "aitor-dwes"
#     8) "usuario:aitor:nombre"
#     9) "tech"
#    10) "usuario:aitor:pwd"
#    11) "usuario:aitor"

SCAN 7
# 1) "0"            # El cursor 0 indica que se ha completado la iteración
# 2) (empty array)

También podemos filtrar por las claves, así como restringir el tipo de valor:

SCAN 0 MATCH usuario:aitor* COUNT 15
# 1) "0"
# 2) 1) "usuario:aitor:nombre"
#    2) "usuario:aitor:pwd"
#    3) "usuario:aitor"

SCAN 0 TYPE "string" COUNT 10
# 1) "7"
# 2) 1) "aitor-bd"
#    2) "contador"
#    3) "aitor-dwes"
#    4) "usuario:aitor:nombre"
#    5) "usuario:aitor:pwd"

SCAN 0 TYPE "set"
# 1) "7"
# 2) 1) "sitiosweb"
#    2) "noticias"
#    3) "tech"

Las características principales de SCAN son:

  • Iteración incremental: Devuelve un cursor que se utiliza para continuar la iteración
  • No bloqueante: Permite que otras operaciones se ejecuten mientras se realiza el escaneo
  • Cobertura completa: Garantiza que se examinan todas las claves existentes
  • Opciones de filtrado: Permite filtrar mediante patrones con MATCH
  • Control de rendimiento: El parámetro CO`UNT sugiere cuántas claves examinar por iteración

Para borrar una determinada clave usaremos DEL y EXISTS para comprobar si existe:

EXISTS aitor-iabd
# (integer) 1
DEL aitor-iabd
# (integer) 1
EXISTS aitor-iabd
# (integer) o
GET aitor-bd
# (nil)

Cadenas

Si nuestros valores son cadenas, podemos emplear una serie de operaciones para trabajar con ellos, como STRLEN para recuperar el tamaño de un valor, GETRANGE clave inicio fin para obtener una subcadena o APPEND para añadir contenido al final

GET aitor-bd
# "aitor-medrano.github.io/bd"
STRLEN aitor-bd
# (integer) 26
GETRANGE aitor-bd 0 22
# "aitor-medrano.github.io"
GETRANGE aitor-bd 24 -1
# "bd"
APPEND aitor-bd "/"
# (integer) 27
GET aitor-bd
# "aitor-medrano.github.io/bd/"

Enteros

Aunque Redis almacene cadenas, también permite almacenar números enteros y realizar ciertas operaciones sobre ellos, como INCR para incrementar de uno en uno INCRBY para indicar la cantidad, y sus contrarios DECR y DECRBY:

SET contador 2
# OK
INCR contador
# (integer) 3
GET contador
# "3"
INCRBY contador 2
# (integer) 5
DECR contador
# (integer) 4

Casos de uso muy comunes de almacenamiento de número enteros pueden ser contar el número de clics que se realiza sobre un enlace, o controlar la cantidad de entradas disponibles de un evento/concierto/partido/festival, etc...

Espacios de nombres

Redis utiliza un concepto diferente al de las bases de datos tradicionales. En lugar de tener múltiples bases de datos separadas, proporciona bases de datos lógicas numeradas dentro de una única instancia. Por defecto, Redis cuenta con 16 bases de datos numeradas de 0 a 15, aunque este número se puede configurar en el archivo redis.conf mediante la directiva databases.

Podemos recuperar las bases de datos que tenemos mediante CONFIG GET DATABASES:

127.0.0.1:6379> CONFIG GET DATABASES
1) "databases"
2) "16"

Para elegir qué base de datos queremos emplear, usaremos el comando SELECT, que permite cambiar la base de datos actual a otro espacio de nombres (al cambiar de base de datos, si no empleamos la base de datos 0, en el CLI aparecerá entre corchetes su número identificador):

127.0.0.1:6379> GET aitor-bd
# "aitor-medrano.github.io/bd"
127.0.0.1:6379> SELECT 1
# OK
127.0.0.1:6379[1]> GET aitor-bd
# (nil)

De esta manera, podemos crear espacios de nombres separados para diferentes aplicaciones o componentes de la misma aplicación.

Redis cloud

Si estás utilizando Redis en modo clúster en el cloud, este solo soporta la base de datos 0. Los comandos SELECT con índices distintos de 0 no funcionarán en Redis Cluster.

Aunque podemos emplear SELECT para cambiar de base de datos, algunos desarrolladores prefieren utilizar prefijos en las claves del tipo usuario:1:nombre o producto:1:nombre para diferencias espacios de nombres.

A pesar de sus limitaciones, el comando SELECT puede ser útil en varios escenarios:

  • Entornos de desarrollo/pruebas: Usar diferentes bases de datos para separar datos de prueba de datos de producción en la misma instancia.
  • Migración de datos: Mover datos temporalmente a otra base de datos durante procesos de migración.
  • Caché multi-nivel: Usar diferentes bases de datos para diferentes niveles de caché con políticas de expiración distintas.
  • Aplicaciones simples: Para aplicaciones pequeñas donde no se justifica tener múltiples instancias de Redis.

Expiración

Una de las características más útiles es la capacidad de establecer un tiempo de vida (TTL, Time-To-Live) para las claves, lo que permite gestionar la caducidad automática de los datos, sin necesidad de implementar mecanismos de limpieza manuales.

Redis utiliza dos estrategias para eliminar claves expiradas:

  1. Eliminación pasiva: Cuando se accede a una clave, Redis comprueba si ha expirado y, en caso afirmativo, la elimina.
  2. Eliminación activa: Redis tiene un proceso que periódicamente muestrea claves aleatorias con tiempo de expiración y elimina las que han caducado.

Esto permite que Redis mantenga un buen rendimiento incluso con millones de claves con expiración.

Para ello, usaremos el comando EXPIRE clave segundos, el cual permite asignar un tiempo de vida (en segundos) a una clave existente. Una vez transcurrido el tiempo especificado, la clave se elimina automáticamente.

EXPIRE aitor-blog 3600 # establecemos una clave con una hora de expiración
# (integer) 1

Otras posibilidad son utilizar EXPIREAT clave timestamp que permite establecer un tiempo de expiración basado en una fecha y hora específica, expresada en timestamp UNIX (segundos desde 1 de enero de 1970), o PEXPIRE que permite definir la expiración en milisegundos:

EXPIREAT aitor-blog 1742751722
PEXPIRE aitor-blog 30000

Si queremos saber cuanto tiempo le queda a una clave, usaremos el comando TTL clave:

TTL aitor-blog  # Comprobamos el tiempo restante
# (integer) 3283

Si la clave no tiene expiración asignada, devolverá -1. En el caso de que la clave no exista, devolverá -2, bien sea porque ya ha expirado o que nunca se creó.

Otra posibilidad de asignar la expiración es hacerlo al mismo tiempo que se asigna un valor. Así pues, en las operaciones tipo SET podemos indicar el parámetro EX con la cantidad de segundos (o PX para la cantidad de milisegundos):

SET aitor-blog aitor-medrano.github.io/iabd/blog EX 3600
# (integer) 3283

Finalmente, si queremos cambiar el estado para que una clave ya no caduque y deje de ser volátil, debemos persistirla mediante PERSIST:

PERSIST aitor-blog
# (integer) 1

La expiración de las claves se emplea para:

  • Gestión de sesiones: expirar claves de sesión de usuarios tras un tiempo de inactividad.
  • Caché temporal: almacenar datos temporalmente para evitar accesos frecuentes a bases de datos tradicionales.
  • Colas de mensajes: eliminar mensajes antiguos automáticamente.
  • Control de rate-limiting: implementar restricciones de uso por usuario con expiración automática.

Transacciones

De la misma forma que hemos visto en MariaDB, necesitamos de alguna manera poder agrupar dos o más operaciones como si fuera una operación atómica. Para ello, usaremos el comando MULTI para inicial el bloque de instrucciones, y EXEC para confirmar la transacción. En el caso de querer hacer un rollback, usaremos el comando DISCARD:

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET aitor-dwes aitor-medrano.github.io/dwes2122
QUEUED
127.0.0.1:6379(TX)> INCR contador
QUEUED
127.0.0.1:6379(TX)> EXEC
1) OK
2) (integer) 5

Cabe destacar que al comenzar una transacción, en el CLI se mostrará el sufijo (TX) tras el prompt para indicar que las operaciones forman parte de una misma transacción.

Estructuras de datos

La estructura de datos básica de Redis está formada por las llamadas Strings, es decir, cadenas simples de caracteres.

El uso de Strings permite almacenar datos de forma genérica, como por ejemplo, serializando cualquier dato en una cadena o utilizando documentos JSON. Conviene destacar que el tamaño máximo es de 512 MB.

Como convención de código, se utilizan los dos puntos (:) como separador de las palabras o niveles en las claves:

MSET usuario:aitor:nombre "Aitor Medrano" usuario:aitor:pwd "123456" 
# OK
MGET usuario:aitor:nombre usuario:aitor:pwd
# 1) "Aitor Medrano"
# 2) "123456" 

Algunos patrones comunes para nombrar las claves son:

# Para datos de usuario
usuario:{id}:{campo}

# Para sesiones
sesion:{token}:{campo}

# Para datos por entorno
{entorno}:{tipo}:{id}

# Para caché
cache:{servicio}:{recurso}:{id}

El sistema, sin embargo, es también compatible con otras estructuras de datos. Veamos las más destacadas

Hash

  • Estructura

    Un mapa o hash en Redis es similar a un documento JSON, donde se mapea una clave con un valor. Destacar que sus valores siempre serán cadenas, es decir, no puede anidar otros mapas o conjuntos.

    Hashes en Redis
    Hashes en Redis

    Se utiliza para almacenar objetos ligeros, como las sesiones de usuario, los propios datos del usuario, visitantes, etc...

    Todas sus operaciones comienzan por la letra H: HGET , HSET, HMGET, HKEYS, HVALS, HINCRBY, HDEL, etc...

  • Comandos

    Para interactuar con un Hash utilizaremos los comandos HSET para asignar valores, HKEYS para recuperar todas las claves y HVALS para recuperar todos los valores, o HGET para recuperar una clave concreta:

    HSET usuario:aitor nombre "Aitor Medrano" pwd "123456"
    # OK
    HVALS usuario:aitor
    # 1) "Aitor Medrano"
    # 2) "123456"
    HKEYS usuario:aitor
    # 1) "nombre"
    # 2) "pwd"
    HGET usuario:aitor nombre
    # "Aitor Medrano"
    

    Otros comandos más específicos son HDEL para elimina campos, HINCRBY para incrementar un campo cierta cantidad, HLEN para obtener la cantidad de campos, HGETALL para recuperar todas las claves y valores y HSETNX para asignar una valor sólo si la clave no existe (ya que si existe, mediante HSET siempre la sustituye).

Lista

  • Estructura

    Las listas en Redis son como un array de cadenas, teniendo en cuenta que los elementos se ordenan por orden de inserción. Pueden funcionar como colas FIFO (first in, first out) o como pilas LIFO (last in, first out).

    Se implementan mediante listas enlazadas, lo que provoca que las operaciones en la cabeza y en la cola ocurran con complejidad constante, independientemente del tamaño de la lista. En cambio, acceder a un elemento determinado, supone recorrer la lista elemento por elemento. En este caso, es mejor utilizar conjuntos ordenados.

    En Redis, la cabeza (head) es el extremo izquierda y la cola (tail) el derecho.

    Listas en Redis
    Listas en Redis

    Casos de uso:

    • Implementar colas como estructuras de datos
    • Cronogramas de mensajes (feed de una red social, logs recientes, etc...)
    • Listado de tareas, para su procesamiento en el orden de adicción.
  • Comandos

    Para interactuar con un lista utilizaremos los comandos LPUSH para insertar valores por la izquierda y LPOP para sacarlos, y sus homónimos RPUSH y RPOP para hacerlo por la derecha. Si queremos recuperar un subconjunto de elementos, usaremos LRANGE, mientras que para averiguar la longitud de la lista usaremos LLEN.

    Supongamos que queremos asignarles a nuestros usuarios una lista de URL favoritas. Para ello, podríamos hacer:

    RPUSH aitor:enlaces aitor-bd aitor-iabd aitor-blog
    # (integer) 3
    LLEN aitor:enlaces
    # (integer) 3
    LRANGE aitor:enlaces 0 -1
    # 1) "aitor-bd"
    # 2) "aitor-iabd"
    # 3) "aitor-blog"
    

    Todas las operaciones de listas utilizan un acceso 0-index, donde un valor negativo indica el número de pasos desde el final.

    Para sacar elementos, podemos emplear LREM para a partir de una clave, cuantos elementos queremos quitar de un determinado valor (si ponemos 0, borrará todos los que coincidan, y si ponemos un número negativo, borrará los indicados pero empezando a buscar desde el final):

    LREM aitor:enlaces 0 aitor-blog
    # (integer) 1
    LLEN aitor:enlaces
    # (integer) 2
    

    Si queremos reducir una lista y quedarnos con una cantidad concreta de elementos, podemos emplear LTRIM clave inicio fin para, por ejemplo, asegurarnos que nuestra lista no contiene más de 10 elementos:

    LTRIM aitor:enlaces 0 9
    # OK
    

    Para eliminar y recuperar cada valor en el orden en el que se añadieron (de forma similar a una cola FIFO), al insertarlos por la derecha, debemos sacarlos por la izquierda (la cabeza) de la lista:

    LPOP aitor:enlaces
    # "aitor-bd"
    

    Si en cambio queremos que funcione como una pila, tras insertarlos con RPUSH, utililizaremos RPOP para sacarlos. Estas operaciones tienen un coste computacional constante (lo que significa que el tamaño de la lista no impacta en el rendimiento).

    Si queremos sacar, con un único comando, valores de la cola para insertarla en la cabeza de otra, antiguamente podíamos hacerlo mediante el comando RPOPLPUSH (right pop, left push):

    RPOPLPUSH aitor:enlaces pedro:visitados
    "aitor-iabd"
    

    El problema era doble. Por un lado, el comando se ha marcado como obsoleto, y por el otro, solo permitía sacar por la derecha e insertar por la izquierda. Por suerte, desde la versión 6.2, podemos emplear el comando LMOVE origen destino <LEFT | RIGHT> <LEFT | RIGHT>, el cual es más flexible al permitir sacar el elemento tanto de la izquierda como de la derecha y a su vez, volver a insertarlo a la izquierda o la derecha del destino. Así pues, podemos conseguir el mismo resultado mediante:

    LMOVE aitor:enlaces pedro:visitados RIGHT LEFT
    

Conjunto

  • Estructura

    Los conjuntos se parecen a las listas en que almacenan una colección de cadenas, pero los elementos no están ordenados ni repetidos.

    Sets en Redis
    Sets en Redis

    Además de añadir y eliminar elementos de un conjunto, podemos realizar varias operaciones de conjuntos, como la unión, la intersección, la diferencia etc.... Estas operaciones se realizan dentro de Redis y son muy rápidas. Lo que no podemos realizar es un acceso secuencial.

    ¿Cuándo debemos considerar el uso de conjuntos? Siempre que no nos importe la cardinalidad o cuántos elementos tenemos de una misma clave. Es más, en ocasiones necesitamos realizar un almacenamiento de elementos únicos, sin requerir la sobrecarga de tener que realizar una búsqueda para saber si el elemento existía previamente.

    Por ejemplo, podemos emplear conjuntos para almacenar la lista de usuarios únicos de una web, elegir el ganador de un sorteo así como encontrar usuarios o elementos que pertenecen a dos grupos distintos.

  • Comandos

    Para añadir elementos a un conjunto utilizaremos SADD, de manera que podemos añadir diversos valores a una clave, SMEMBERS para recuperar todos los valores, SISMEMBER para averiguar si un valor forma parte de un determinado conjunto:

    SADD noticias elpais.com eldiario.es xataka.com
    # (integer) 3       # Devuelve la cantidad de elementos añadidos
    SMEMBERS noticias   # Devuelve todos los miembros
    # 1) "elpais.com"
    # 2) "eldiario.es"
    # 3) "xataka.com    
    SISMEMBER noticias "xataka.com"  # Comprueba si "xataka.com" está en el conjunto
    # (integer) 1
    SISMEMBER noticias "eurogamer.es"
    # (integer) 0
    

    Si queremos recuperar una cantidad de elementos aleatorios de un conjunto usaremos SRANDMEMBER clave cantidad, donde si cantidad es positiva devolverá elementos sin repetir, pero si es negativa, permitirá elementos repetidos:

    SRANDMEMBER noticias 2
    # 1) "elpais.com"
    # 2) "xataka.com"
    SRANDMEMBER noticias -2
    # 1) "xataka.com"
    # 2) "xataka.com"
    

    También podemos emplear las operaciones matemáticas de conjuntos, mediante SINTER para la intersección, SDIFF la diferencia y SUNION para la union:

    SADD tech xataka.com eurogamer.es
    # (integer) 3
    SINTER noticias tech    # Enlaces que aparecen en los dos conjuntos
    # 1) "xataka.com    
    SDIFF noticias tech     # Noticias que no son de tecnología
    # 1) "elpais.com"
    # 2) "eldiario.es"
    SUNION noticias tech    # Todas las noticias, sin enlaces repetidos
    # 1) "elpais.com"
    # 2) "eldiario.es"
    # 3) "xataka.com"
    # 4) "eurogamer.es"
    # (integer) 0
    

    Todas las operaciones sobre conjuntos ofrecen la posibilidad de almacenar el resultado en uno nuevo, así pues, podemos emplear SINTERSTORE, SDIFFSTORE y SUNIONSTORE respectivamente:

    SUNIONSTORE sitiosweb noticias tech
    # (integer) 4
    SMEMBERS sitiosweb
    # 1) "elpais.com"
    # 2) "eldiario.es"
    # 3) "xataka.com"
    # 4) "eurogamer.es"
    

    Igual que usamops LMOVE para mover elementos entre listas, podemos emplear SMOVE para mover valores entre conjuntos. Otras operaciones que muy útiles son SCARD clave para averiguar su tamaño (cardinalidad), SPOP para sacar un elemento cualquiera (al no estar ordenados) o SREM clave valor si queremos eliminar un valor concreto.

    SCARD sitiosweb
    # (integer) 4
    SREM sitiosweb eldiario.es
    # (integer) 1
    SCARD sitiosweb
    # (integer) 3
    

Conjunto ordenado

  • Estructura

    Un conjunto ordenado también es una colección de cadenas únicas. A diferencia de un conjunto normal, los valores de un conjunto ordenado están ordenados (elemental, querido Watson)

    Sets ordenados en Redis
    Sets ordenados en Redis

    ¿Cómo se realiza la ordenación? La ordenación se basa en las puntuaciones y el orden léxico (orden alfabético) de las cadenas. Así, cada cadena es única, aunque las puntuaciones pueden coincidir. ¿Cómo se realiza la puntuación de estos valores? Redis proporciona operaciones para que los usuarios puedan manipular una puntuación para un elemento del conjunto.

    Los conjuntos ordenados son preferibles a las listas cuando el acceso rápido a un miembro concreto es más importante que la rapidez de inserción/eliminación, es decir, son más importantes las lecturas que las escrituras. Es por ello, que la complejidad temporal de la inserción/eliminación en un conjunto ordenado es O(N), donde N es el número de elementos del conjunto, ya que mantiene los valores ordenados, en vez la complejidad O(1) que tienen las listas. Así pues, podemos considerar un conjunto ordenado como una cola prioritaria de acceso aleatorio.

    Los conjuntos ordenados de Redis son ideales para casos de uso como rankings de productos más comprados, visitados, etc... así como sistemas con puntuación, tipo las preguntas de Stack Overflow o posts en Reddit.

  • Comandos

    Para añadir elementos a un conjunto ordenado usaremos la operacion ZADD clave puntuación valor, ZREM para eliminarlos, mientras que mediante ZRANGE clave inicio fin podemos recuperar las claves entre un rango inclusivo (si queremos que no entren inicio o fin en el rango, les pondremos un paréntesis delante) por posición o puntuación (BYSCORE), pudiendo recuperarlo en orden inverso (REV) y/o con su puntuación WITHSCORES:

    ZADD visitas 1000 aitor-bd 5000 aitor-iabd 66 aitor-blog
    # (integer) 3
    ZRANGE visitas 0 -1 WITHSCORES                  # Todos los elementos con puntuaciones
    # 1) "aitor-blog"
    # 2) "66"
    # 3) "aitor-bd"
    # 4) "1000"
    # 5) "aitor-iabd"
    # 6) "5000"
    ZRANGE visitas 50 1500 BYSCORE                  # Filtrado por puntuaciones
    # 1) "aitor-blog"
    # 2) "aitor-bd"
    ZRANGE visitas (66 1500 BYSCORE                 # No incluye el 66
    # 1) "aitor-bd"
    ZRANGE visitas (5000 66 REV BYSCORE WITHSCORES  # No incluye el 5000, en orden descendente
    # 1) "aitor-bd"
    # 2) "1000"
    # 3) "aitor-blog"
    # 4) "66"
    

    Han caído en desuso las operaciones ZREVRANGE, ZRANGEBYSCORE, ZREVRANGEBYSCORE, etc... ya que con ZRANGE y los parámetros adecuados podemos obtener los mismos resultados.

    Si lo que queremos es averiguar cuantos valores tienen una puntuación entre entre un rango determinado, usaremos ZCOUNT clave inicio fin:

    ZCOUNT visitas 50 1500
    # (integer) 2
    

    Para incrementar la puntuación de un elemento, podemos volver a añadirlo con la nueva puntuación, lo cual modifica la puntuación pero no añade un nuevo valor, o incrementarlo por un determinado valor mediante ZINCRBY, el cual devolverá un nuevo valor (o reducirlo mediante un valor negativo):

    ZINCRBY visitas 1 aitor-bd
    # "1001"
    

JSON

  • Estructura

    Redis permite el uso de JSON como un tipo de datos, permitiendo almacenar, modificar y recuperar valores JSON, lo que en parte la convierte en una base de datos documental.

    Un documento JSON está formado por pares de clave-valor, donde las claves son cadenas, pero los valores pueden ser de diferentes tipos, los cuales pueden ser: * numéricos: enteros y decimales * cadenas de texto * valores booleanos: true o false , null (valor nulo), objetos (conjuntos de pares clave-valor anidados) y arrays (listas ordenadas de valores, que pueden incluir cualquier combinación de los tipos anteriores).

    Toda la interacción de Redis con estructuras JSON se realizan con el prefijo JSON..

  • Comandos

    Los comandos que más vamos a emplear son JSON.SET para asignar una cadena JSON a una clave y JSON.GET para recuperar un valor, pudiendo realizar selección y filtrado de elementos.

    Como hemos comentado, para asignar un elemento usaremos el comando JSON.SET clave ruta valor, donde ruta será $ para indicar el raíz o una expresión JSONPATH si queremos añadir contenido en un lugar específico, y mediante valor le asignamos una cadena en formato JSON. Además, podemos pasar el parámetro NX para indicar que lo cree si no existe o XX para sobreescribir en caso de que ya exista. Veamos algunos ejemplos:

    JSON.SET usuario:1 $ '{"nombre": "Aitor Medrano", "altura": 182, "hobbies": ["baloncesto", "videojuegos"]}'
    # OK
    JSON.SET usuario:2 $ '{"nombre": "José Manuel Pérez"}' NX   # NX: Lo crea si no existe
    # OK
    JSON.SET usuario:2 $ '{"nombre": "Andreu Medrano"}' XX      # XX: Lo sobreescribe, sólo si ya existe. Si no, ni lo inserta
    

    Si queremos modificar un documento, además de JSON.SET para campos sencillos, podemos utilizar JSON.ARRAPPEND y JSON.ARRINSERT al trabajar con arrays para añadir, respectivamente, elementos al final o en una posición determinada:

    JSON.SET usuario:1 '$.altura' 183   # Modificamos un campo
    # OK
    JSON.SET usuario:1 '$.ciudad' '"Elche"'   # Agregamos un nuevo campo
    # OK
    JSON.ARRAPPEND usuario:1 '$.hobbies' '"big data"'
    # 1) (integer) 3
    JSON.ARRINSERT usuario:1 '$.hobbies' 1 '"música"'
    # 1) (integer) 4
    

    Para recuperar documentos usaremos JSON.GET clave [ruta], donde ruta es una expresión JSONPath:

    JSON.GET usuario:1
    # "{\"nombre\":\"Aitor Medrano\",\"altura\":183,\"hobbies\":[\"baloncesto\",\"m\xc3\xbasica\",\"videojuegos\",\"big data\"],\"ciudad\":\"Elche\"}"
    JSON.GET usuario:1 '$.nombre'       # Un campo sencillo
    # "[\"Aitor Medrano\"]"
    JSON.GET usuario:1 '$.hobbies'      # Un campo array
    # "[[\"baloncesto\",\"m\xc3\xbasica\",\"videojuegos\",\"big data\"]]"
    JSON.GET usuario:1 '$.hobbies[0]'   # El primer elemento del array
    # "[\"baloncesto\"]"
    JSON.GET usuario:1 '$.nombre' '$.ciudad'    # Varias rutas
    # "{\"$.nombre\":[\"Aitor Medrano\"],\"$.ciudad\":[\"Elche\"]}"
    

    También podemos emplear JSON.MGET para recuperar la misma ruta de varias claves:

    JSON.MGET usuario:1 usuario:2 '$.nombre'
    # 1) "[\"Aitor Medrano\"]"
    # 2) "[\"Andreu Medrano\"]"
    

    Finalmente, para eliminar elementos disponemos de JSON.DEL y para vaciarlos JSON.CLEAR (vacía los arrays y los objetos y pone a cero los valores enteros). En el caso de arrays, podemos emplear JSON.ARRPOP para sacar un elemento de una posición o JSON.ARRTRIM para recortar el array a una subconjunto de los elementos:

    JSON.DEL usuario:1 '$.ciudad'
    # (integer) 1
    JSON.CLEAR usuario:1 '$.altura'         # Ponemos la altura a 0
    # (integer) 1
    JSON.ARRPOP usuario:1 '$.hobbies' 0     # Sacamos el primer elemento
    # 1) "\"baloncesto\""
    JSON.GET usuario:1
    # "{\"nombre\":\"Aitor Medrano\",\"altura\":0,\"hobbies\":[\"m\xc3\xbasica\",\"videojuegos\",\"big data\"]}"
    JSON.ARRTRIM usuario:1 '$.hobbies' 1 2  # Nos quedamos con los elementos del 1 al 2
    # 1) (integer) 2
    JSON.GET usuario:1
    # "{\"nombre\":\"Aitor Medrano\",\"altura\":0,\"hobbies\":[\"videojuegos\",\"big data\"]}"
    

    Otras operaciones comunes son JSON.ARRLEN para averiguar el tamaño de un array o JSON.TYPE para obtener el tipo de un determinado campo.

JSONPath

En el módulo de lenguaje de marcas ya habéis utilizado JSONPath para hacer consultas sobre documentos JSON. Aún así, vamos a repasar su sintaxis, la cual se basa en el uso de selectores:

Símbolo Significado Ejemplo Explicación
$ Raíz del documento $.nombre Selecciona el campo "nombre" en la raíz
. Acceso a propiedades $.usuario.nombre Accede a propiedades anidadas
[] Indexación de arrays $.hobbies[0] Selecciona el primer elemento del array
* Comodín para todos los elementos $.usuario.* Selecciona todas las propiedades de usuario
.. Búsqueda recursiva $..nombre Busca "nombre" en cualquier nivel

Preparando datos

Vamos a crear un documento JSON complejo, con datos de dos usuarios con elmentos anidados y uso de arrays, para ilustrar diferentes consultas:

JSON.SET usuarios $ '{
    "usuarios": [
        {
            "id": 1,
            "nombre": "Aitor Medrano",
            "edad": 21,
            "contacto": {
                "email": "a.medrano@edu.gva.es",
                "telefonos": ["636 123456", "686 567890"]
            },
            "hobbies": ["programación", "música", "viajes"],
            "direcciones": [
                {"tipo": "casa", "ciudad": "Elche"},
                {"tipo": "trabajo", "ciudad": "Elche"}
            ]
        },
        {
            "id": 2,
            "nombre": "Juani Moya",
            "edad": 18,
            "contacto": {
                "email": "j.moya@edu.gva.es",
                "telefonos": ["696 543210"]
            },
            "hobbies": ["lectura", "música", "deporte"],
            "direcciones": [
                {"tipo": "casa", "ciudad": "Murcia"}, 
                {"tipo": "trabajo", "ciudad": "Elche"}
            ]
        }
    ]
}'

Para poder ejecutar la instrucción, bien necesitamos que esté en una única línea:

redis-cli JSON.SET usuarios $ '{"usuarios":[{"id":1,"nombre":"Aitor Medrano","edad":21,"contacto":{"email":"a.medrano@edu.gva.es","telefonos":["636 123456","686 567890"]},"hobbies":["programación","música","viajes"],"direcciones":[{"tipo":"casa","ciudad":"Elche"},{"tipo":"trabajo","ciudad":"Elche"}]},{"id":2,"nombre":"Juani Moya","edad":18,"contacto":{"email":"j.moya@edu.gva.es","telefonos":["696 543210"]},"hobbies":["lectura","música","deporte"],"direcciones":[{"tipo":"casa","ciudad":"Murcia"},{"tipo":"trabajo","ciudad":"Elche"}]}]}'

O podemos almacenar los datos en un archivo JSON (por ejemplo, usuarios.json) y ejecutarlo desde fuera del CLI mediante:

redis-cli JSON.SET usuarios $ "$(cat usuarios.json)"

Para nuestras primeras consultas, usaremos los selectores básicos:

JSON.GET usuarios '$.usuarios[0]'   # Recuperamos el primer usuario
# s"[{\"id\":1,\"nombre\":\"Aitor Medrano\",\"edad\":21,\"contacto\":{\"email\":\"a.medrano@edu.gva.es\",\"telefonos\":[\"636 123456\",\"686 567890\"]},\"hobbies\":[\"programaci\xc3\xb3n\",\"m\xc3\xbasica\",\"viajes\"],\"direcciones\":[{\"tipo\":\"casa\",\"ciudad\":\"Elche\"},{\"tipo\":\"trabajo\",\"ciudad\":\"Elche\"}]}]"
JSON.GET usuarios '$.usuarios[*].nombre' # Obtenemos el nombre de todos los usuarios
# "[\"Aitor Medrano\",\"Juani Moya\"]"
JSON.GET usuarios '$.usuarios[*].contacto.telefonos[0]' # Primer teléfono de cada usuario
# "[\"636 123456\",\"696 543210\"]"

Datos sin escapar

Si queremos recuperar los datos sin escapar las llaves, corchetes, etc..., desde fuera del cliente, podemos invocarlo pasándole la consulta y el parámetro --raw:

redis-cli --raw JSON.GET usuarios '$.usuarios[0]'
# [{"id":1,"nombre":"Aitor Medrano","edad":21,"contacto":{"email":"a.medrano@edu.gva.es","telefonos":["636 123456","686 567890"]},"hobbies":["programación","música","viajes"],"direcciones":[{"tipo":"casa","ciudad":"Elche"},{"tipo":"trabajo","ciudad":"Elche"}]}]

Por ejemplo, podemos filtrar los datos haciendo uso de expresiones de filtrado mediante ?() para restringir los documentos.

Las rutas dentro de la condición de filtro utilizan la notación de punto con @ para indicar el elemento actual del array o el valor actual del objeto, o $ para indicar el elemento de nivel superior. Es importante destacar que Redis no soporta el operador ?() para filtrar arrays directamente en la consulta.

Por ejemplo:

# Nombre de los usuarios mayores de 20 años
JSON.GET usuarios '$.usuarios[?(@.edad < 20)].nombre'      
# "[\"Juani Moya\"]"

# Nombre del usuario cuyo email es a.medrano@edu.gva.es
JSON.GET usuarios '$.usuarios[?(@.contacto.email == "a.medrano@edu.gva.es")].nombre'
"[\"Aitor Medrano\"]"

También podemos emplear .. para realizar búsquedas recursivas:

JSON.GET usuarios '$..nombre'   # Encontrar todos los elementos llamados "nombre"
# "[\"Aitor Medrano\",\"Juani Moya\"]"
JSON.GET usuarios '$..ciudad'   # Encontrar todas las ciudades
# "[\"Elche\",\"Elche\",\"Murcia\",\"Elche\"]"

Una vez visto estas operaciones, cabe destacar que:

  1. Redis tiene soporte limitado para JSONPath complejo, por lo que si necesitamos hacer consultas avanzadas, bien necesitaremos hacerlas mediante un lenguaje de programación para realizar el procesamiento adicional u optar por una solución documental como MongoDB.
  2. La eficiencia depende del tamaño y complejidad del documento

Otras estructuras de datos menos comunes son:

  • Bitmaps: estructura compacta que permite operaciones a nivel de bit.
  • HyperLogLog: estimación según valores unívocos
  • Stream: lista de strings o pares complejos de key-value

Puesto que Redis es un sistema de código abierto, hay muchos desarrolladores trabajando para ampliar el sistema. Los llamados módulos o ampliaciones aumentan el rango de funciones de la base de datos, que por lo demás es bastante sencilla, y adaptan el software a ámbitos de uso específicos.

Publicación-Suscripción

Existen diferentes sistemas de mensajería que permiten desacoplar el emisor de datos del receptor, como pueden ser RabbitMQ, Celery o incluso Apache Kafka en sistemas de Big Data. Si no necesitamos una solución tan potente, Redis ofrece un mecanismo de publicación-suscripción que permite a los clientes enviar y recibir mensajes en tiempo real.

Los clientes pueden suscribirse a canales y recibir mensajes publicados en esos canales por otros clientes. Este mecanismo es útil para implementar sistemas de mensajería, notificaciones en tiempo real, actualizaciones en tiempo real, etc.

Para ello, se emplean los siguientes comandos:

Así pues, mediante la publicación-suscripción en Redis podemos implementar:

  • Un sistema de notificaciones en tiempo real. Por ejemplo, se puede utilizar Redis para enviar notificaciones a los usuarios en tiempo real cuando se producen eventos importantes en la aplicación.
  • Un sistema de chat en tiempo real. Por ejemplo, se puede utilizar Redis para enviar mensajes de chat entre usuarios en tiempo real.

Persistencia

Por defecto, la gestión de la persistencia en Redis implica que los datos no se almacenan instantáneamente en disco, sino que se realiza de forma periódica o a petición del cliente.

Para ello, en redis.conf se especifica el número de cambios ocurridos antes de almacenar una instantánea de los datos en disco, lo que se conoce como la persistencia RDB (Redis DataBase). Los valores predeterminados son:

redis.conf
save 900 1      # Guarda cada 15 minutos si se modificó al menos 1 clave
save 300 10     # Guarda cada 5 minutos si al menos cambiaron 10 claves
save 60 10000   # Guarda cada 60 segundos si al menos cambiaron 10000 claves 

Esto puede provocar pérdida de datos en caso de fallo. Para evitarlo, Redis dispone del archivo appendonly.aof que guarda un registro para cada operación de escritura, lo que se conoce como persistencia AOF (Append Only File). Si el servidor cae antes de escribir en disco, cuando vuelva a estar disponible incluirá lo que esté pendiente.

redis.conf
appendonly yes
appendfsync everysec  # Sincroniza cada segundo (opciones: always, everysec, no)

La elección entre un mecanismo y otro va a depender de las necesidades de la aplicación, evaluando la criticidad de los datos y del rendimiento. RDB es más eficiente, al utilizar un archivo único compacto, ideal para copias de seguridad. En cambio, puede suponer una posible pérdida de datos entre instantáneas

En cambio, como AOF registra todas las operaciones de escritura, ofrece mayor durabilidad y menor pérdida de datos, pero empleando archivos más grandes y con un peor rendimiento.

En resumen

Redis destaca por su velocidad extrema, permitiendo realizar operaciones en cuestión de microsegundos. Su flexibilidad no se limita a un simple sistema clave-valor, sino que ofrece estructuras de datos avanzadas, lo que lo convierte en una herramienta versátil para distintas aplicaciones.

Además, permite la ejecución de operaciones atómicas, como el incremento de valores con INCR, garantizando la coherencia de los datos. Otra de sus ventajas es la gestión nativa del tiempo de vida de las claves (TTL), lo que facilita la expiración automática de datos.

Sin embargo, Redis también presenta ciertas limitaciones. Su principal restricción es que los datos deben almacenarse en memoria RAM, lo que impone un límite de capacidad. Además, no impone una estructura de datos estricta, ya que no cuenta con validación de esquemas o tipos. Sus opciones de consulta son limitadas, ya que solo permite búsquedas por clave o patrones simples. A diferencia de las bases de datos relacionales, no soporta integridad referencial, por lo que no se pueden definir claves foráneas ni realizar uniones (JOIN). También, dependiendo de su configuración, la durabilidad de los datos no está garantizada, existiendo la posibilidad de pérdida de información. Sus transacciones, aunque permiten la ejecución de múltiples operaciones, no disponen de un mecanismo de reversión (rollback) real.

Redis es la opción ideal cuando se requiere acceso a los datos con latencias mínimas, especialmente en aplicaciones donde los datos pueden modelarse con estructuras simples. Sus casos de uso más comunes son sistemas de caché, contadores, colas y almacenamiento de información en tiempo real con alta concurrencia. Además, su capacidad de gestionar automáticamente la expiración de datos lo convierte en una herramienta valiosa en escenarios donde la información tiene un ciclo de vida determinado.

Referencias

Actividades

  • AC1207. (RABD.7 // CE7c, CE7d, CE7e // 3p) Una vez puesto en marcha Redis, y utilizando cadenas cuyas claves utilicen el prefijo ac1207pelis:, se pide:

    1. Inserta 3 títulos de películas (por ejemplo, usando las claves ac1207pelis:1, ac1207pelis:2, etc...).
    2. Inserta 2 películas con una sola instrucción.
    3. Comprueba si existe la primera película que has insertado, así como una que no debería existir.
    4. Recupera mediante una sola instrucción las dos primeras películas.
    5. Recupera todas las claves.
    6. Recupera las dos primeras claves.
    7. Elimina la tercera película.
    8. Asigna un tiempo de vida de 30 minutos a la segunda película.
    9. Crea una nueva película con un tiempo de vida de dos horas.
    10. Comprueba el tiempo de vida de la segunda película.

  • AR1208. Utilizando las hashes en Redis con el prefijo ar1208pelis:, se pide:

    1. Crea un hash para añadir a la película ar1208pelis:1 la propiedad titulo.
    2. Para la misma película, mediante una única operación, añade las propiedades anyo y plataforma (plataforma de streaming donde hemos visto la película, o bien cine o tv dependiendo del caso).
    3. Recupera el año de la película.
    4. Recupera toda la información de la película.
    5. En otra clave, añade una nueva película con todas sus propiedades.
    6. Recupera todas las claves de la última película.
    7. Recupera todos los valores de la última película.
    8. En nuevas claves, añade dos películas más.
    9. Recupera los valores de la última película.
    10. Recupera todas las hashes existentes.
    11. Elimina el año de la última película.
    12. Averigua cuantos campos tiene la última película.
    13. Elimina la última película.
    14. Vuelve a recuperar todas las hashes existentes para comprobar que ya no existe.

  • AC1209. (RABD.7 // CE7c, CE7d, CE7e // 3p) Utilizando las listas en Redis, se pide:

    1. Crea una lista que permita almacenar las series que marca un usuario como ac1209watchlist.
    2. Añade 3 títulos de series a la lista por la izquierda.
    3. Añade 2 series a la lista por la derecha.
    4. Recupera el tamaño de la lista.
    5. Recupera todos las series.
    6. Recupera la primera y la última.
    7. Saca el último elemento insertado por la derecha.
    8. Comprueba si la lista tiene un elemento menos.

  • AP1210. (RABD.7 // CE7c, CE7d, CE7e // 3p) Utilizando las listas de Redis, se pide simular una cola. Para ello:

    1. Crea una cola llamada emails:pendientes.
    2. Añade 3 mensajes de email al final de la cola. Cada mensaje es un hash formado por para, asunto y texto.
    3. Procesa el primer mensaje (extráelo).
    4. Verifica cuántos mensajes quedan.
    5. Añade un mensaje prioritario al inicio de la cola.
    6. Procesa los mensajes restantes.
    7. Comprueba que quedan 0 mensajes.

  • AC1211. (RABD.7 // CE7c, CE7d, CE7e // 3p) Utilizando los conjuntos en Redis, se pide:

    1. Crea un conjunto que permita almacenar bases de datos NoSQL dentro de ac1211nosql.
    2. Añade al conjunto los valores Redis, MongoDB y Neo4j.
    3. Vuelve a añadir al conjunto el valor Redis.
    4. Recupera los elementos del conjunto.
    5. Crea otro conjunto que permita almacenar bases de datos relacionales dentro de ac1211sql.
    6. Añade al conjunto los valores MariaDB, PostgreSQL y Oracle.
    7. Comprueba si el conjunto ac1211sql contiene Redis.
    8. Crea el conjunto ac1211bd con la union de los dos conjuntos.
    9. Recupera cuantos elementos tiene el nuevo conjunto.
    10. Recupera los elementos del nuevo conjunto.
    11. Elimina del nuevo conjunto el valor Neo4j.
    12. Comprueba si hay algún elemento compartido entre los dos conjuntos iniciales.
    13. Recupera dos elementos aleatorios de cada conjunto (sin repetidos)
    14. Recupera dos elementos aleatorios de cada conjunto (con repetidos repetidos)

  • AP1212. (RABD.7 // CE7c, CE7d, CE7e // 3p) Utilizando los conjuntos ordenados en Redis, se pide:

    1. Crea el conjunto ordenado ap1212bd que permita almacenar bases de datos con una puntuación donde calificaremos nuestro conocimiento.
    2. Añade al conjunto los valores Redis con 50 puntos, Neo4j con 5 puntos, MariaDB con 90 puntos y MongoDB con 20 puntos.
    3. Recupera los elementos donde tengamos un conocimiento entre 30 y 95 puntos.
    4. Recupera los elementos donde tengamos un conocimiento entre 20 y 90 puntos, pero sin incluir esos valores.
    5. Recupera los elementos donde tengamos un conocimiento entre 5 y 100 puntos, con las puntuaciones asociadas.
    6. Recupera el número de elementos del conjunto ordenado.
    7. Recupera todos los elementos de menor a mayor conocimiento.
    8. Recupera todos los elementos de mayor a menor conocimiento.
    9. Elimina el elemento Neo4j.

  • AR1215. (RABD.1, RABD.7 // CE7c, CE7d, CE7e // 3p) De forma similar a las cheatsheets técnicas, crea una tabla resumen con los comandos más importantes que hemos trabajado en esta sesión.

  • AR1216. (RABD.1, RABD.7 // CE1g, CE1h, CE1j, CE7a, CE7b, CE7c, CE7d // 3p) Una vez finalizada la unidad, responde todas las preguntas del cuestionario inicial, con al menos un par de líneas para cada una de las cuestiones.