symfony 1, mi viejo amigo

64

Upload: jose-antonio-pio

Post on 15-Jan-2015

4.376 views

Category:

Technology


0 download

DESCRIPTION

Algunas ideas y experiencias tratando de optimizar Symfony 1.4 Doctrine 1.2 para una web de alto rendimiento.

TRANSCRIPT

Page 1: Symfony 1, mi viejo amigo
Page 2: Symfony 1, mi viejo amigo
Page 3: Symfony 1, mi viejo amigo

Mi viejo amigo

Page 4: Symfony 1, mi viejo amigo

Un poco sobre mi

Estoy trabajando con webs desde 2001Me encanta investigar y meterme en problemas

era solo un poco!

Page 5: Symfony 1, mi viejo amigo

青玉男ao tama o

Page 6: Symfony 1, mi viejo amigo

ao

Page 7: Symfony 1, mi viejo amigo

Y por qué hablar de 1.4?

Page 8: Symfony 1, mi viejo amigo

PreguntasLas hago yo!

Page 9: Symfony 1, mi viejo amigo

Que podemos optimizar?

Experiencia de Usuario?

Carga de proceso?

Tiempo de descarga?

Uso de memoria?

Page 10: Symfony 1, mi viejo amigo

Carga de procesoTiempo que pasa el servidor procesando la web

Page 11: Symfony 1, mi viejo amigo

Proveedor de Internet

Desarrolladores Backend

Desarrolladores Frontend

AAA AAA
Page 12: Symfony 1, mi viejo amigo

Tiempo de Espera

Cuanto tiempo tarda tu servidor en procesar tu página.

< 100ms está bien? Que opinan por experiencia?

500ms está bien para el 90% de las webs del mundo

Page 13: Symfony 1, mi viejo amigo

Mi experiencia20 Seg

• Punto de partida

2 Seg

• Un par de semanas + 1 nivel de cache

800ms

• Un mes + 3 niveles de cache

62ms

• Un par de meses + 2 tipos de cache

Page 14: Symfony 1, mi viejo amigo

La optimización dueleVale la pena el

esfuerzo?

Page 15: Symfony 1, mi viejo amigo

Que podemos mejorar

Symfony Internals

Cache

Doctrine

Page 16: Symfony 1, mi viejo amigo

Hechos

Tiempo de renderizacion

Cantidad de consultas y sus uso

Los helpers y librerías

Logs agregan algo mas de tiempo

Page 17: Symfony 1, mi viejo amigo

+ Hechos

Page 18: Symfony 1, mi viejo amigo

Herramientas

Debug Tool Bar

sfTimer & sfTimerManager

Page 19: Symfony 1, mi viejo amigo

Debug Tool Bar

Todos la conocemos ya!

Page 20: Symfony 1, mi viejo amigo

sfTimerManager

$timer = sfTimerManager::getTimer("test");...var_export ( $timer->addTime() );

Page 21: Symfony 1, mi viejo amigo

sfTimerManager

Page 22: Symfony 1, mi viejo amigo

sfTimer

$timer = new sfTimer ("test");$timer->startTimer();...var_export ( $timer->addTime() );

Page 23: Symfony 1, mi viejo amigo

DoctrineQue podemos optimizar

Page 24: Symfony 1, mi viejo amigo

Optimizando DoctrineCantidad de consultas

Tiempos de consulta

Tiempos de hidratación

Tiempo de conversion de consultas

Conteo de registros

Selects y joins e indices

Page 25: Symfony 1, mi viejo amigo

Cantidad de ConsultasOptimizando Doctrine

Page 26: Symfony 1, mi viejo amigo

Tiempo de ConsultaOptimizando Doctrine

0.02s

0.02s

0.02s

0.02s

0.02s

Tiempo que nos cuesta buscar información en la DB

Page 27: Symfony 1, mi viejo amigo

Tiempo de HidrataciónOptimizando Doctrine

Que es?

http://www.doctrine-project.org/documentation/manual/1_2/en/data-hydrators

