symfony 1, mi viejo amigo
DESCRIPTION
Algunas ideas y experiencias tratando de optimizar Symfony 1.4 Doctrine 1.2 para una web de alto rendimiento.TRANSCRIPT
Mi viejo amigo
Un poco sobre mi
Estoy trabajando con webs desde 2001Me encanta investigar y meterme en problemas
era solo un poco!
青玉男ao tama o
ao
Y por qué hablar de 1.4?
PreguntasLas hago yo!
Que podemos optimizar?
Experiencia de Usuario?
Carga de proceso?
Tiempo de descarga?
Uso de memoria?
Carga de procesoTiempo que pasa el servidor procesando la web
Proveedor de Internet
Desarrolladores Backend
Desarrolladores Frontend
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
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
La optimización dueleVale la pena el
esfuerzo?
Que podemos mejorar
Symfony Internals
Cache
Doctrine
Hechos
Tiempo de renderizacion
Cantidad de consultas y sus uso
Los helpers y librerías
Logs agregan algo mas de tiempo
+ Hechos
Herramientas
Debug Tool Bar
sfTimer & sfTimerManager
Debug Tool Bar
Todos la conocemos ya!
sfTimerManager
$timer = sfTimerManager::getTimer("test");...var_export ( $timer->addTime() );
sfTimerManager
sfTimer
$timer = new sfTimer ("test");$timer->startTimer();...var_export ( $timer->addTime() );
DoctrineQue podemos optimizar
Optimizando DoctrineCantidad de consultas
Tiempos de consulta
Tiempos de hidratación
Tiempo de conversion de consultas
Conteo de registros
Selects y joins e indices
Cantidad de ConsultasOptimizando Doctrine
Tiempo de ConsultaOptimizando Doctrine
0.02s
0.02s
0.02s
0.02s
0.02s
Tiempo que nos cuesta buscar información en la DB
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;
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?
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
Tiempo de HidrataciónOptimizando Doctrine
$conn = Doctrine_Manager::connection();$data = $conn->execute("select * from countries")->fetchAll();
Tiempo de HidrataciónOptimizando Doctrine
$conn = Doctrine_Manager::connection();
Para que quiero el ORM?
Para manejar las conexiones!
Doctrine?Una interesante paradoja!
Tiempo de HidrataciónOptimizando Doctrine
Name Surname Age
Pedro Perez 60
Raúl Perez 54
Daniel Rodriguez 76
…
Ejemplo!
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
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
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
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
PDOhttp://es2.php.net/manual/en/book.pdo.php
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
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
Select, Joins e IndicesOptimizando Doctrine
MySQL
Doctrine
Sphinx
MySQL
Doctrine
✔
VistasQue podemos optimizar
Optimizando Vistas
Tiempos de inclusión de partials
Tiempo en los listados y consultas anidadas
Inclusión de PartialsOptimizando Vistas
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
La CacheGuardemos en Cache para salvar algunos (muchos)
milisegundos
La Cache
• Vistas• Doctrine
Como funcionan
Como modificarla
• Memcached• Redis• Mongo?• MySQL?
Drivers
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
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
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);
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
Cómo modificarlaLa Cache
public function setPageCache($uri)public function getPageCache($uri)
la cache de de vistas
class sfViewCacheManager
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
DriversLa Cache
Memcached Redis MySQL Mongo
Optimizando SymfonyPodemos hacer algo?
No mucho!
Que podemos hacer?Optimizando Symfony
Desabilitar TODO lo que no se USE
HelpersLogs
Routing CachePlugins que no usen100%
Una idea!
Front ControllerOptimizando Symfony
require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'prod', false);
sfContext::createInstance($configuration)->dispatch();
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;
Gracias!
PreguntasAhora si las hacen Uds!