Sus múltiples casos de usos y su excepcional rendimiento hacen que Redis sea hoy una pieza clave en la arquitectura de aplicaciones altamente dinámicas.
En la charla se expone de forma práctica cómo puede integrarse Redis en una aplicación Symfoy y cómo pueden implementarse varias de las características de las aplicaciones usando Redis, como por ejemplo: Session storage, Monolog logging handlers, Doctrine caching, SwiftMailer spooling, Profiler storage, Data Collector for Symfony2 Profiler.
Además de estos casos de uso generales, se comentan otros casos de usos específicos de aplicaciones que por su naturaleza no pueden beneficiarse de una capa de cache y se requiere por tanto una herramienta eficiente y escalable para resolver ciertos tipos de problemas.
3. ¿Quién soy?
• Backend Core Tech Lead @ Socialpoint
Arquitectura y desarrollo de aplicaciones que
eficientemente respondan peticiones de millones de
usuarios cada día
• Redis Fan
• Made in Cuba
• Poco original eligiendo memes
• @ronnylt
Monday, June 24, 13
18. Ideal para
• Analíticas real-time
• Tracking
• Caching server (memcached on steroid)
• Colas de trabajo
• Escritura/Lectura intensiva (sesiones)
Monday, June 24, 13
20. Claves y valores
• Los datos (values) son refereciados a través
de claves (keys)
• Los datos pueden ser recuperados solo si
conocemos el nombre de la clave
Monday, June 24, 13
21. Claves
(keys)
• Únicas dentro de la BD
• Binary safe string
• Claves muy grandes pueden impactar en el
rendimimiento
• Claves muy pequeñas no aportan mucho
(u:123:n vs user:123:name)
Monday, June 24, 13
22. No es un RMDBS
• No hay consultas (queries)
• No hay índices
• No hay esquemas
Monday, June 24, 13
24. Comandos
• Lenguaje de comandos fácil de usar y de
aprender
• Los comandos (en su mayoría) son aplicables
a un tipo de datos específico
Monday, June 24, 13
26. Cadenas
• Tipo de dato simple (cualquier cadena binary-safe)
• Tamaño máximo de 512 MB
key string
GET, SET, STRLEN,APPEND, GETRANGE, SETRANGE
http://redis.io/commands#string
Monday, June 24, 13
27. Casos de uso cadenas
• Almacenamiento de cualquier dato (serializado):
GET, SET
• Vector de acceso aleatorio con GETRANGE,
SETRANGE
• Mapa de bits usando GETBIT, SETBIT,
BITCOUNT
Monday, June 24, 13
28. Casos de uso cadenas
• Contadores atómicos con:
INCR, DECR
INCRBY, DECRBY
INCRFLOATBY
INCR dowloads:item:123
=> 450
INCR dowloads:item:123
=> 451
Monday, June 24, 13
29. Listas
• Listado de cadenas donde el orden es importante
• Operaciones de inserción por la izquierda y por
la derecha o por posición
• Máxima longitud de 2^32 -1 (+4 billones)
key s2s1 s3...
http://redis.io/commands#list
Monday, June 24, 13
30. Casos de uso Listas
• Representación de colas (insertando por la
derecha, leyendo por la izquierda) RPUSH, LPOP
• Representación de pilas (insertando y leyendo por
la izquierda) LPUSH, LPOP
• Comandos blocking BLPOP, BRPOP, BRPOPLPUSH
Monday, June 24, 13
31. Conjuntos
• Colección de elementos únicos donde el
orden no importa
• Operaciones típicas de conjuntos sobre los
datos
key
blue
green
red
black
Monday, June 24, 13
33. Casos de uso
Conjuntos
• Representación de relaciones
• Tracking de sucesos únicos
• Cualquier problema donde por su naturaleza se
realicen operaciones sobre conjuntos
Monday, June 24, 13
34. Conjuntos Ordenados
• Conjuntos de datos, pero ordenados por
un score
• Elementos únicos dentro del conjunto, cada
uno con un score asignado
key
blue – 520
green – 890
red – 303
black – 680
Monday, June 24, 13
35. Conjuntos Ordenados
• Operaciones de conjuntos aplicables
• Operaciones de acceso por score y por rango en
tiempo constante y predecible
http://redis.io/commands#sorted_set
Monday, June 24, 13
36. Casos de uso
Conjuntos Ordenados
• Leaderboards
• Rankings
• Tracking basado en tiempo
Monday, June 24, 13
37. Hashes
• Múltiples campo => valor en una misma clave
• Hasta un máximo de 2^32 -1 pares campo => valor
key field1 value1
field2 value2
field3 value3
http://redis.io/commands#hash
Monday, June 24, 13
38. Hashes
• Puede verse como un arreglo asociativo en PHP:
clave => [
campo1 => valor1,
campo2 => valor2,
campo3 => valor3
]
• Operaciones sobre campos individuales
Monday, June 24, 13
39. Casos de uso Hashes
• Almacenamiento de objetos compuestos
por varios campos
• Mappings
Monday, June 24, 13
40. Resumiendo...
• Tenemos la oportunidad de usar la estructura
de datos adecuada para cada tipo de problema
• Tendremos tiempos de ejecución constantes y
predecibles, sin importar el tamaño de los
conjuntos de datos (dataset)
Monday, June 24, 13
42. Clientes para PHP
• https://github.com/nrk/predis
• https://github.com/nicolasff/phpredis
Clientes disponibles para la mayoría de los lenguajes de
programación (http://redis.io/clients)
Monday, June 24, 13
43. Predis
"require": {
"predis/predis": "~0.8.3"
},
• Escrito en PHP
• Maduro y activamente mantenido
• Extensible
• Feature-complete (pipelines, client side sharding,
server profiles, master/slave config, etc.)
Monday, June 24, 13
44. phpredis
• Escrito en C como una extensión PHP
• Listo para producción
• Extremadamente rápido
• No backward compatible con anteriores
versions de Redis
Monday, June 24, 13
45. ¿Cuál usar?
• Depende...
• Predis cubre la mayoría de las necesidades,
fácil de instalar con composer, y nos ofrece
un rendimiento aceptable
• phpredis si necesitas un rendimiento
excepcional
Monday, June 24, 13
46. La latencia de red sigue
siendo el principal
“performance killer”,
no el cliente
Monday, June 24, 13
53. Obteniendo el cliente a
través del container
$redis = $container->get('snc_redis.default');
$key = 'downloads:' . $pid . ':count'
$downloads = $redis->incr($key);
php app/console container:debug | grep snc_redis
Monday, June 24, 13
54. Clientes registrados
como servicios
php app/console container:debug
snc_redis.default
Information for service snc_redis.default
Service Id snc_redis.default
Class PredisClient
Tags -
Scope container
Public yes
Synthetic no
Required File -
Monday, June 24, 13
55. Inyectando Redis como
dependencia
namespace AcmeDemoBundleService;
use SncRedisBundleClientPredis as Redis;
class DownloadCounter
{
protected $redis;
public function __construct(Redis $redis)
{
$this->redis = $redis;
}
public function count($itemId)
{
return $this->redis->incr('downloads:' . $itemId . ':count');
}
}
Monday, June 24, 13
56. Inyectando Redis como
dependencia
<service id="acme.demo.download_counter"
class="AcmeDemoBundleServiceDownloadCounter">
...
<argument type="service" id="snc_redis.default" />
...
</service>
Monday, June 24, 13
58. Sesiones
• Difícil de escalar con la configuración por
defecto
• Por naturaleza no cacheable (read-change-
write back)
• Se necesita mantener estado consistente
Monday, June 24, 13
63. Sesiones en Redis
• Sesiones distribuídas (ej. detrás de un
balanceador sin sticky sessions)
• Excepcional rendimiento de escritura/lectura
• Tiempo de acceso constante y predecible
• Escalable horizontalmente (client-side sharding)
Monday, June 24, 13
64. Session handlers
• A través de un session handler implementado
en PHP, conectando a través de un cliente
Redis
• A través de un session handler implementado
en una extensión de PHP (phpredis)
Monday, June 24, 13
66. Usando phpredis
framework:
session:
# Default storage service
storage_id: "session.storage.native"
# No handler service, use default
handler_id: ~
# The name for the session cookie
name: "appsesid"
config.yml
Monday, June 24, 13
70. Monolog logging
• Los mensajes de logs son almacenados en
una lista
• Ideal cuando se necesita un broker que
reciba los logs que serán posteriormente
enviados a un agregador (ej. logstash)
Monday, June 24, 13
74. SwiftMailer Spooling
• Los mensajes no se envian directamente, sino que
se mantienen en un “spool” y son enviados por un
proceso en background
• Usando redis como “spool”, los mensajes son
mantenidos en una lista hasta que son enviados
Monday, June 24, 13
79. Desventajas
• Difícil de cambiar la configuración de routing
una vez creadas
• Es requerido tener copia de la base de datos
de routings en los ambientes de desarrollo
para que la aplicación funcione
Monday, June 24, 13
80. Solución alternativa
• Crear un mapping entre rutas amigables y
rutas internas
• Subscribirse a KernelEvents::REQUEST y
antes que nada, cambiar pathInfo en el objeto
Request
Monday, June 24, 13
88. ¿Quién está online?
• Mantenemos un conjunto con los usuarios
que han estado online por cada minuto
• En cada request, agregamos al usuario al
conjunto de usuarios online del minuto
actual
• Obtenemos los usuarios que han estado
online de la unión de los 5 últimos
conjuntos
Monday, June 24, 13
90. Amigos online
• Mantenemos un conjunto con los amigos
de cada usuario
• Obtenemos los amigos que están online, de
la intersección del conjunto de usuarios
online con el conjunto de amigos de un
usuario
Monday, June 24, 13
92. class OnlineUsersManager
{
protected $redis;
public function __construct(Redis $redis)
{
$this->redis = $redis;
}
public function trackUser($userId)
{
$timestamp = time();
$minute = date('i', $timestamp);
$key = 'online_users:' . $minute;
// Add the user to the set of online users in the
current minute.
$this->redis->sadd($key, $userId);
// Expire in 10 minutes.
$this->redis->expire(60 * 10);
}Monday, June 24, 13
93. Leaderboards
• Caso de uso típico el cual es fácil de implementar
con Redis y díficil de implementar de forma
eficiente en otro sistema
• Los conjuntos ordenados son las estructuras de
datos perfectas para su implementación
Monday, June 24, 13
95. class RankingManager
{
protected $redis;
public function __construct(Redis $redis)
{
$this->redis = $redis;
}
public function onCombatFinished(Combat $combat)
{
$winner = $combat->getAttacker();
$score = $combat->getScoreResult();
$this->redis->zincr('ranking', $score, $winner->getId());
}
public function getTopScores($limit)
{
return $this->redis->zrevrange('ranking', 0, $limit);
}
}
Monday, June 24, 13
96. Extra Tips
• Redis es single-threaded, todos los
comandos son atómicos
• Transacciones pueden usarse para ejecutar
múltiples comandos de forma atómica
• Lua scripts se ejecutan de forma atómica
también
•
Monday, June 24, 13
97. Performance Tips
• Usando client-side sharding puede escalarse
horizontalmente ganando en capacidad y
rendimiento
• Ejecutar múltiples comandos a través de
pipelines
Monday, June 24, 13
99. NO usar Redis
• Cuando el conjunto de datos (dataset) no
cabe en memoria
• Datos de naturaleza relacional
• Cuando no se conoce de antemano como
van a consultarse los datos
Monday, June 24, 13
100. Cuándo usar Redis
• Redis para datos temporales, altamente dinámicos
y estructuras de datos complejas
• Datos de naturaleza no-relacional
• Ideal para aplicaciones que son write-heavy
• Datos que naturalmente se ajustan a una
estructura de Redis
Monday, June 24, 13
101. Inegrando Redis en un
stack PHP/Symfony2
• No necesariamente como la DB principal
• Resolviendo problemas que son difíciles de
resolver en un sistema relacional
• Beneficiandonos de las características de Redis de
forma incremental
• Usando la herramienta adecuada para cada tarea
Monday, June 24, 13