Slide 1

Slide 1 text

Monday, June 24, 13

Slide 2

Slide 2 text

Monday, June 24, 13

Slide 3

Slide 3 text

¿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

Slide 4

Slide 4 text

Hablemos de algunos de nuestros desafíos como desarrolladores Monday, June 24, 13

Slide 5

Slide 5 text

Tenemos aplicaciones que por su naturaleza no es posible usar una cache Monday, June 24, 13

Slide 6

Slide 6 text

Tenemos cientos, miles, de usuarios concurrentes y necesitamos una solución escalable para almacenar las sesiones Monday, June 24, 13

Slide 7

Slide 7 text

Queremos saber quién y cómo se está usando nuestra aplicación Monday, June 24, 13

Slide 8

Slide 8 text

Redis WTF? Redis FTW! Monday, June 24, 13

Slide 9

Slide 9 text

Agenda • Redis y sus características • Entendiendo Redis • Conectando desde PHP • Integrando Redis en Symfony2 • Casos de uso Monday, June 24, 13

Slide 10

Slide 10 text

REDIS Y SUS CARACTERÍSTICAS Monday, June 24, 13

Slide 11

Slide 11 text

¿Qué es Redis? • REmote DIctionary Server • Creado en 2009 por Salvatore Sanfilipo (@antirez) • Open source Monday, June 24, 13

Slide 12

Slide 12 text

Mejor definido como: NoSQL Monday, June 24, 13

Slide 13

Slide 13 text

Monday, June 24, 13

Slide 14

Slide 14 text

advanced in-memory key-value data-structure server Redis Monday, June 24, 13

Slide 15

Slide 15 text

Data Structure Server • Cadenas • Listas • Conjuntos • Conjuntos ordenados • Hashes (hash maps) Monday, June 24, 13

Slide 16

Slide 16 text

In-memory Database • Datos deben caber en memoria • Persistencia configurable • “Memory is the new disc, disc is the new tape” Monday, June 24, 13

Slide 17

Slide 17 text

Advanced key-value store database • Persistencia (snapshot, append-only file) • Replicación (master/slave) • Transacciones • Pipelining • Publisher/Subscriber (pub/sub) • Lua scripting Monday, June 24, 13

Slide 18

Slide 18 text

Ideal para • Analíticas real-time • Tracking • Caching server (memcached on steroid) • Colas de trabajo • Escritura/Lectura intensiva (sesiones) Monday, June 24, 13

Slide 19

Slide 19 text

ENTENDIENDO REDIS Monday, June 24, 13

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

No es un RMDBS • No hay consultas (queries) • No hay índices • No hay esquemas Monday, June 24, 13

Slide 23

Slide 23 text

Monday, June 24, 13

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

Tipos de datos Cadenas Listas Conjuntos Conjuntos ordenados Hashes Data structure server Monday, June 24, 13

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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 s2 s1 s3 ... http://redis.io/commands#list Monday, June 24, 13

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

Operaciones de conjuntos SINTERSECT SUNION SDIFF Monday, June 24, 13

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

Casos de uso Conjuntos Ordenados • Leaderboards • Rankings • Tracking basado en tiempo Monday, June 24, 13

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

Hashes • Puede verse como un arreglo asociativo en PHP: clave => [ campo1 => valor1, campo2 => valor2, campo3 => valor3 ] • Operaciones sobre campos individuales Monday, June 24, 13

Slide 39

Slide 39 text

Casos de uso Hashes • Almacenamiento de objetos compuestos por varios campos • Mappings Monday, June 24, 13

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

CONECTANDO DESDE PHP Monday, June 24, 13

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

¿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

Slide 46

Slide 46 text

La latencia de red sigue siendo el principal “performance killer”, no el cliente Monday, June 24, 13

Slide 47

Slide 47 text

INTEGRANDO REDIS EN SYMFONY2 Monday, June 24, 13

Slide 48

Slide 48 text

Monday, June 24, 13

Slide 49

Slide 49 text

SncRedisBundle { "require": { "snc/redis-bundle": "1.1.*" } } https://github.com/snc/SncRedisBundle Monday, June 24, 13

Slide 50

Slide 50 text

SncRedisBundle • Integra Predis y phpredis en Symfony2 • Soporte para: • Session storage • Monolog logging handler • SwiftMailer Spooling • Doctrine caching Monday, June 24, 13

Slide 51

Slide 51 text

Definiendo clientes snc_redis: clients: default: type: predis alias: default dsn: redis://redis.example.com session: type: predis alias: session dsn: - redis://rses1.example.com - redis://rses2.example.com config.yml / redis.yml Monday, June 24, 13

Slide 52

Slide 52 text

Configuración avanzada snc_redis: clients: cache: type: predis alias: cache dsn: - redis://cache1.example.com - redis://cache2.example.com options: profile: 2.6 connection_timeout: 10 readwrite_timeout: 30 config.yml / redis.yml Monday, June 24, 13

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

