taller redis
TRANSCRIPT
![Page 1: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/1.jpg)
Introducción y casos de uso
@betabeers – Barcelona17 de enero 2012
![Page 2: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/2.jpg)
¿Quién soy?
SELECT value FROM odds INNER JOIN outcomes ON ... INNER JOIN bets ON ... INNER JOIN markets ON ... ...WHERE ... ORDER BY ...
Ronny López @ronnyltDesarrollador @pricebets
Descubrí Redis cuando una consulta como esta hacia que una página demorara +15 segundos en cargar:
![Page 3: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/3.jpg)
Antes de continuar...¿Qué hay de malo en los sistemas tradicionales de
BD relacionales?
Nada...
![Page 4: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/4.jpg)
Pero...
• Hay tareas que no necesitan tanta complejidad
• El modelo relacional actual es difícil de escalar horizontalmente
• No se pueden modelar problemas comunes lo suficientemente bien
![Page 5: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/5.jpg)
¿Qué es Redis?
• Remote dictionary server
• Motor de almacenamiento clave-valor avanzado, rápido y persistente
• Servidor de estructuras de datos
• NoSQL (Not-only SQL)
• Open source
![Page 6: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/6.jpg)
¿Quién está detrás de Redis?
• Creado por Salvatore Sanfilippo @antirez
• Release inicial en marzo del 2009
• Actualmente el desarrollo es patrocinado por VMWare
• Comunidad activa y amigable
![Page 7: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/7.jpg)
¿Quién usa Redis?
• Usado hoy en el mundo real por grandes y pequeñas empresas
• Resolviendo grandes y pequeños problemas
![Page 8: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/8.jpg)
¿Quién usa Redis?
Y muchos más...
http://redis.io/topics/whos-using-redis
![Page 9: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/9.jpg)
¿Para qué lo usan? • github.com: motor de almacenamiento clave/valor, cola de trabajos
(resque)
• guardian.co.uk: servidor de estructuras de datos persistente
• disqus.com: sistema estádistico desarrollado sobre Redis
• stackoverflow.com: capa de cache para toda su red
• flickr.com: lista de trabajos
• pricebets.es: almacenamiento primario (conjuntos, listas, hashes, etc...), routing, cache, sessions
![Page 10: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/10.jpg)
Características principales
• Escrito en C
• Muy pocas dependencias
• Super rápido
• Single-threaded
• Clientes disponibles para varios lenguajes
![Page 11: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/11.jpg)
Características principales
• Alto rendimiento en escritura y lectura
• Soporte de operaciones atómicas
• Soporte de transacciones
![Page 12: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/12.jpg)
CaracterísticasSimplicidad
• Fácil instalación
• Curva de aprendizaje relativamente baja
• Lenguage de comandos fácil de utilizar y aprender
![Page 13: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/13.jpg)
CaracterísticasPrevisibilidad
• La memoria es rápida, y permite a Redis tener un redimiento muy fiable
• Operaciones sobre datasets de 10 mil claves tendrán el mismo rendimiento que sobre datasets de 50 millones de claves
• La complejidad algorítmica de todos los comandos está documentada
![Page 14: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/14.jpg)
CaracterísticasConfiabilidad
• Todos los datos residen en memoria, y son persistidos a disco eventualmente (snapshots, append-only log)
• Replication system (master-slave) , pueden configurarse múltiples esclavos. Un esclavo puede ser el master de otro esclavo
![Page 15: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/15.jpg)
CaracterísticasPersistencia
• RDB, Point-in-time snapshots en intervalos configurados
• AOF (append-only file) cada operación de escritura
• Es posible combinar RDB y AOF en la misma instancia
• Se puede deshabilitar la persistencia del todo
![Page 16: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/16.jpg)
CaracterísticasVersatilidad
• Adaptable a varios tipos de aplicaciones
• Modelado de los datos a partir de estructuras de datos como listas, conjuntos, conjuntos ordenados y hashes
![Page 17: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/17.jpg)
Tipos de datos
• Redis NO es simplemente un motor de almacenamiento clave-valor
• Es un servidor de estructuras de datos que incluye varios tipos de datos predefinidos (strings, lists, sets, sorted sets, hashes)
![Page 18: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/18.jpg)
Claves
• Puede usarse cualquier cadena de caracteres como clave
• Es recomendable tener organizadas las claves en namespaces (keyspaces) de la forma:
object-type:id:field
Ejemplo:user:123:passworduser:456:friends
![Page 19: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/19.jpg)
Claves
• NO es recomendable que las claves sean demasiado grandes (memoria, localización en el keyspace)
• Tampoco es recomendable que las claves sean demasiado cortas por cuestiones de legibilidad
Ejemplo: u:123:pw
![Page 20: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/20.jpg)
Tipos de datosStrings
• Es el tipo de dato más simple
• Si solamente usa este tipo de dato es similar a usar un servidor memcached pero con persistencia
![Page 21: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/21.jpg)
Operaciones conStrings
$ redis-cli SET mykey “binary-safe string”
OK
$redis-cli GET mykey
binary-safe string
![Page 22: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/22.jpg)
OperacionesStrings
• Comandos SET/GET para escribir/leer
• Los valores pueden ser cualquier string no mayor que 1 GB
• Si necesita almacenar algo más grande, Redis is NOT for you ;)
![Page 23: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/23.jpg)
Operaciones atómicasStrings
Atomic increment
$ redis-cli SET counter 100 OK $ redis-cli INCR counter (integer) 101 $ redis-cli INCR counter (integer) 102 $ redis-cli INCRBY counter 10 (integer) 112
![Page 24: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/24.jpg)
¿Por qué atómico?
Aún cuando hayan varios clientes ejecuntando operaciones sobre la misma clave, no se va a incurrir en una condición de carrera (race condition)
![Page 25: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/25.jpg)
Otras operaciones Strings
• APPEND, GETRANGE, STRLEN, ...
• MSET, MGET, ...
• SETNX, MSETNX, GETSET, ...
http://redis.io/commands#string
![Page 26: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/26.jpg)
Tipos de datosLists
• Son simplemente una secuencia de elementos (strings) ordenados
• Son implementadas como listas enlazadas
![Page 27: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/27.jpg)
Tipos de datosLists
• Listas doblemente enlazadas garantizan la inserción en cualquier extremo en un tiempo constante O(1)
• Con estas caterísticas también puede modelarse una “cola”
![Page 28: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/28.jpg)
OperacionesLists
• LPUSH agrega un elemento en el extremo izquierdo de la lista
• RPUSH en el extremo derecho
• LRANGE extrae elementos de la lista en cierto rango
![Page 29: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/29.jpg)
OperacionesLists
• LPUSH, RPUSH, LPOP, RPOP, ...
• LINDEX, LINSERT, LLEN, ...
• LTRIM, LREM, ...
http://redis.io/commands#list
![Page 30: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/30.jpg)
Variantes “blocking”
• BLPOP, BRPOP, BRPOPLPUSH
• Bloquean la conexión (del cliente) cuando no hay elementos en la lista
• Ideal para implementar colas de trabajos/mensajes
![Page 31: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/31.jpg)
OperacionesLists
$ redis-cli RPUSH messages "Hola, ¿qué tal?"
OK$ redis-cli RPUSH messages "Bien, gracias"OK$ redis-cli RPUSH messages "Muy grande Redis"OK$ redis-cli LRANGE messages 0 21. Hola, ¿qué tal?2. Bien, gracias3. Muy grande Redis
![Page 32: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/32.jpg)
Usos de las listas
• Utilice listas cada vez que requiera acceder a los datos en el mismo orden en que se agregan
• Ideal para casos en los que se requiere un ORDER BY de SQL
• Escalable a millones de elementos manteniendo el mismo rendimiento
![Page 33: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/33.jpg)
Ejemplos de uso de las listas
• Mantener un listado cronológico de los comentarios publicados en un blog
• Es posible paginar usando LRANGE de forma trivial
$ redis-cli RPOP post:123:comments 456
$ redis-cli RPOP post:123:comments 789
$ redis-cli LRANGE post:123:comments 50 60
![Page 34: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/34.jpg)
Tipos de datosSets
• Colección NO–ordenada de datos (conjuntos)
• Permite operaciones típicas sobre conjuntos como unión, intersección, diferencia, etc...
• Estas operaciones son difíciles de implementar en un modelo relacional
![Page 35: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/35.jpg)
EjemplosSets
$ redis-cli SADD user:123:friends 456(integer) 1$ redis-cli SADD user:123:friends 789(integer) 1$ redis-cli SADD user:123:friends 123(integer) 1$ redis-cli SMEMBERS user:123:friends1. 1232. 4563. 789
$ redis-cli SISMEMBER user:123:friends 789(integer) 1
![Page 36: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/36.jpg)
EjemplosSets
Etiquetado de noticias
$ redis-cli SADD news:123:tags 1(integer) 1$ redis-cli SADD tags:1:objects 123(integer) 1
Obtener todos los tags de una noticia es trivial
$ redis-cli SMEMBERS news:123:tags
![Page 37: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/37.jpg)
OperacionesSets
• SADD, SREM, SCARD, SMEMBERS, SISMEMBER, ...
• SUNION, SDIFF, SINTER, ...
• SPOP, SRAND, .... O(1)
http://redis.io/commands#set
![Page 38: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/38.jpg)
Tipos de datosSorted Sets
• Un conjunto ordenado es un conjunto donde a cada elemento se le asocia una puntuación (score)
• Este valor (score) determina el orden de los elementos dentro del conjunto
![Page 39: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/39.jpg)
Tipos de datosSorted Sets
• ZADD es usado para agregar nuevos elementos o para actualizar el score de uno ya existente
$ redis-cli ZADD top-sites: 4 “pricebets”
(integer) 1 $ redis-cli ZADD top-sites: 6 “betabeers” (integer) 1
$ redis-cli ZRANGE top-sites 0 -1 1. betabeers 2. pricebets
![Page 40: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/40.jpg)
OperacionesSorted Sets
• ZADD, ZREM, ZCARD, ZCOUNT, ZINCR, ...
• ZSCORE, ZINCRBY, ZRANK, ...
• ZCOUNT, ....
http://redis.io/commands#sorted_set
![Page 41: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/41.jpg)
Tipos de datosHashes
• Permiten que objetos compuestos se almacenen en una clave específica
• Tipo de dato ideal para almacenar objetos complejos en Redis, usando los atributos del objeto como claves del hash
![Page 42: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/42.jpg)
OperacionesHashes
$ redis-cli HSET event:1 name betabeers
$ redis-cli HSET event:1 date 2012-01-17
$ redis-cli HSET event:1 where Barcelona
$ redis-cli HGETALL event:11) name2) betabeers3) date4) 2012-01-175) where6) Barcelona
![Page 43: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/43.jpg)
OperacionesHashes
• HSET, HGET, HDEL, HEXIST, ...
• HKEYS, HGETALL, HVALS, ...
• HMGET, HMSET, ...
http://redis.io/commands#hash
![Page 44: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/44.jpg)
Casos de uso
Contadores
![Page 45: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/45.jpg)
Contadores
• Gran cantidad de escrituras/lecturas
• Datos valiosos, pero NO críticos
• Insertar una fila en MySQL o actualizar un contador en cada petición será sin dudas difícil de escalar
![Page 46: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/46.jpg)
Contadores
$ redis-cli INCR active:sports:spain $ redis-cli INCR active:sports:italy $ redis-cli GET active:sports:spain
$ redis-cli GETSET active:sports:spain 0
![Page 47: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/47.jpg)
Casos de uso
Presencia¿Quién está online?
¿Cuáles de mis amigos están online?
![Page 48: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/48.jpg)
Presencia
• Mantener en un conjunto los usuarios que se han detectado online en cada minuto
$ redis-cli SADD online:15:01 123$ redis-cli SADD online:15:01 456
$ redis-cli SADD online:15:02 123
![Page 49: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/49.jpg)
Presencia
• El conjunto de usuarios online se puede obtener de la UNION de los conjuntos relativos a los últimos ¿5? minutos.
A las 15:05$ redis-cli SUNION online:15:01 online:15:02 online:15:03 online:15:04 online:15:05
![Page 50: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/50.jpg)
Presencia• El conjunto de “amigos” online es otra
operación sobre conjuntos
$ redis-cli SUNION online:15:01 online:15:02 online:15:03 online:15:04 online:15:05 user:123:friends
• Puede almacenarse en otra clave:
$ redis-cli SUNIONSTORE friends:online:123 online:15:01 online:15:02 online:15:03 online:15:04 online:15:05 user:123:friends
![Page 51: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/51.jpg)
Casos de uso
Tabla de posiciones
![Page 52: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/52.jpg)
Tabla de posiciones
• Se necesita listar elementos ordenados por una puntuación
• Las puntuaciones se actualizan en tiempo real
• Consultas lentas por naturaleza
SELECT * FROM ... WHERE ... ORDER BY ... LIMIT 10
![Page 53: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/53.jpg)
Tabla de posiciones
• Mantener un conjunto ordenado (sorted set) con los elementos y su score
$ redis-cli ZADD popular:sports 10 football
$ redis-cli ZADD popular:sports 6 basketball
$ redis-cli ZADD popular:sports 12 football
$ redis-cli ZREVRANGE popular:sports 0 9
![Page 54: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/54.jpg)
Casos de uso
URL Routing
![Page 55: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/55.jpg)
URL Routing
• Convertir alias de URL a paths internos de la aplicación
Ejemplo:
http://www.apuestas.com/futbol => /sport/1
http://www.apuestas.com/futbol/espana/copa-del-rey => /competition/3120
![Page 56: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/56.jpg)
URL Routing
• Convertir paths internos a friendly URLs
Ejemplo: /sport/1=> http://www.apuestas.com/futbol
/competition/3120http://www.apuestas.com/futbol/espana/copa-del-rey
![Page 57: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/57.jpg)
URL Routing
• Mantener un hash con el routing y otro con los alias
$ redis-cli HSET routing /futbol /sport/1
$ redis-cli HSET alias /sport/1 /futbol
• Tiempo constante O(1) para cualquier número de rutas/alias
![Page 58: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/58.jpg)
Otras características...
• Pipelining
• Transactions
• Pub/Sub
![Page 59: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/59.jpg)
¿Qué se espera para próximas vesiones?
• Scripting (con LUA) (Redis 2.6)
• High resolution expires (Redis 2.6)
• Mejoras en el rendimiento de la escritura/lectura de grandes objetos (Redis 2.6)
• Redis Cluster (Redis 3.0)
![Page 60: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/60.jpg)
Algunos consejos
• Instancias de Redis en 64 bits consumen más RAM
• No usar el comando KEYS en producción
• Comando MONITOR para observar y monitorear
![Page 61: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/61.jpg)
Benchmarksredis-benchmark -q -n 100000
PING (inline): 108459.87 requests per secondPING: 89766.61 requests per secondMSET (10 keys): 48053.82 requests per secondSET: 80064.05 requests per secondGET: 87108.02 requests per secondINCR: 82644.62 requests per secondLPUSH: 82169.27 requests per secondLPOP: 78802.20 requests per secondSADD: 71890.73 requests per secondSPOP: 93283.58 requests per secondLPUSH (again, in order to bench LRANGE): 86880.97 requests per secondLRANGE (first 100 elements): 64061.50 requests per secondLRANGE (first 300 elements): 40064.10 requests per secondLRANGE (first 450 elements): 30478.51 requests per secondLRANGE (first 600 elements): 25873.22 requests per second
Processor 2.2 GHz Intel Core i7Memory 8 GB 1333 MHz DDR3
![Page 62: Taller Redis](https://reader031.vdocuments.net/reader031/viewer/2022012400/5562eb4bd8b42ab47d8b4fd7/html5/thumbnails/62.jpg)
Muchas gracias...
$ redis-cli HGETALL pricebets
1. Buscamos2. Programadores3. PHP4. [email protected]