const HYDRATE_RECORD = 2;const HYDRATE_ARRAY = 3;const HYDRATE_NONE = 4;const HYDRATE_SCALAR = 5;const HYDRATE_SINGLE_SCALAR = 6;const HYDRATE_ON_DEMAND = 7;const HYDRATE_ARRAY_HIERARCHY = 8;const HYDRATE_RECORD_HIERARCHY = 9;

Page 28: Symfony 1, mi viejo amigo

Tiempo de HidrataciónOptimizando Doctrine

Qué buscamos?

Registros

Campos específicos sobre registros

Referencias cruzadas entre tablas

Conteo de registros

Conteo de registros cruzados por campos

Etc.

Qué es lo mínimo que estamos buscando?

Page 29: Symfony 1, mi viejo amigo

Tiempo de HidrataciónOptimizando Doctrine

$clist = Doctrine::getTable("Country")->createQuery()

->select({fields})->execute({strategy});

{fields}/{strategy}

Object Array None PDO

* 0.17 0.09 0.008 0.003

id,name 0.15 0.067 0.0066 0.002

Page 30: Symfony 1, mi viejo amigo

Tiempo de HidrataciónOptimizando Doctrine

$conn = Doctrine_Manager::connection();$data = $conn->execute("select * from countries")->fetchAll();

Page 31: Symfony 1, mi viejo amigo

Tiempo de HidrataciónOptimizando Doctrine

$conn = Doctrine_Manager::connection();

Para que quiero el ORM?

Para manejar las conexiones!

Page 32: Symfony 1, mi viejo amigo

Doctrine?Una interesante paradoja!

Page 33: Symfony 1, mi viejo amigo

Tiempo de HidrataciónOptimizando Doctrine

Name Surname Age

Pedro Perez 60

Raúl Perez 54

Daniel Rodriguez 76

Ejemplo!

Page 34: Symfony 1, mi viejo amigo

Tiempo de HidrataciónOptimizando Doctrine

UserTable::getInstance() ->createQuery() ->select ("id, name, surname, age") ->where("is_deleted = ?", false) ->andWhere("is_closed = ? ", false)

