Top Banner
Doctrine Optimization php|tek, May 25, 2011
25

Doctrine 1.2 Optimization

Jan 15, 2015

Download

Technology

Anna Filina

Also covers a little Doctrine 2.0
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Doctrine 1.2 Optimization

Doctrine Optimizationphp|tek, May 25, 2011

Page 2: Doctrine 1.2 Optimization

Anna Filina

PHP Quebec - user group, organizer

ConFoo - non for profit Web conference, organizer

FooLab Inc. - IT solutions for businesses, founder

I write code

Page 3: Doctrine 1.2 Optimization

Content

Doctrine 1.2 optimization

Profiling tools

Find bad DQL

Fix bad DQL

Doctrine 2.0

Share your DQL concerns

Page 4: Doctrine 1.2 Optimization

Tools Used

Doctrine 1.2

MySQL 5.1

PHP 5.3

Xdebug

eZ Components (now Zeta Components)

Page 5: Doctrine 1.2 Optimization

Database Schema

Invoice (2000 rows)

Items (7000 rows)

Tags (1-2 per item)

Page 6: Doctrine 1.2 Optimization

Let’s make it explode!

Page 7: Doctrine 1.2 Optimization

Two queries

$query1 = Doctrine::getTable('Invoice') ->createQuery('inv') ->leftJoin('inv.Items.Tags') ->where('inv.number LIKE "%10"') ->orderBy('inv.subtotal DESC');

$query2 = Doctrine::getTable('Invoice') ->createQuery('inv') ->leftJoin('inv.Items.Tags');

Page 8: Doctrine 1.2 Optimization

Execution Time Average

ezcDebug timed each query 3x

Query 1: 0.20 sec

Query 2: 16.26 sec

What’s taking so long?

Page 9: Doctrine 1.2 Optimization

MySQL or PHP?

Query took 0.002 sec in MySQL

~18,000 objects in PHP

Doctrine hydrates results

Page 10: Doctrine 1.2 Optimization

Hydration Process

Create objects

Copy properties

Reindex results

Convert results to multi-dimensional arrays/collections

And lots of other good (and slow) things

Page 11: Doctrine 1.2 Optimization

Query Variations

->leftJoin('inv.Items');

;

->leftJoin('inv.Items.Tags');->limit(25);

No relations: 2.39 sec (~4x faster)

Limit results: 0.21 sec (~77x faster)

No tags: 9.21 sec

Page 12: Doctrine 1.2 Optimization

Doctrine_Pager

$pager = new Doctrine_Pager($query,$page,$pageSize

);$results = $pager->execute();

Page 13: Doctrine 1.2 Optimization

More Bad DQL Examples

Page 14: Doctrine 1.2 Optimization

Count

$inv = Doctrine::getTable('Invoice') ->createQuery('inv') ->leftJoin('inv.Items') ->execute();

$inv[0]->Items->count();

Page 15: Doctrine 1.2 Optimization

Count

->leftJoin('inv.Items');

->addSelect('(SELECT COUNT(*) FROM InvoiceItem item WHERE item.invoice_id = inv.id ) AS num_items')

$inv[0]->num_items;

Using left join: 8.91 sec

Using subquery: 2.39 sec

Page 16: Doctrine 1.2 Optimization

Memory

->execute();

->execute(Doctrine_Core::HYDRATE_ARRAY);

Using default hydration: 10.24 MB

Using HYDRATE_ARRAY: 0.64 MB, ~16x less

Page 17: Doctrine 1.2 Optimization

Lazy Loading

$invoice->Items[0]->Tags[0];

->leftJoin('inv.Items.Tags tag')

Relations without leftJoin: 0.10 sec

Relations with leftJoin: 0.02 sec, 5x faster

Lazy loading especially bad when iterating

Page 18: Doctrine 1.2 Optimization

Lazy Loading

$inv->Items[] = $item;

$invoiceItem = new InvoiceItem();$invoiceItem->invoice_id = $inv->id;$invoiceItem->item_id = $item->id;

Using relation: 0.03 sec

Using associative entity: 0.00065 sec (~46x faster)

Page 19: Doctrine 1.2 Optimization

Lessons LearnedLimit results

Select only what needed

Use COUNT subqueries where appropriate

Use ARRAY hydration when possible

Avoid lazy-loading in most cases

Also look into database optimization(index columns, de-normalization, storage engine, etc.)

This also applies to Doctrine 2.0

Page 20: Doctrine 1.2 Optimization

Doctrine 2.0

Page 21: Doctrine 1.2 Optimization

Lighter and Faster

1 year of planning, 98% rewrite

Independent model

Faster hydration (3x)

New lazy loading techniques

Batch processing

Page 22: Doctrine 1.2 Optimization

Examples

$iteratable = $query->iterate();foreach ($iteratable as $item) {}

Hydrate the results on-demand in an iteration

COUNT(inv.Items) ... GROUP BY inv.ItemsCOUNT relations

$item1->persist();$item2->persist();$entityManager->flush();

Smart batch saving

Page 23: Doctrine 1.2 Optimization

Caching Layers

Annotations to ClassMetadata

DQL to SQL

SQL to Collection

Page 25: Doctrine 1.2 Optimization

Special thanks to Guilherme Blanco & Raphael Dohms