Clientes registrados como servicios php app/console container:debug snc_redis.default Information for service snc_redis.default Service Id snc_redis.default Class Predis\Client Tags - Scope container Public yes Synthetic no Required File - Monday, June 24, 13

Slide 55

Slide 55 text

Inyectando Redis como dependencia namespace Acme\DemoBundle\Service; use Snc\RedisBundle\Client\Predis 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

Slide 56

Slide 56 text

Inyectando Redis como dependencia ... ... Monday, June 24, 13

Slide 57

Slide 57 text

Sesiones Monday, June 24, 13

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Estado inconsistente en cada nodo (no sticky sessions) Monday, June 24, 13

Slide 60

Slide 60 text

• Difícil de escalar con mucho tráfico • Escrituras en cada request • Replication lag Monday, June 24, 13

Slide 61

Slide 61 text

• In-memory sessions • Tiempo de acceso constante y predecible • Escala horizontalmente Monday, June 24, 13

Slide 62

Slide 62 text

Monday, June 24, 13

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

Usando Predis snc_redis: clients: session_cluster: type: predis alias: session dsn: - redis://sess000.example.net - redis://sess001.example.net - redis://sess002.example.net session: client: session_cluster ttl: 1200 prefix: appsession config.yml Monday, June 24, 13

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

php.ini usando phpredis session.save_handler = redis session.save_path = " tcp://s000.example.net:6379?weight=1, tcp://s001.example.net:6379?weight=2, tcp://s002.example.net:6379?weight=2 " Monday, June 24, 13

Slide 68

Slide 68 text

redis 127.0.0.1:6379> MONITOR OK "GET" "appsession:9jmmp11dvh3b4f1bp9trfuqlj3" "SETEX" "appsession:9jmmp11dvh3b4f1bp9trfuqlj3" "1440" "_sf2_attributes|a:1:{s:5:\"visit\";i:1371678033;} _sf2_flashes|a:0:{}_sf2_meta|a:3:{s:1:\"u\";i: 1371678033;s:1:\"c\";i:1371678023;s:1:\"l\";s:1:\"0\";}" session name session id (cookie) ttl (expire time) session data Monday, June 24, 13

Slide 69

Slide 69 text

Monolog logging Monday, June 24, 13

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

Monolog config snc_redis: clients: monolog: type: predis alias: monolog dsn: redis://localhost/1 logging: false monolog: client: monolog key: monolog monolog: handlers: main: type: service id: monolog.handler.redis level: debug Monday, June 24, 13

Slide 72

Slide 72 text

Referencias • http://blog.lusis.org/blog/2012/01/31/load- balancing-logstash-with-redis/ Monday, June 24, 13

Slide 73

Slide 73 text

SwiftMailer Spooling Monday, June 24, 13

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

Mailer spooling snc_redis: clients: emails: type: predis alias: emails dsn: redis://emails-spool-00.example.com logging: false swiftmailer: client: emails key: swiftmailer config.yml Monday, June 24, 13

Slide 76

Slide 76 text

Otros casos de uso en Symfony2 Monday, June 24, 13

Slide 77

Slide 77 text

Router dinámicos • Necesitamos convertir URLs amigables a rutas internas de Symfony2: Idioma Ruta interna Ruta “amigable” es /sport/123 /futbol en /sport/123 /football Monday, June 24, 13

Slide 78

Slide 78 text

Router dinámicos Idioma Ruta “amigable” _controller es /futbol Bundle:SportController:sportPageAction, array(‘sport’ => 123) es /madrid Bundle:CityController:cityPageAction array(‘city’ => 456) Monday, June 24, 13

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

Flujo Request /futbol Request /sport/123 RouterMapperListener Controller/Action Routing System Monday, June 24, 13

Slide 82

Slide 82 text

Monday, June 24, 13

Slide 83

Slide 83 text

Usando Redis hashes /futbol /sport/123 /baloncesto /sport/456 /tenis /sport/789 routes:es /sport/123 /futbol /sport/456 /baloncesto /sport/789 /tenis alias:es Monday, June 24, 13

Slide 84

Slide 84 text

Integración con el profiler y web debug toolbar Monday, June 24, 13

Slide 85

Slide 85 text

Integración en el profiler Monday, June 24, 13

Slide 86

Slide 86 text

OTROS CASOS DE USO GENERALES Monday, June 24, 13

Slide 87

Slide 87 text

Presencia de usuarios (who is online?) Monday, June 24, 13

Slide 88

Slide 88 text

¿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

Slide 89

Slide 89 text

Monday, June 24, 13

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

Usuarios online Amigos Amigos Online Monday, June 24, 13

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

Monday, June 24, 13

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

CONCLUSIONES Monday, June 24, 13

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

Monday, June 24, 13

Slide 103

Slide 103 text

Referencias http://redis.io/commands http://redis.io/documentation Monday, June 24, 13

Slide 104

Slide 104 text

Muchas Gracias https://joind.in/8844 @ronnylt https://github.com/ronnylt Monday, June 24, 13

Slide 105

Slide 105 text

We are hiring! Monday, June 24, 13