->andWhere(”age > ? ", 50 ) ->execute( );

UserTable::getInstance() ->createQuery() ->select ("id, name, surname, age") ->where("is_deleted = ?", false) ->andWhere("is_closed = ? ", false)

->andWhere(”age > ? ", 50 ) ->execute( array(),

Doctrine_Core::HYDRATE_ARRAY);

Estrategia

Page 35: Symfony 1, mi viejo amigo

Tiempo de HidrataciónOptimizando Doctrine

<td> <?php echo $user->get("name") ?></td><td> <?php echo $user->name ?></td>

<?php $user = $user->getRawValue() ?><td> <?php echo $user["name"] ?></td>

Compatibilidad entre estrategias en la vista

Page 36: Symfony 1, mi viejo amigo

T. Conversión de Consulta

Optimizando Doctrine

$q = Doctrine::getTable("Country")->createQuery();$q->getSqlQuery().

$q = Doctrine::getTable("Gallery")->createQuery("g")->leftJoin("g.images");$q->getSqlQuery();

Gallery 1..n GalleryImages n..1 Image

0.006 seg

0.1 seg

Page 37: Symfony 1, mi viejo amigo

Conteo de RegistrosOptimizando Doctrine

$clist = Doctrine::getTable("Country”)

->findAll()->count();

->createQuery()->count();

->createQuery()->select("count(id)")->execute( array (), Doctrine::HYDRATE_NONE );

$conn->execute("select count(id) as count from countries")->fetchAll();

0.2 seg

0.0056 seg 0.01

seg

0.00087 seg

Page 38: Symfony 1, mi viejo amigo

PDOhttp://es2.php.net/manual/en/book.pdo.php

Page 39: Symfony 1, mi viejo amigo

Select, Joins e IndicesOptimizando Doctrine

->limit(100)->execute();

->limit(1000)->execute();

->limit(100)->execute( array(), Doctrine::HYDRATE_ARRAY );

->limit(1000)->execute( array(), Doctrine::HYDRATE_ARRAY );

$articles = Doctrine::getTable("Article")->createQuery("a")->leftJoin("a.pages p")

2.93 seg

30.80 seg

0.20 seg

1.37 seg

Page 40: Symfony 1, mi viejo amigo

Select, Joins e IndicesOptimizando Doctrine

SELECT c.id, c.name , c.resume, c.disclaimer, c.product , c.description, \UNIX_TIMESTAMP(c.valid_start_at) as valid_start, UNIX_TIMESTAMP(c.valid_end_at) as valid_end, UNIX_TIMESTAMP(c.promotion_start_at) as promotion_start, UNIX_TIMESTAMP(c.promotion_end_at) as promotion_end, UNIX_TIMESTAMP(c.publish_at) as publish, \c.tracking_click, c.tracking_save_count, c.visibility_status_id, c.print_coupon, c.mobile_coupon, c.online_coupon, c.company_id, cp.comercial_name as company_name, c.redemption_price as redemption_price, \CONCAT_WS(',', GROUP_CONCAT(DISTINCT ca.attributes_id SEPARATOR ",") ) as attributes, \CONCAT_WS(',', GROUP_CONCAT(DISTINCT ca.root_id SEPARATOR ",") ) as roots, \CONCAT_WS(',', GROUP_CONCAT(DISTINCT ca.level SEPARATOR ",") ) as levels, \CONCAT_WS(',', GROUP_CONCAT(DISTINCT cs.store_id SEPARATOR ",") ) as stores, \CONCAT_WS(',', GROUP_CONCAT(DISTINCT uc.user_id SEPARATOR ",") ) as users, \CONCAT_WS(',', GROUP_CONCAT(DISTINCT cs.state_id SEPARATOR ","), IF(online_coupon = 1, 0, null) ) as states, \ CONCAT_WS(',', IF( print_coupon = 1, 1,null), IF( mobile_coupon = 1, 2,null), IF( online_coupon = 1, 3,null) ) as types, \CONCAT_WS(',', GROUP_CONCAT(DISTINCT cs.postal_code SEPARATOR ",") ) as postal_codes_ids, \CONCAT_WS(' ', GROUP_CONCAT(DISTINCT cs.city_name SEPARATOR " ") ) as cities, \CONCAT_WS(' ', GROUP_CONCAT(DISTINCT s.road_name SEPARATOR " ") ) as roads, \CONCAT_WS(' ', GROUP_CONCAT(DISTINCT s.comercial_name SEPARATOR " ") ) as names, \CONCAT_WS(' ', GROUP_CONCAT(DISTINCT s.postal_code SEPARATOR " ") ) as postal_codes, \CONCAT_WS(' ', GROUP_CONCAT(DISTINCT sa.name SEPARATOR " ") ) as state_names \FROM ( ( ( (coupons c LEFT JOIN coupon_attributes ca ON c.id = ca.coupons_id ) LEFT JOIN coupon_stores cs ON c.id = cs.coupon_id) LEFT JOIN sf_guard_user_coupons uc ON c.id = uc.coupon_id ) LEFT JOIN stores s ON cs.store_id = s.id ) LEFT JOIN states sa ON cs.state_id = sa.id LEFT JOIN companies cp ON cp.id=c.company_id \WHERE ( c.administrative_status_id = 1 AND c.publish_at < NOW() AND DATE(c.valid_end_at) + INTERVAL 1439 HOUR_MINUTE > NOW() ) \GROUP BY c.id

Page 41: Symfony 1, mi viejo amigo

Select, Joins e IndicesOptimizando Doctrine

MySQL

Doctrine

Sphinx

MySQL

Doctrine

Page 42: Symfony 1, mi viejo amigo
Page 43: Symfony 1, mi viejo amigo

VistasQue podemos optimizar

Page 44: Symfony 1, mi viejo amigo

Optimizando Vistas

Tiempos de inclusión de partials

Tiempo en los listados y consultas anidadas

Page 45: Symfony 1, mi viejo amigo

Inclusión de PartialsOptimizando Vistas

Page 46: Symfony 1, mi viejo amigo

Listados y consultas anidadas

Optimizando Vistas

<?php foreach($results as $plan): ?><li>

<?php echo $plan->get(“title”) ?> (<?php echo $plan->countAssistants() ?> )

</li><?php endforeach; ?>

Consultas anidadas

Page 47: Symfony 1, mi viejo amigo

La CacheGuardemos en Cache para salvar algunos (muchos)

milisegundos

Page 48: Symfony 1, mi viejo amigo

La Cache

• Vistas• Doctrine

Como funcionan

Como modificarla

• Memcached• Redis• Mongo?• MySQL?

Drivers

Page 49: Symfony 1, mi viejo amigo

Cómo funcionanLa Cache

Cache de Vistas

Guardar el HTML en un contendor y recuperarlo antes de procesar la web

Cache de Doctrine

Guardar Resultados y Consultas y recuperarlos con facilidad

Page 50: Symfony 1, mi viejo amigo

Cómo funcionanLa Cache

view_cache_manager: class: sfViewCacheManager param: cache_key_use_vary_headers: true cache_key_use_host_name: true

view_cache: class: sfFileCache param: automatic_cleaning_factor: 0 cache_dir: %SF_TEMPLATE_CACHE_DIR% lifetime: 86400 prefix: %SF_APP_DIR%/template

factories.yml

la cache de de vistas

Page 51: Symfony 1, mi viejo amigo

Cómo funcionanLa Cache

la cache de de Doctrine

$manager->setAttribute(Doctrine_Core::ATTR_QUERY_CACHE, $cacheDriver);

$conn->setAttribute(Doctrine_Core::ATTR_QUERY_CACHE, $cacheDriver);

$q = Doctrine_Query::create() ->useQueryCache(new Doctrine_Cache_Apc());

$manager->setAttribute(Doctrine_Core::ATTR_RESULT_CACHE, $cacheDriver);

$conn->setAttribute(Doctrine_Core::ATTR_RESULT_CACHE, $cacheDriver);

$manager->setAttribute(Doctrine_Core::ATTR_RESULT_CACHE_LIFESPAN, 3600);

->useResultCache(true);

Page 52: Symfony 1, mi viejo amigo

Cómo modificarlaLa Cache

sfViewCacheManager

Prepara el objeto para ser almacenado

Crea la clave para nombrar el objeto

Instancia el “Driver” para el almacenamientoGuarda y recupera el objeto

… y algunas cosas mas

la cache de de vistas

Page 53: Symfony 1, mi viejo amigo

Cómo modificarlaLa Cache

public function setPageCache($uri)public function getPageCache($uri)

la cache de de vistas

class sfViewCacheManager

Page 54: Symfony 1, mi viejo amigo

La Cache

abstract public function get ($key, $default = null);abstract public function has ($key);abstract public function set ($key, $data, $lifetime = null);abstract public function remove ($key);abstract public function removePattern ($pattern);abstract public function clean ($mode = self::ALL);

class xyzCache extends sfCache

la cache de de vistasCómo modificarla

Page 55: Symfony 1, mi viejo amigo

DriversLa Cache

Memcached Redis MySQL Mongo

Page 56: Symfony 1, mi viejo amigo

Optimizando SymfonyPodemos hacer algo?

Page 57: Symfony 1, mi viejo amigo

No mucho!

Page 58: Symfony 1, mi viejo amigo

Que podemos hacer?Optimizando Symfony

Desabilitar TODO lo que no se USE

HelpersLogs

Routing CachePlugins que no usen100%

Page 59: Symfony 1, mi viejo amigo

Una idea!

Page 60: Symfony 1, mi viejo amigo

Front ControllerOptimizando Symfony

require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');

$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'prod', false);

sfContext::createInstance($configuration)->dispatch();

Page 61: Symfony 1, mi viejo amigo

Front ControllerOptimizando Symfony

require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');

$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'dev', true);

$manager = new sfDatabaseManager($configuration);

$data = Doctrine::getTable(“Article”)->createQuery()->etc();

$view = CMSProcess::ProcessData( $data );

echo $view;

Page 62: Symfony 1, mi viejo amigo

Gracias!

Page 63: Symfony 1, mi viejo amigo

PreguntasAhora si las hacen Uds!

Page 64: Symfony 1, mi viejo amigo

[email protected]

@josetonyp

[email protected]

Mi viejo amigo