Asier Marqués @asiermarques<br />Socio fundador de Blackslot.com<br />Socio fundador de Artesanio.com<br />Organizador de ...
Rendimiento Web<br />
El rendimiento importa<br />Según kissmetrics.com:<br />El 73% de los usuarios de móvil ha encontrado algún sitio web dema...
Rendimiento no es escalabilidad<br />Pero es clave que la aplicación esté bien diseñada y sea escalable.<br />
Cómo diseñar bien<br />No te repitas. DRY<br />Divide. MVC<br />Calidad. TDD<br />Symfony2 te ayuda a conseguirlo.<br />
“la optimización prematura es la raíz de todo mal”<br />Donald Knuth<br />
Antes de optimizar<br />No resuelvas problemas que no tienes<br />No tomes decisiones tecnológicas en base a lo que hacen ...
Usa InnoDB!<br />
MyISAM<br />Bloqueos a nivel de tabla, no de fila<br />Los selects pesados bloquean la tabla frente a escritura<br />Las e...
InnoDB<br />Relacional<br />Bloqueos de fila, no de tabla<br />Soporta transacciones<br />Doctrine2 hace uso interno de tr...
Configuración<br />Innodb_buffer_pool_size<br />Innodb_log_file_size<br />Innodb_flush_log_at_trx_commit=2<br />innodb_flu...
Query Cache<br />http://dev.mysql.com/doc/refman/5.1/en/query-cache-status-and-maintenance.html<br />query_cache_type = 1 ...
El espacio importa<br />
Divide<br />Particionado<br />Vertical (por columna)<br />Horizontal (por fila)<br />http://mysql.isu.edu.tw/tech-resource...
Índices<br />Usa EXPLAIN para saber cuando utilizarlos<br />NOT NULL cuando sea posible<br />Una columna indexada que pued...
EXPLAIN <QUERY> <br />Valores en los que fijarte<br />type<br />ALL indica que debe recorrerse toda la tabla y debe evitar...
Índices y espacio: Tipos de datos<br />Usa INT en claves primarias como unsigneden lugar de BIGINT.<br />Usa TINYINT en lu...
Índices y espacio: Tipos de datos<br />Usa CHAR(num) en lugar de VARCHAR(num) para valores fijos<br />No uses palabras en ...
Conoce tus consultas<br />Es peor tener 83 consultas simples que una consulta “compleja”<br />No hagas operaciones matemát...
Conoce tus consultas<br />Count(*)<br />Usa mejor SQL_CALC_FOUND_ROWS<br />OrderBy<br />filesort en la mayoría de los caso...
Ejemplo típico en doctrine<br />Es típico hacer consultas innecesarias dentro de un bucle.<br />Tomemos por ejemplo la apl...
Vista de Ponentes<br />Se ejecuta el siguiente dql en el controlador<br />SELECT p <br />    FROM DesymfonyDesymfonyBundle...
Resultado<br />Cada iteración de Ponente hace una query de Ponencias<br />Tenemos 13 ponentes, se ejecutan 13 querys<br />...
La solución es sencilla<br />En el DQL<br />SELECT p, po FROM DesymfonyDesymfonyBundleEntityPonente p<br />INNER JOIN p.po...
Symfony y Doctrine<br />Usar arrays en las vistas en lugar de objetos<br />Usar la cache de doctrine<br />Cache de DQL<br ...
Búsqueda<br />InnoDB no soporta FULLTEXT<br />Sphinx, Lucene, Solr<br />Buenas opciones incluso para reducir complejidad e...
Analiza<br />Debesllevar un control de lo quesucede a diario en tuservidory comparar los valoresqueobtengascuandoestetenga...
Analiza mysqladminextended<br />Threads_running<br />Número de consultasque se estánejecutando. <br />Handler_read_rnd_nex...
Analiza<br />SHOW FULL PROCESS LISTG <br />Para saber cuánto tardan las consultas pesadas en ejecutarse y las que se encue...
NO SQL<br />No lo uses por moda, conoce la tecnología y los problemas que solventa<br />Conoce sus posibilidades de escala...
Cache<br />
Http Cache – RFC 2616<br />Symfony2 implementa la cache definida en el RFC<br />Sigue los conceptos Expiración-Validación<...
Cache-Control<br />Public<br />Todos los usuarios comparten la cache<br />Private<br />La cache es distinta para cada usua...
Expires y Cache-Control: max-age<br />expires (fecha GMT)<br />Frescura = expires – fecha actual<br />max-age (segundos)<b...
expires y max-age en Symfony2<br />       $response = $this<br />                                   ->render('DesymfonyBun...
expires y max-age en Symfony2<br />Primera carga de la página, 234ms<br />Carga desde la caché, 3ms<br />
Validación: LastModified y ETAG<br />LastModified<br />Fecha de última modificación del archivo, si el servidor retorna un...
LastModified y ETAG en Symfony2<br />//debemos crear los métodos en la entidad<br />$etag            = $entity->getCurrent...
Varnish<br />
Varnish<br />Proxy cache<br />Balanceador de carga<br />Lenguaje de configuración VCL<br />Soporte para ESI y HTTP Cache<b...
Varnish<br />Declaramos el servidor o servidores web frontales<br />Podemos especificar qué es lo que queremos cachear<br ...
Varnish<br />Podemos indicar un ttl para el objeto en la cache<br />También podemos evaluar si queremos cachear desde aquí...
Varnish: invalidar cache<br />Indicamos los Host que pueden lanzar peticiones de invalidación<br />Si el request es de tip...
Varnish: ESI<br />
Varnish: ESI<br />SI una url contiene tags ESI basta con indicar esi; para que Varnish lo procese<br />Podemos establecer ...
Varnish con Symfony2<br />En la plantilla<br />{% render'...:vista' with {}, {'standalone': true} %}<br />Devuelve..<br />...
Más cosas<br />
APC<br />ByteCode cache para PHP<br />APC.STAT = 0<br />En producción si no hay cambios frecuentes<br />
Assetic<br />Agrupa los estáticos indicados de tu sitio web en un único archivo.<br />Se puede escribir en disco, en un cd...
Assetic<br />$assets= new AssetCollection(<br />array(<br />new FileAsset(“web/Bundle/style.css”),<br />new GlobAsset(“web...
Assetic en Symfony2<br />{% javascripts'@Bundle/Resources/public/js/*' filter=‘nombre_js' %} <br />     <script src="{{ as...
Usa un CDN público<br />Ahorro de ancho de banda<br />El navegador probablemente tenga ya cacheadas las librerías comunes ...
Operaciones en tiempo real<br />El tiempo real no es necesario en la mayoría de los casos.<br />Envío de emails<br />Proce...
Usa Html5 y CSS3<br />
Async<br />
Cache Manifest<br /><htmllang="en" manifest=“ejemplo.manifest"><br />CACHE MANIFEST <br />CACHE<br />clock.js <br />clock....
Cache Manifest<br />El manifiesto se descarga en base a nuestras indicaciones en el HTTP Cache<br />..o forzándolo con Jav...
No uses imágenes, usa CSS3<br />Border-radius<br />Gradient<br />Text-shadow y Box-shadow<br />Canvas<br />Font-face<br />
¿Preguntas?<br />
Gracias<br />Asier Marqués<br />Blackslot<br />@asiermarques<br />linkedin.com/in/asier<br />asiermarques@blackslot.com<br...
Upcoming SlideShare
Loading in …5
×

Rendimiento en aplicaciones web con Symfony2

6,950 views

Published on

Ponencia sobre rendimiento web orientado a aplicaciones web Symfony2, para el evento #desymfony 2011

Rendimiento en aplicaciones web con Symfony2

  1. 1.
  2. 2.
  3. 3. Asier Marqués @asiermarques<br />Socio fundador de Blackslot.com<br />Socio fundador de Artesanio.com<br />Organizador de #webdevbilbao<br />Arquitecto web, ex-administrador de sistemas Microsoft, apasionado de la creación de servicios en internet y adicto al café.<br />Escribo en asiermarques.com<br />
  4. 4. Rendimiento Web<br />
  5. 5. El rendimiento importa<br />Según kissmetrics.com:<br />El 73% de los usuarios de móvil ha encontrado algún sitio web demasiado lento en cargar.<br />El 47% de los usuarios esperan un máximo de 2 segundos a que cargue una página web en la que van a comprar algo. <br />Si un sitio de comercio electrónico está ganando $ 100.000 por día, un retraso de 1 segundo en la página podría costar $ 2.5 millones de perdidas al año.http://blog.kissmetrics.com/loading-time<br />
  6. 6. Rendimiento no es escalabilidad<br />Pero es clave que la aplicación esté bien diseñada y sea escalable.<br />
  7. 7. Cómo diseñar bien<br />No te repitas. DRY<br />Divide. MVC<br />Calidad. TDD<br />Symfony2 te ayuda a conseguirlo.<br />
  8. 8. “la optimización prematura es la raíz de todo mal”<br />Donald Knuth<br />
  9. 9. Antes de optimizar<br />No resuelvas problemas que no tienes<br />No tomes decisiones tecnológicas en base a lo que hacen “los grandes”<br />Monitoriza, estudia la información y después optimiza<br />Evita operaciones síncronas que supongan demora<br />
  10. 10.
  11. 11. Usa InnoDB!<br />
  12. 12. MyISAM<br />Bloqueos a nivel de tabla, no de fila<br />Los selects pesados bloquean la tabla frente a escritura<br />Las escrituras bloquean la tabla<br />LOW_PRIORITY, HIGH_PRIORITY<br />http://dev.mysql.com/doc/refman/5.0/en/table-locking.html<br />No soporta transacciones<br />No es relacional<br />
  13. 13. InnoDB<br />Relacional<br />Bloqueos de fila, no de tabla<br />Soporta transacciones<br />Doctrine2 hace uso interno de transacciones<br />
  14. 14. Configuración<br />Innodb_buffer_pool_size<br />Innodb_log_file_size<br />Innodb_flush_log_at_trx_commit=2<br />innodb_flush_method=O_DIRECT<br />innodb_file_per_table<br />
  15. 15. Query Cache<br />http://dev.mysql.com/doc/refman/5.1/en/query-cache-status-and-maintenance.html<br />query_cache_type = 1 # 0 desactivada, 1 activada, 2 bajo demanda<br />query_cache_size = <tamaño de cache><br />SHOW STATUS LIKE 'Qcache%';<br />! -> Qcache_hits: Número de aciertos de la querycaché.<br />! -> Qcache_lowmem_prunes: El número de consultas eliminadas de la cache por falta de memoria disponible.<br />! -> Qcache_inserts: El número de consultas insertadas a la cache.<br />
  16. 16.
  17. 17. El espacio importa<br />
  18. 18. Divide<br />Particionado<br />Vertical (por columna)<br />Horizontal (por fila)<br />http://mysql.isu.edu.tw/tech-resources/articles/testing-partitions-large-db.html<br />Mantener los datos de texto opcionales en otra tabla<br />Configura InnoDBcon innodb_file_per_table<br />Cada archivo maneja<br />1) Los datos de la tabla<br />2) Los datos de los índices<br />3) Los MVCC (Multiversioning Concurrency Control) <br />4) La metainformación de la tabla<br />Optimize Table<br />reduce la información de cadaarchivo y reorganiza los índices<br />
  19. 19. Índices<br />Usa EXPLAIN para saber cuando utilizarlos<br />NOT NULL cuando sea posible<br />Una columna indexada que pueda ser NULL ocupa más espacio<br />Usa el tipo de datos correcto<br />
  20. 20. EXPLAIN <QUERY> <br />Valores en los que fijarte<br />type<br />ALL indica que debe recorrerse toda la tabla y debe evitarse<br />INDEX se debe recorrer todo el índice<br />key y key_len<br />Cuanto menor sea el valor de KeyLen mejor<br />Key te indica el Índice elegido para la consulta<br />EXTRA<br />Evitar Filesort y tablas temporales<br />Las tablas temporales aparecen al ordenar resultados o al hacer agrupaciones<br />
  21. 21. Índices y espacio: Tipos de datos<br />Usa INT en claves primarias como unsigneden lugar de BIGINT.<br />Usa TINYINT en lugar de INT(1).<br />TINYINT es 1 Byte, INT(1) son 4<br />Usa BIT para booleanos en lugar de INT(1) o TINYINT<br />Usa TIMESTAMP en lugar de DATETIME<br />TIMESTAMP son 4 Bytes, DATETIME son 8<br />
  22. 22. Índices y espacio: Tipos de datos<br />Usa CHAR(num) en lugar de VARCHAR(num) para valores fijos<br />No uses palabras en ENUM(),usa un solo carácter si es posible<br />Usa unsigned INT para guardar ips, no varchar<br />Usa utf8 cuando sea realmente necesario<br />Varchar(255) en utf8 son más de 700 bytes<br />
  23. 23. Conoce tus consultas<br />Es peor tener 83 consultas simples que una consulta “compleja”<br />No hagas operaciones matemáticas en tus consultas<br />Aligera en la medida de lo posible tus consultas<br />No hagas JOINs en campos que no tengan índice<br />Cuidado con los count y los order<br />
  24. 24. Conoce tus consultas<br />Count(*)<br />Usa mejor SQL_CALC_FOUND_ROWS<br />OrderBy<br />filesort en la mayoría de los casos<br />recupera los PK en una consulta simple antes de hacer una consulta compleja<br />
  25. 25. Ejemplo típico en doctrine<br />Es típico hacer consultas innecesarias dentro de un bucle.<br />Tomemos por ejemplo la aplicación de #desymfony<br />
  26. 26. Vista de Ponentes<br />Se ejecuta el siguiente dql en el controlador<br />SELECT p <br /> FROM DesymfonyDesymfonyBundleEntityPonente p<br />ORDER BY p.nombreASC<br />Y en la vista se hace<br />{% for ponente in ponentes %}<br /> …<br /> {% for ponencia in ponente.ponencias %}<br />
  27. 27. Resultado<br />Cada iteración de Ponente hace una query de Ponencias<br />Tenemos 13 ponentes, se ejecutan 13 querys<br />Con 200 ponentes, se ejecutarían 200 querys<br />
  28. 28. La solución es sencilla<br />En el DQL<br />SELECT p, po FROM DesymfonyDesymfonyBundleEntityPonente p<br />INNER JOIN p.ponenciaspo<br />ORDER BY p.nombre ASC<br />Solamente con añadir el INNER JOIN reducimos el número de querys a tan sólo una en lugar de 13 o (n).<br />
  29. 29. Symfony y Doctrine<br />Usar arrays en las vistas en lugar de objetos<br />Usar la cache de doctrine<br />Cache de DQL<br />$config->setMetadataCacheImpl(new DoctrineCommonCacheApcCache());<br />Cache de resultados<br />$config->setQueryCacheImpl(new DoctrineCommonCacheApcCache());<br />
  30. 30. Búsqueda<br />InnoDB no soporta FULLTEXT<br />Sphinx, Lucene, Solr<br />Buenas opciones incluso para reducir complejidad en consultas, <br />pe. Ordenar, filtros complejos..<br />
  31. 31. Analiza<br />Debesllevar un control de lo quesucede a diario en tuservidory comparar los valoresqueobtengascuandoestetengaproblemas con tusvaloresnormales.<br />Herramientas: Munin, Nagios, Cacti..<br />
  32. 32. Analiza mysqladminextended<br />Threads_running<br />Número de consultasque se estánejecutando. <br />Handler_read_rnd_next y Handler_read_first<br />Si esmuy superior a lo normal puedeque sea debido a un problema con los índices.<br />Handler_rollback<br />El número de rollbacks que se hanhecho.<br />Select_full_join<br />Joins sin índices, debeser cero.<br />Slow_queries<br />Si este valor creceindicaproblemas en el rendimiento de la aplicación.<br />
  33. 33. Analiza<br />SHOW FULL PROCESS LISTG <br />Para saber cuánto tardan las consultas pesadas en ejecutarse y las que se encuentran en estado “Locked”<br />SHOW TABLE STATUS FROM <base datos> LIKE ‘<nombre>’G<br />Comprobar el espacio de la tabla (el Max_data_lengthnuncapuedeserconsumidoporcompleto)<br />EXPLAIN [EXTENDED] <query><br />SHOW INNODB STATUSG<br />Estadísticas de acceso a disco, transacciones, consultas con problemas de integridad..<br />SHOW PROFILE <br />Comprobar los recursos de hardware consumidos por una query<br />http://dev.mysql.com/doc/refman/5.0/en/show-profiles.html<br />
  34. 34. NO SQL<br />No lo uses por moda, conoce la tecnología y los problemas que solventa<br />Conoce sus posibilidades de escalado<br />Conoce su rendimiento<br />Conoce sus posibilidades de backup/restore!<br />Funciona bien como frontend de una base relacional si su motor es rápido<br />Buena alternativa a desnormalizar<br />
  35. 35. Cache<br />
  36. 36. Http Cache – RFC 2616<br />Symfony2 implementa la cache definida en el RFC<br />Sigue los conceptos Expiración-Validación<br />Symfony2 tiene su propio proxy cache pero permite ser integrado con sistemas proxy cache como Varnish.<br />
  37. 37. Cache-Control<br />Public<br />Todos los usuarios comparten la cache<br />Private<br />La cache es distinta para cada usuario<br />No-Cache<br />No se cachea en ningún caso<br />En Symfony2 por defecto es no-cache o private<br />
  38. 38. Expires y Cache-Control: max-age<br />expires (fecha GMT)<br />Frescura = expires – fecha actual<br />max-age (segundos)<br />s-maxage se usa por servidores proxy, es público por lo que es común a todos los usuarios<br />max-age tiene prioridad sobre Expires <br />..y s-maxage sobre el max-age si es pública<br />
  39. 39. expires y max-age en Symfony2<br /> $response = $this<br /> ->render('DesymfonyBundle:Ponente:index.html.twig', <br />array('ponentes' => $ponentes));        $response->setPublic();       $response->setMaxAge(10); <- Private<br />$response->setSharedMaxAge(10); <- Public<br />  $date =newDateTime(); $date->modify('+60 seconds');<br />$response->setExpires($date); <- Private(si no hay un setPublic())<br />       return$response;    <br />
  40. 40. expires y max-age en Symfony2<br />Primera carga de la página, 234ms<br />Carga desde la caché, 3ms<br />
  41. 41. Validación: LastModified y ETAG<br />LastModified<br />Fecha de última modificación del archivo, si el servidor retorna una fecha menor a la de la caché del cliente, descargará el archivo.<br />Etag<br />El navegador cachea el archivo identificándolo con el valor del Etag.<br />El navegador comprobará si debe servir el archivo comparando el Etag del servidor con el valor que él tiene almacenado en su cache, para ello usará el campo If-None-Match<br />
  42. 42. LastModified y ETAG en Symfony2<br />//debemos crear los métodos en la entidad<br />$etag = $entity->getCurrentETag();<br />$mod_date = $entity->getLastModification();<br />$response->setETag($etag);$response->setLastModified($mod_date);<br />if($response->isNotModified($this->get('request')))<br />{ <br /> …<br />
  43. 43. Varnish<br />
  44. 44. Varnish<br />Proxy cache<br />Balanceador de carga<br />Lenguaje de configuración VCL<br />Soporte para ESI y HTTP Cache<br />
  45. 45. Varnish<br />Declaramos el servidor o servidores web frontales<br />Podemos especificar qué es lo que queremos cachear<br />Retornando pass evitamos que se cachee y con lookup forzamos a que se busque en el cache<br />
  46. 46. Varnish<br />Podemos indicar un ttl para el objeto en la cache<br />También podemos evaluar si queremos cachear desde aquí<br />
  47. 47. Varnish: invalidar cache<br />Indicamos los Host que pueden lanzar peticiones de invalidación<br />Si el request es de tipo PURGE se elimina la url del host especificado <br />curl -X PURGE http://dominio.com/url-a-invalidar<br />
  48. 48. Varnish: ESI<br />
  49. 49. Varnish: ESI<br />SI una url contiene tags ESI basta con indicar esi; para que Varnish lo procese<br />Podemos establecer que si la ruta es /esi/* le ponga otro ttl o retorne pass, evitando que se cachee<br />
  50. 50. Varnish con Symfony2<br />En la plantilla<br />{% render'...:vista' with {}, {'standalone': true} %}<br />Devuelve..<br /><esi:includesrc=“http://dominio/url_al_contenido”/><br />Symfony2 añade automáticamente sólo cuando use ESI un headerSurrogate-Control= "abc=ESI/1.0“.<br />En Varnish le debemos indicar que es capáz de procesar ESI con set req.http.Surrogate-Capability = "abc=ESI/1.0“.<br />http://symfony.com/doc/2.0/cookbook/cache/varnish.html<br />
  51. 51. Más cosas<br />
  52. 52. APC<br />ByteCode cache para PHP<br />APC.STAT = 0<br />En producción si no hay cambios frecuentes<br />
  53. 53. Assetic<br />Agrupa los estáticos indicados de tu sitio web en un único archivo.<br />Se puede escribir en disco, en un cdn (pe.Amazon s3) o manejar como string. <br />Comprime y optimiza los css, js e imágenes.<br />Reduce las peticiones HTTP<br />Se integra con HTTP Cache de Symfony2 y con la sintaxis de Twig<br />
  54. 54. Assetic<br />$assets= new AssetCollection(<br />array(<br />new FileAsset(“web/Bundle/style.css”),<br />new GlobAsset(“web/*.css”),<br />) <br /> );<br />$assets->load();<br />$writer = new AssetWriter(“s3://bucket”);<br />$writer->writeManagerAssets($assets);<br />
  55. 55. Assetic en Symfony2<br />{% javascripts'@Bundle/Resources/public/js/*' filter=‘nombre_js' %} <br /> <script src="{{ asset_url }}"></script> <br />{% endjavascripts%}<br />$ phpapp/consoleassetic:dump s3://bucket<br />
  56. 56. Usa un CDN público<br />Ahorro de ancho de banda<br />El navegador probablemente tenga ya cacheadas las librerías comunes (pe. jquery)<br />
  57. 57. Operaciones en tiempo real<br />El tiempo real no es necesario en la mayoría de los casos.<br />Envío de emails<br />Procesamiento de imágenes<br />Invalidaciones de cache<br />Herramientas: RabbitMQ, Gearman.<br />Otras soluciones:<br />Queue de Switfmailer<br />Implementar nuestra solución en memcached, redis..<br />
  58. 58. Usa Html5 y CSS3<br />
  59. 59. Async<br />
  60. 60. Cache Manifest<br /><htmllang="en" manifest=“ejemplo.manifest"><br />CACHE MANIFEST <br />CACHE<br />clock.js <br />clock.css<br />NETWORK<br />no_se_cachea.php<br />
  61. 61. Cache Manifest<br />El manifiesto se descarga en base a nuestras indicaciones en el HTTP Cache<br />..o forzándolo con JavaScript mediante el método window.applicationCache<br />La caché se invalida mediante una comparación byte-for-byte entre el archivomásreciente y el anterior.<br />
  62. 62. No uses imágenes, usa CSS3<br />Border-radius<br />Gradient<br />Text-shadow y Box-shadow<br />Canvas<br />Font-face<br />
  63. 63. ¿Preguntas?<br />
  64. 64. Gracias<br />Asier Marqués<br />Blackslot<br />@asiermarques<br />linkedin.com/in/asier<br />asiermarques@blackslot.com<br />Imágenes<br />http://www.flickr.com/photos/caharley72/11332057<br />http://www.flickr.com/photos/poetatum/3457696479<br />http://www.flickr.com/photos/32299138@N08/5772093221<br />http://www.flickr.com/photos/aereimilitariorg/3956024476<br />http://www.flickr.com/photos/zombieite/5181067906<br />http://www.flickr.com/photos/bcash67/4496036286<br />

×