Usando Redis con PHP
Este artículo pretende ser la segunda parte del artículo anterior “Instalar Redis en Windows”.
El objetivo de este artículo es saber cómo trabajar con Redis usando PHP.
Anteriormente vimos como instalar Redis en Windows a través de Docker e instalamos la biblioteca de Redis para usarla con PHP. Sin embargo, lo dejamos ahí, solo hicimos una pequeña introducción a modo de ejemplo y ya está. Así que sigamos…
Añadir, obtener y eliminar claves
Como ya vimos en la primera parte del articulo, en el ejemplo que puse, para crear y obtener una clave usábamos set() y get() respectivamente.
set: Almacena un valor.
// Almacenar un valor $redis->set('saludar', 'Hola desde PHP');
get: Recupera el valor con la clave dada.
// Recuperar el valor $valor = $redis->get('saludar'); echo "El valor almacenado es: $valor\n";
Y para eliminar una clave, simplemente usamos… del() ¿Qué lógico no? 😝
$redis->del('nombre_clave');
Listas
Una lista es una colección ordenada de elementos que puede contener elementos duplicados y mantiene el orden de inserción. Pero muy importante y a tener en cuenta es que solo admite cadenas de texto. Si queremos almacenar números, por ejemplo, Redis los convertirá a texto. No solo ocurre con las listas, sino que Redis almacena todo como cadenas de texto en cualquier estructura de datos.
Para crear una lista lo podemos hacer de dos maneras.
La primera y la forma más natural de hacerlo es usando rPush().
rPush(): Añade los elementos al final de la lista. O, en otras palabras, en orden de llegada.
Por ejemplo:
$redis->rPush('colores', 'azul'); // ['azul'] $redis->rPush('colores', 'rojo'); // ['azul', 'rojo'] $redis->rPush('colores', 'verde'); // ['azul', 'rojo', 'verde']
O también podemos hacer:
$redis->rPush("colores", "azul", "rojo", "verde");
El resultado sería: azul, rojo, verde.
lPush(): Añade los elementos al inicio de la lista. En otras palabras, construye la lista en orden inverso dando prioridad a los elementos más recientes.
Por ejemplo:
$redis->lPush('colores', 'azul'); // ['azul'] $redis->lPush('colores', 'rojo'); // ['rojo', 'azul'] $redis->lPush('colores', 'verde'); // ['verde', 'rojo', 'azul']
Para acceder a un elemento de la lista, usamos índices.
$elemento = $redis->lIndex("colores", 1); // Obtiene el segundo elemento echo "Elemento en el índice 1: $elemento\n"; // rojo
Si queremos recorrer una lista entera usaremos la función lRange():
$colores = $redis->lRange("colores", 0, -1); print_r($colores);
Lo que hace esta función es obtener un rango de elementos de una lista, especificando los índices de inicio y fin del rango que queremos obtener.
Con el parámetro ‘0’ le indicamos que comience a obtener elementos desde el índice cero y, mediante ‘-1’, le indicamos que obtenga todos los elementos de la lista.
Si por ejemplo quisiéramos obtener sólo algunos elementos, por ejemplo, los dos primeros, deberíamos de hacer:
$colores = $redis->lRange("colores", 0, 1); print_r($colores);
Para conocer el tamaño de una lista se usa la función lLen():
$tamano = $redis->lLen("colores"); echo "Tamaño de la lista: $tamano\n";
Y para eliminar un elemento de la lista usaremos lRem():
$redis->lRem("colores", "rojo", 1);
Esto elimina el valor o elemento “rojo” solo una vez. ¿Qué quiere decir esto?
Pues que lRem funciona buscando coincidencias con un valor dado (en este caso rojo) y eliminando un número definido de ocurrencias (en nuestro caso una vez). Si queremos eliminar todos los elementos “rojo” de la lista (suponiendo que tuviéramos más) bastaría con poner el valor del tercer parámetro a ‘0’.
Sets o conjuntos
Un conjunto, a diferencia de una lista, es una colección desordenada de elementos únicos.
Para crear un conjunto usamos sAdd():
$redis->sAdd('colores', 'azul', 'rojo', 'verde', 'rojo');
En el ejemplo, hemos repetido el color rojo, sin embargo, dado que un conjunto solo permite elementos únicos nos mostrará ‘rojo’ una vez.
Para recorrer un conjunto tenemos varias maneras.
La primera es mediante la función sMembers():
$redis->sAdd('colores', 'azul', 'rojo', 'verde', 'rojo'); // Obtener todos los elementos del conjunto $colores = $redis->sMembers('colores'); // Recorrer el conjunto foreach ($colores as $color) { echo "Color: $color <br>"; }
La segunda manera es mediante la función sScan():
$iterator = null; while ($element = $redis->sScan('colores', $iterator)) { foreach ($element as $color) { echo "Color: $color <br>"; } }
Casi siempre usaremos la función sMembers(), a excepción de cuando tengamos un conjunto con una gran cantidad de elementos, entendiéndose “gran cantidad” como miles o millones de elementos que, en ese caso, usaremos sScan() por ser más eficiente, ya que permite iterar sobre los elementos en lugar de cargarlos todos en memoria.
¿Y si queremos comprobar si existe un elemento en la colección?
Para eso usamos la función de pertenencia llamada sIsMember():
$redis->sAdd('colores', 'azul', 'rojo', 'verde', 'rojo'); if ($redis->sIsMember('colores', 'verde')) { echo 'Verde está en el conjunto.'; }
Unión, intersección y diferencia
Cuando trabajamos con conjuntos también puede resultarnos útil la unión de conjuntos, la intersección y la diferencia.
No es otra cosa que combinar todos los elementos únicos de dos o más conjuntos.
Por ejemplo, si tenemos dos usuarios que comparten intereses o aficiones, podemos hacer así:
// Añadir conjuntos $redis->sAdd('usuario1', 'fútbol', 'música', 'cine', 'beisbol'); $redis->sAdd('usuario2', 'cine', 'videojuegos', 'fútbol', 'baloncesto'); // Unión de los conjuntos $union = $redis->sUnion('usuario1', 'usuario2'); print_r($union); // Resultado: ["fútbol", "música", "cine", "beisbol", "videojuegos", "baloncesto"]
Como se observa, obtenemos una lista de intereses únicos.
Ahora, en lugar de obtener elementos únicos, obtenemos los elementos presentes en todos los conjuntos. ¡Vaya, los que se repiten!
Esto puede ser útil para, por ejemplo, encontrar cosas en común. Siguiendo con el ejemplo de los usuarios, podemos saber qué intereses comparten:
// Añadir conjuntos $redis->sAdd('usuario1', 'fútbol', 'música', 'cine', 'beisbol'); $redis->sAdd('usuario2', 'cine', 'videojuegos', 'fútbol', 'baloncesto'); // Intersección de los conjuntos $interseccion = $redis->sInter('usuario1', 'usuario2'); print_r($interseccion); // Resultado: ["fútbol", "cine"]
Como vemos, obtenemos los intereses en común que son fútbol y cine.
Se trata de obtener los elementos que están en el primer conjunto, pero no en los otros.
Siguiendo con el ejemplo de los usuarios, puede resultarnos útil saber qué interés tiene un usuario que el otro no tenga.
// Añadir conjuntos $redis->sAdd('usuario1', 'fútbol', 'música', 'cine', 'beisbol'); $redis->sAdd('usuario2', 'cine', 'videojuegos', 'fútbol', 'baloncesto'); // Diferencia entre los conjuntos $diferencia = $redis->sDiff('usuario1', 'usuario2'); print_r($diferencia); // Resultado: ["música", "beisbol"]
Ahora vemos que el usuario1, tiene como intereses la música y el beisbol, cosa que el usuario2 no tiene.
Trabajar con sorted sets (zsets)
Recordemos que un conjunto es una colección desordenada de elementos únicos. Pues bien, los zsets o también llamados sorted sets, no son otra cosa que conjuntos; pero ordenados y únicos. Para determinar el orden, es decir, su posición en el conjunto, se usa un puntaje numérico que se asocia a cada elemento (llamado “score”).
Así que la diferencia con los conjuntos normales es: el orden mediante un valor numérico asociado.
Por ejemplo, imagina que tenemos una tabla de puntuaciones de un juego en el que cada jugador a obtenido una puntuación.
Por ejemplo:
$redis->zAdd('ranking', 120, 'oscar'); $redis->zAdd('ranking', 200, 'maria'); $redis->zAdd('ranking', 150, 'david'); // Ranking de mayor a menor: $ranking = $redis->zRevRange('ranking', 0, -1, ['withscores' => true]); print_r($ranking);
El resultado sería:
Array ( [maria] => 200 [david] => 150 [oscar] => 120 )
Como vemos, la función zAdd se usa para añadir un elemento junto con su score y, la función zRevRange para obtener los elementos de mayor a menor score.
El parámetro withscores indica que queremos recuperar los elementos de un Zsets junto con sus puntuaciones. Si no le pasamos este parámetro y hacemos:
$ranking = $redis->zRevRange('ranking', 0, -1); print_r($ranking);
Redis por defecto solo nos devolverá los elementos. Es decir, el resultado sería:
Array ( [0] => maria [1] => david [2] => oscar )
Por cierto, actualmente zRevRange y zRangeByScore estan obsoletas, pero en la biblioteca de redis para PHP se siguen usando. Tranquil@, no influye en temas de seguridad ni nada por el estilo. Por lo que he podido leer en “los foros” de github, según dice el desarrollador principal, el motivo de que se siga usando en la biblioteca es por temas de compatibilidad con versiones anteriores. Como ya digo, no te tiene que preocupar pues no afecta a la seguridad. Y por otro lado, el motivo de declararla obsoleta por parte de Redis, entiendo que es para evitar código redundante…
¡Continuemos!
Si por el contrario, quisiéramos ordenar el ranking de menor a mayor, usaríamos la función zRange:
// Ordenados de menor a mayor: $ranking = $redis->zRange('ranking', 0, -1, ['withscores' => true]); print_r($ranking);
Algunos métodos más son:
zScore: Para obtener la puntuación, o, mejor dicho, el score de un elemento específico.
// zScore $score = $redis->zScore('ranking', 'david'); // Resultado: 150 print_r($score);
zRank y zRevRank: Se encargan de obtener la posición de un elemento en el ranking (orden ascendente o descendente respectivamente).
// zRank y zRevRank $posicion = $redis->zRank('ranking', 'david'); // Resultado: 1 (2da posición en base 0) $posicion_inversa = $redis->zRevRank('ranking', 'david'); // Resultado: 1 (2da posición desde arriba)
zRem: Elimina un elemento del zset.
// zRem $redis->zRem('ranking', 'maria');
Una vez eliminado con zRem, recuerda volver a consultar el Zsets actualizado para ver el ranking actualizado.
zRangeByScore: busca elementos en un rango de puntuaciones.
// zRangeByScore $jugadores_rango = $redis->zRangeByScore('ranking', 150, 200, ['withscores' => true]); print_r($jugadores_rango);
El resultado sería: Array ( [david] => 150 [maria] => 200 )
Hashes
Un hash podemos decir que equivale a un array asociativo en PHP o mejor aún, a un diccionario en Python.
Un hash es una estructura de datos que almacena un conjunto de pares clave-valor asociados con una clave principal.
Las siguientes funciones están relacionadas con los hashes.
hSet: Establecemos el valor de un campo o lo que es lo mismo, creamos el hash.
$redis->hSet('usuario:1', 'nombre', 'Óscar'); $redis->hSet('usuario:1', 'pais', 'España'); $redis->hSet('usuario:1', 'idioma', 'Español');
hGet: Se encarga de obtener el valor del campo especificado.
echo "Nombre: " . $redis->hGet('usuario:1', 'nombre');
Resultado: Nombre: Óscar
hMGet: Con esta función obtenemos los valores de los campos que nos interesan de un hash.
A diferencia de hGet, obtenemos en una misma línea múltiples valores.
$usuario = $redis->hMGet('usuario:1', ['nombre', 'pais']); print_r($usuario);
En el ejemplo, solo obtendríamos nombre y país.
hGetAll: Obtiene todos los campos y valores del hash.
// Obtener todos los campos y valores $usuario = $redis->hGetAll('usuario:1'); print_r($usuario);
hDel: Se encarga de eliminar un campo del hash.
$redis->hDel('usuario:1', 'idioma');
hKeys: Devuelve todas las claves (campos) del hash. En lugar de obtener los valores de los campos, obtenemos los nombres de los mismos.
$campos = $redis->hKeys('usuario:1'); print_r($campos);
hVals: A diferencia de hKeys, este método devuelve todos los valores del hash.
$valores = $redis->hVals('usuario:1'); print_r($valores);
hExists: Sirve para comprobar si un campo existe en el hash.
if ($redis->hExists('usuario:1', 'edad')) { echo "El campo 'edad' existe."; } else { echo "El campo 'edad' no existe."; }
hLen: Devuelve el número total de campos que hay en el hash.
$numeroCampos = $redis->hLen('usuario:1'); echo "El hash tiene $numeroCampos campos.";
Como vemos, los hashes resultan útiles cuando queremos almacenar entidades con varias propiedades. Por ejemplo, la información de un usuario, un producto, etc.
Expiración de claves
Para ir finalizando el artículo, que ya va siendo hora 😅 vamos a ver qué es la expiración de claves.
La expiración de claves como su propio nombre indica, permite establecer una fecha de expiración para las claves almacenadas.
Son útiles por ejemplo para almacenar datos temporales como los resultados de una consulta, tokens de sesión, códigos de verificación, etc.
Para empezar, debemos usar el método expire que se encarga de establecer un tiempo de vida en segundos para una clave.
$redis->set('mensaje', 'Este mensaje se autodestruirá en 10 segundos ;-)'); $redis->expire('mensaje', 10);
O bien podemos usar setext para hacerlo todo en una misma línea de código:
$redis->setex('mensaje_temporal', 15, 'Mensaje temporal');
Sobre la expiración de claves, en el ejemplo hemos usado set, pero se aplica también a listas, conjuntos o hashes.
ttl: Para conocer el tiempo de vida restante de una clave en segundos.
echo "Tiempo restante: " . $redis->ttl('mensaje') . " segundos";
persist: Elimina el tiempo de expiración de una clave y la convierte a ser persistente.
$redis->persist('mensaje'); echo "La clave 'mensaje' ahora es permanente.";
Y ya está, por ahora eso es todo. Ahora sabemos cómo instalar Redis en Windows y como usarlo con PHP.
Recuerda que este articulo pretende formar parte de una serie de artículos que pretendo hacer sobre algunas de las bases de datos NoSQL.
Espero que te haya gustado y te sea útil 😉
Sobre el autor
Este artículo está publicado bajo una licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional . Puedes compartirlo y adaptarlo, incluso con fines comerciales, siempre que cites al autor y mantengas esta misma licencia.