Performance with Symfony 2
Originally, I wrote that article in french, you can read it here.
Note : I’m not an english native, so be cool with my english :)
Performance can have an important impact for your business and you should take care of that.
Big data can be very useful for selling better: store everything, every users actions, but it can be very horrible for your website or app performance.
The causes of performance loss
There may be many reasons for performance loss :
- Non optimized assets : maybe you don’t minified or unified your CSS and JS files, your images are not optimized for web use. The weight of your assets could be awful… (Website’s front end)
- Servers latency, code interpretation
- Request, Complexity of the database’s architecture, Queries’ complexity, Database type (MySQL, MongoDB, etc.)
Frontend’s optimisations are not the topic, so I won’t write about that here. Just focus on backend solutions and Symfony’s solutions.
HTTP Headers can help you !
Before introducing server’s side solutions, I would like to write about Browser’s cache solution. Every modern browsers can use HTTP Cache, keeping locally a page, and every X seconds ask to the server if that page changed since last check.
The HTTP Cache is well documented here : https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=fr
The principle is pretty simple, when we display an HTML page, we will send informations in the page’s headers : cache lifetime, an unique id aka ETag.
When the cache lifetime is over, the browser will check the ETag value and compare it to the old one. We need to create an ETag with as less informations as possible and change it after every content modifications.
The Response’s object from HTTPFoundation’s Symfony component can handle it for you in your controller :
I’m defining an ETag with the update date, if datas haven’t been changed since last time we checked, then we stop our script and return a 304 header (not changed) automatically returned by Response instance.
We can specify the cache lifetime (in second)
Browsers can check automatically the HTTP cache and handle it. But with cURL, you need to add additionnal header to check the ETag and locally save the cURL response.
More informations about cURL and ETag:
The first thing you need to use when you care about performance is an HTTP cache and use it wisely. Your server will thank you.
use Memcached to cache query results and usefull data
When you need to use a huge amount of data, performance can decrease. You need to focus on your database and queries results.
Memchached is a cache server with High performance to data access. You can use it to put non-sensitive data and access it quickly (and let your database to rest a while). Memcached can be isolated on a different server from your site.
Memcached is pretty easy to use with PHP, a Memcached request is much more efficiant than a MySQL request. But you need to store strings, if you want to store objects, arrays or other complex datas, you need to serialize it before putting it into Memcached.
You can specify several Memcached servers to your PHP’s Memcached library, data allocation will be handled by it. You can specify server’s priority too.
PHP Documentation http://php.net/manual/en/memcached.addserver.php
Symfony’s cookbook has a page dedicated to doctrine’s caching drivers : http://symfony.com/doc/current/reference/configuration/doctrine.html#caching-drivers
Your Doctrine configuration section should be like this (config.yml) :
I instantiate the Memcached PHP class, add a Memcached server, and create a Doctrine Memecached object (service.yml).
- metadata_cache_driver : Will parse once your entity metadata
- query_cache_driver : Will cache the transformation from DQL to SQL
- result_cache_driver : Will cache the query results set.
To use your doctrine cache driver, you will do that :
Pretty easy, isn’t it?
We can use Memcached in our controllers as well, just get your service and use it normally :
Note : You can use your Memcached server to handle PHP sessions, just add that in your php.ini :
Your database server certainly have a cache too
MySQL has a cache, disabled by default, but you enabled it easily. You can use that tutorial : http://www.cyberciti.biz/tips/enable-the-query-cache-in-mysql-to-improve-performance.html
Pay attention, you can’t specify a lifetime for you MySQL cache. You will have to reset it sometimes like this :
RESET QUERY CACHE
You can read the official documentation from MySQL : http://dev.mysql.com/doc/refman/5.5/en/reset.html
Database Architecture and technology matters…
Database cache is not the only thing you can do. You need to think about your data architecture and what kind of database should you use?
If you are using a table with millions of users data, any request involving that table could be very resource intensive. But a simple solutions is to split your users into several database servers. Using database_1 for Users with their usernames from A to E, database_2 for from F to H, etc.
Using mongoDB, MySQL or PostgreSQL can have an influence to your database performance, you need to find which database’s kind can fit for your project and data. You can compare database’s performance on Google, few examples :
For search engines, you can use Elasticsearch
Note : Don’t use Elasticsearch as a SQL database, it’s not safe, you don’t have credentials to access to your data, so you can’t store password or criticful data with that.
To use Elasticsearch, you can use a Symfony’s bundle : https://github.com/FriendsOfSymfony/FOSElasticaBundle You need to use Elasticsearch 1.7 at least.
This post is not exhaustive, there’s a lot of solution to scale your Website (or API). Finally, you need to configure your solutions taking care of your application’s needs.
You don’t certainly have to load a whole page everytime. Use cache lifetime wisely, update your cache when you update your data, you will gain money and time.