Thursday 5 January 2012

Understanding Magento Scalability and Performance


Understanding Magento Scalability and Performance

image
Performance and scalability are hot topics for any enterprise application, and this is especially true of Magento. Our clients are usually impressed with the features and extensibility of Magento, but there is a worry in the background if the platform can scale to many thousands of orders per day or huge catalogs. This worry is partly because early versions of Magento had some issues with performance, and competitors used this as an argument in favor of their product. But it is also true that getting Magento to be fast and scalable requires some knowledge of the underlying software stack – such as the web server and PHP configuration – and a little custom development to optimize Magento’s caching for your specific use case. This article focuses on getting the most out of a single server, or virtual machine; as you will see, this can take you very far already.
Visions works mostly on large projects and these invariably involve consultancy on infrastructure, performance and the like. There are two concerns: first, everybody wants to have the shortest loading time possible, which is what we mean by performance. Amazon and Google did some research on this, and found that a hundred milliseconds of delay can already reduce conversion rates. Performance is important for projects large and small, so we will take about in some depth.
The second problem is scalability, namely: will my shop remain fast when there are visitors on the site? Think of this is as the online analog of standing in line at the cashier: if you are the only consumer, you can go to pay directly – so performance is good. But when there are many consumers, everybody has to wait to get his turn; in other words, a single cashier doesn’t scale to a large supermarket.
Clearly, the two are related – if you give the cashier a better till, performance will improve further and queues get a little shorter. But even the best till will only take you so far, and sooner or later you need a second cashier. In the Magento world, this means offloading some parts of the shop to a separate server, or even moving to a fully-fledged cluster system. Setting up a cluster system is difficult and can take a lot of time. We’re not going to cover it here; instead, our focus will be to get the maximum possible scalability and performance out of a single, suitably-sized server.
In essence, I’ll walk you through the steps we at Visions take when we do infrastructure consultancy in such settings. First, I will show you some configuration tweaks that will let you score some easy wins. We then take a look at two tools that help you understand where the loading times of your Magento come from, and estimate how scalable your deployment is.
Now that you have some numbers, you want to improve them. We’ll discuss three of them: switching your web server to use FastCGI instead of mod_php, enabling Block Caching for selected Catalog and CMS blocks, and convincing your designers to make fewer HTTP requests per page load.
Most of the time, this will be more than enough to get a snappy Magento shop that scales to some serious visitor numbers per day. If your project is larger still, we’ll leave you with a few pointers for what you can do next.

Easy Wins

Be sure the Magento caches are enabled
MySQL comes with a query cache, which can save the result of SELECT queries for short periods of time – that is, until some change is made to the database. How much benefit you get from the query cache depends strongly on the kind of application you use – this is why the query cache is not turned on by default. When it comes to Magento, tests have shown again and again just how much extra scalability you get if it is enabled. To enable query caching, go to your my.cnf and set the following options in the [mysqld] section:
query_cache_type=
query_cache_size
=64M
Save your changes and restart the MySQL server – getting a third more requests per second is not uncommon after the change!
There is a lot more optimization that you can do on the MySQL level, but it is beyond the scope of this post. Check out chapters 6 and 7 in O’Reillys High-Performance MySQL 2nd edition for an excellent guide. The authors also have a great blog post on the query cache at http://www.mysqlperformanceblog.com/2006/07/27/mysql-query-cache/.
Enable Expires Headers
Browsers use caching extensively, and can save a lot of the elements included in a web site locally so that they can be served from the browser’s cache rather than the web server on the next request. This can help quite a bit in shortening load times. The problem is for the browser to know when a file can be served from the cache, and when not – because the local copy is outdated.  To solve this issue, browsers rely on two HTTP headers, Expires and Cache-Control.
Magento’s default .htaccess file already configures these according to Yahoo’s performance recommendations (more on them below), but does not enable them by default. To enable them, all you need to do is add the following lines to your Apache server configuration (usually found in /etc/apache2/apache.conf):
<IfModule mod_expires.c>ExpiresActive On</IfModule>
If you don’t have access to the server configuration, you can also add those lines to Magento’s .htaccess file – but then you need to be careful when updating Magento that your changes do not get lost.
Use APC as the cache backend
By default, Magento stores its cache data in the file system. This is usually fine for small sites, but as you get more and more requests, reading and writing to the file system all the time gets slow. This is especially true if you store your Magento on a networked drive, such as NFS, which is much slower than a local disk. If you are using APC, this problem is compounded further by the architecture of APC.
If you are using APC, you can usually improve the throughput of your Magento by storing cache data in APC’s cache instead. Open your app/etc/local.xml and add the following lines: 
<global>
    <
cache>
         <
backend>apc</backend>
         <
prefix>MAGE_</prefix>
    </
cache>
</global>
After you’ve saved your changes, remember to refresh the configuration cache through the admin panel.
Enable MySQL query caching
MySQL comes with a query cache, which can save the result of SELECT queries for short periods of time – that is, until some change is made to the database. How much benefit you get from the query cache depends strongly on the kind of application you use – this is why the query cache is not turned on by default. When it comes to Magento, tests have shown again and again just how much extra scalability you get if it is enabled. To enable query caching, go to your my.cnf and set the following options in the [mysqld] section:
query_cache_type=
query_cache_size
=64M
Save your changes and restart the MySQL server – getting a third more requests per second is not uncommon after the change!
There is a lot more optimization that you can do on the MySQL level, but it is beyond the scope of this post. Check out chapters 6 and 7 in O’Reillys High-Performance MySQL 2nd edition for an excellent guide. The authors also have a great blog post on the query cache at http://www.mysqlperformanceblog.com/2006/07/27/mysql-query-cache/.

Measure your Magento

Understand Loading Times with Fiddler
When loading a web page in the browser, you get a feeling for whether the site is „fast“ or not. This feeling is very important, especially for your end users. But it is not actionable information, because if all you know is that the page is not fast, you don’t know where to start changing your code or configuration.
Thankfully, Microsoft has released a very helpful freeware tool called Fiddler, which is available for Windows only. Fiddler works as an HTTP proxy on your desktop computer, tracing the content of each request and response that your browser makes. You can then review the content of each of these, try „fiddling“ with requests and see your app behaves, and much more. Fiddler can be a really helpful tool, from debugging Ajax to improving performance generally, so it may be worth your while to read some of the documentation and screen casts on their site.
We want to understand what makes up the page loading time of our Magento store, by components. And Fiddler can show a page load timeline that does exactly that. So after you have installed Fiddler and configured your browser to use it, empty your browser cache and go to the store page you are interested in. You will see a log of each request made in the Fiddler window. Select all the requests and click on „Timeline“ in the right pane.
You will see a picture that looks a bit like this:
image
This time line was taken from a demo store, and shows you that Magento itself was running for just one second (the blue bar at the top in the right pane). The browser had only loaded all page elements after 11 seconds though – and three of these seconds were spent waiting for the JavaScript to load! When you think about performance improvements to your site, it is crucial to bear in mind that loading static objects can take 10 times as long as running Magento! Before touching the Magento application, you want to make sure that you optimized your static file delivery.
Use YSlow for additional advice
Yahoo has released a free Firefox add-on called Yslow that can also give you information on page loading times and – in contrast to Fiddler – give you easy-to-understand hints on how to improve performance. Go tohttp://developer.yahoo.com/yslow/ for more information and download it – there is a very comprehensive user guide as well.
What YSlow gets right it is that it alerts designers to the role they play in getting great site performance. Reducing the number of HTTP requests – another topic we will return to below – is a very effective strategy to make your site feel faster for users.
Simulate Real-Life Users with Siege
Siege is an open-source tool developed in Perl that lets you simulate visitors surfing on your site. This is the best way to get a feel for how much load your application server can handle, given its current configuration.
Many administrators and developers estimate scalability of their site by hammering a (staging) server with requests. Basically, this means calling the same single URL over and over and seeing how many requests get through per second.You can do this with the Apache Benchmark tool ab2, for example.
The problem with this approach is that real users don’t behave like this. The numbers you get are hence very misleading: first, if you enable caching, tools like ab2 will tell you how your application scales if the cache hit rate is nearly 100%. In reality, your hit rate will be lower because visitors to go many different pages all the time, so ab2 overestimates your scalability.
At the same time, it is difficult to interpret the numbers you get. Suppose your web server can handle 5 dynamic requests per second. Does that mean you can only 5 people visiting your site simultaneously? Of course not, because humans make requests at a much slower rate than a benchmarking tool. How many sessions your server can support is still difficult to estimate though, and will depend on many factors. Simulating user sessions directly is the best way around this problem, and it is strongly recommended that you use a tool like siege before going live with your Magento store. Too many sites crash immediately after load because the server capacity was drastically overestimated; nobody knows how many oversized servers are standing idle around the world because the opposite problem occurred.
The Siege home page at http://www.joedog.org/index/siege-home gives you all the info you need to get started. If you are running Linux, see if your distribution has a ready-made package available before installing it manually. As shown in the README, you need to create a text file containing the requests made in each session. Siege keeps track of cookies, so you can have users first logging in, then adding something to the shopping cart, etc.
Getting started with Siege takes a bit of time and practice, as does setting up the URLs file for your tests. But having this tool at hand can be invaluable for the success of your project.

Three Steps to Improve Scalability and Performance

Enable Block Caching where it makes sense
Out of the box, Magento can cache the Block output of your pages. When the next user requests the same block, the output that was previously calculated can be returned directly – without going through all the database queries and model calls again. This is really helpful for parts of pages that don’t change too often but are somewhat expensive to calculate – such as category pages. Clearly, it makes a lot more sense to calculate output once and then cache it for, say, five minutes, than to continuously go through the same model calls with the same results again and again.
Block caching is not enabled out of the box, because you have to set the right cache to suit your use deployment. For example, if your prices depend on consumer groups, you need to add the consumer group to the cache key.
My recommendation is that you read the wiki page on the subject, and then extend the Mage_Catalog_Block_Category_View class to enable caching. It is quite common that this simple change can double the number of sessions your server can support.
Make fewer HTTP Requests in your Theme
As YSlow alerts you, having many HTTP requests to load a single web page is a performance killer. Even if you enable keep-alive or use a content delivery network, it is still much slower to load many small images than a single bigger file that contains all images. Reducing the number of HTTP requests is largely a performance measure, but also helps scalability a bit.
Using image sprites, you can put all the icons, buttons etc. that you have in your theme into one file that can be downloaded quickly and then just show parts of this large image where you need it. All this involves is a little bit of CSS wizardry. A List Apart has a nice tutorial to get you started at http://www.alistapart.com/articles/sprites/.
Use FastCGI to run PHP
If you are using Apache as your web server, there are two ways you can sensibly set up PHP. The first way is to use the mod_php module, which is easiest to use and hence the default with many hosting providers. In this case, a PHP interpreter is run with each process of the web server and on stand-by until a script is executed.
If you are wondering why your Apache needs a lot of memory, probably the fact that you are using mod_php is a big part of the answer. On a large site, there may well be hundreds of Apache processes running, and each has its own PHP interpreter. However, only very few of them – often less than one in ten – actually need to run PHP. The rest serve static files or simply wait for new requests. Because PHP uses a lot of memory, it is a good idea to see if you can avoid the overhead generated by having dozens and dozens idle PHP processes running.
The way to avoid the overhead is to use FastCGI instead of mod_php. With FastCGI, a separate internal daemon is run on your web server that is contacted by the web server only when execution of PHP required. Thus you do not need to carry the PHP baggage for all requests.
Setting up FastCGI requires you to make some changes to your server configuration, but the benefits will be large. A good starting point is this article: http://2bits.com/articles/apache-fcgid-acceptable-performance-and-better-resource-utilization.html. For more details, check the Apache web site and the documentation of your distribution.
Alternative: Turn Off Keep-Alive if you have to use mod_php
If you cannot, or do not want to, switch to FastCGI, you can still do something to reduce the memory usage per visitor. This is important because each server has only so much memory, and if you can serve a visitor with less memory, you can server more visitors in total and scale further with given resources.
As I pointed out above, the big problem with mod_php is that you need to keep a PHP interpreter running with each Apache process, and that most Apache requests do not involve PHP. By default, Apache enables a feature called HTTP Keep-Alive, which lets visitors re-uses a connection for several requests. This makes the web site faster for the visitor, because images and other static files can be loaded without continuously re-connecting with the web server. But it also means that your web server will have many idle processes, waiting for new requests that may never arrive. And each of these idle processes runs a full PHP interpreter.
To turn off Keep-Alive, search your Apache configuration files for the KeepAlive directive. If the directive is not set, add the following line to your config file
KeepAlive off
and then restart Apache. If it is set, ensure that it is set to “off“. You should start to see lower memory usage immediately.
Use a Content Delivery Network
Another way to relieve stress from your servers is to get an external party to serve static files for you. These services, called Content Delivery Networks (CDNs), are getting very popular and prices have fallen a lot over the last year or so. A CDN can really improve the user experience on your site, and is another way around the problems created by mod_php.
Setting up a CDN is quite easy with the free One Pica CDN extension from Magento Connect.

Going Further

You have followed all the steps of this guide, and still your Magento does not scale to the level you need or give you the performance you crave? First, check again if you have really implemented all the steps. The configuration I described above is known to work very well with large Magento stores, serving hundreds of thousands of page impressions per day. Check that your hardware works well and that you are not limited by the bandwidth of your server.
If your site is truly huge, you will want to move to a cluster infrastructure that also provides high availability to insure you against software or hardware failures. You need to set up MySQL replication for your database and balance load between web servers. In practice, this will usually mean that you move to a specialised managed infrastructure provider or get outside consultants to set up the infrastructure for you.
Magento is very well suited for such deployments. You can set different database connections for read and write queries, which is important when you use MySQL replication, use memcache as a distributed cache backend, and the like. So Magento will not limit you there – the problems you may encounter will be related to the operating system and other software stack.

Conclusion

Performance and scalability are important, but difficult, topics. Here I have tried to get you started with the tools and strategies you can use to get more out of your existing infrastructure. Very often, just taking the easy wins is more than enough to get the power you need for your Magento shop.
As a side note, I want to point out that most of the topics we covered did not relate to Magento directly, but to the server configuration. And this is something you should bear in mind: often, the performance and scalability issues you face are not problems of Magento, but of your infrastructure. But because Magento is quite a complex application, you will run into these problems earlier than with simpler apps. 

No comments:

Post a Comment