This is what Mike Peters says he can do: make your site run 10 times faster. His test bed is "half a dozen servers parsing 200,000 pages per hour over 40 IP addresses, 24 hours a day." Before optimization CPU spiked to 90% with 50 concurrent connections. After optimization each machine "was effectively handling 500 concurrent connections per second with CPU at 8% and no degradation in performance."
Mike identifies six major bottlenecks:
Database write access (read is cheaper)
Database read access
PHP, ASP, JSP and any other server side scripting
Client side JavaScript
Multiple/Fat Images, scripts or css files from different domains on your page
Slow keep-alive client connections, clogging your available sockets
Mike's solutions:
Switch all database writes to offline processing
Minimize number of database read access to the bare minimum. No more than two queries per page.
Denormalize your database and Optimize MySQL tables
Implement MemCached and change your database-access layer to fetch information from the in-memory database first.
Store all sessions in memory.
If your system has high reads, keep MySQL tables as MyISAM. If your system has high writes, switch MySQL tables to InnoDB.
Limit server side processing to the minimum.
Precompile all php scripts using eAccelerator
If you're using WordPress, implement WP-Cache
Reduce size of all images by using an image optimizer
Merge multiple css/js files into one, Minify your .js scripts
Avoid hardlinking to images or scripts residing on other domains.
Put .css references at the top of your page, .js scripts at the bottom.
Install FireFox FireBug and YSlow. YSlow analyze your web pages on the fly, giving you a performance grade and recommending the changes you need to make.
Optimize httpd.conf to kill connections after 5 seconds of inactivity, turn gzip compression on.
Configure Apache to add Expire and ETag headers, allowing client web browsers to cache images, .css and .js files
Consider dumping Apache and replacing it with Lighttpd or Nginx.
Find more details in Mike's article.
Reader Comments (12)
What a lame article for http://highscalability.com site...
What's lame about it?
Lame is the word indeed. The article starts with a outrageous claim and then proceeds with such gems as offline database writes and no more than 2 queries per page which are of course nice if your application is so simple that you can get away with it.
That doesn't mean all the advice is bad, just that there's a lot of open doors being kicked in and that basically we are talking about one hell of a poorly written web application if you can get anywhere near a 10 fold increase in performance like this.
The more useful advice relates to actually measuring what is going on. Tackle the largest bottlenecks first and don't spend time optimizing stuff that isn't a problem. In most cases with a vanilla php app, there's probably a lot you can do to improve the way it interacts with the database. That's a no brainer. Obviously doing large joins over several tables or doing multiple queries in a loop is something you will pay for. Sometimes it is necessary to do that but often you can be a little smarter. Profiling will tell you how long things are taking.
Then the web design of course matters. Basically even with http pipelining, there is only so much requests a browser can do in parallel. That means that if your page downloads 80 images, javascripts and css files, with 4 parallel connections you are still looking at 20 sequential requests. Those take time. Solution: 1 css file and 1 javascript file and 1 html file. Inline serverside as much as possible. Basically the browser can start rendering after those three have been downloaded in parallel. Javascript is critical to the rendering usually. Images don't block the page rendering so they can wait. Not always feasible but generally doable.
Then caching is another obvious optimization. Basically the goal of caching is to avoid IO (disk or network) and processing (whichever is the bottleneck). There's various solutions and various places where you can cache but they all boil down to trading memory for processing/IO. Since the database is slowest, start there. Result set caching for common queries is nice. Putting the entire DB in memory is also nice (beware of crashes though when doing writes). Even better is to cache parts of dynamically generated pages that don't change so often (e.g. page menu). Some of the better CMS systems out there do stuff like this. Another way to take advantage of caching is to use a proper database abstraction layer. On Java side there's various pretty good ones that can do all sorts of clever stuff and optimizations. Ruby and python have similar abstraction layers and are slowly catching up with Java (not quite there yet though). If you want to talk raw sql to a database, you'd better be good at it (most web developers I know suck at SQL).
Saving bandwidth is of course always nice. Recompressing images and using GZIP can help here. Only remember that you are now trading CPU for cost. So this is a different kind of optimization. Offloading static stuff to a separate server and, again, doing some caching helps of course.
Anyway, if you need 10 servers to handle 200K requests per hour, you can probably squeeze out a lot of performance still. That's only 20K requests per server per hour or about 333 per server per minute. That's not that much for a modern server.
As to reducing queries -- I sped up a group of pages by replacing multiple calls to a SQL utility classes. There was one call for each data item on the page.
I replaced this with not quite 2 but under five queries and then pulled the required data out of the returned arrays.
The code was more complex but the performance enhancement was considerable.
In this case, when the project began there were rarely more than 20 data items on each page, but, as workload increased, we were well over 2,000 data items, and so over 2,000 hits on the database.
I don't see how this is a lame article, myself. It's a useful checklist of things to look at.
The item about "Avoid hardlinking to images or scripts residing on other domains" is a bit dubious.
Having a domain that holds immutable objects with expires headers set to *far* into the future, so that many subdomains can share the same cacheable object, as well as ensure that the cacheable object is as cacheable as possible. This is effectively what a CDN does, and you can run your own femto-cdn until you reach a scale point where farming out your immutable media to a real CDN makes sense.
I thought all of the items to be great. I even installed YSlow and Firebug.
I've printed this list and I will list all of the calls and see if there are any writes and try other optimizations.
Of course the claims are grandeose. This is the blago-web people!!
I for one, accept this advice.
I think the criticism comes from expecting an 'article' to have an in depth discussion (which actually is taking place in the comments) rather than bullet points and a link elsewhere.
The bulleted list does cover a good section of things to consider - but the details of how to actually measure and consider tradeoffs isn't there. The article would be improved by linking to specific articles with an in depth discussion of each point in the high level list.
Dude, you got one thing wrong for sure:
INNODB is good for READs not WRITE... Please check stuff up before you put on such a respected site!
http://www.mysqlperformanceblog.com/2007/01/08/innodb-vs-myisam-vs-falcon-benchmarks-part-1/
Todd -
Thanks for posting this article.
It is inspiring to see your readers are asking for more in-depth descriptions and I'll be sure to post a step-by-step how-to in the near future.
If there's one thing I want developers to take out from this article, it is - Switch EVERYTHING to offline processing (caching, no real time processing) and use YSlow fanatically for client-side optimization. Ok, two things. But those two are truly going to give you the best bang for the buck, in terms of performance boost.
Bart -
I have a lot of respect for Peter @ MySQLPerformanceBlog. I have been a long time subscriber and got a chance to speak to Peter on a few occassions about doing some work together.
If there's one thing Peter keeps emphasizing on his blog it is - Do your own tests.
The specific tests you linked to, where Peter is comparing MyISAM, Falcon and InnoDB, don't reflect a typical high availablity scenario because Peter is running the same query concurrently. InnoDB has much better built-in caching that enables it to shine in these cases.
We have been testing InnoDB vs MyISAM in real life high availablity production scenarios, where a server is hit by 100 concurrent connections with varying queries across multiple tables (400 tables to be exact).
By design, MyISAM is lightweight and, IN OUR EXPERIENCE, based on our tests, much more capable of handling spikes in READ queries, whereas InnoDB lags behind. InnoDB truly shines when you have a situation with concurrent WRITEs thanks to row-level locking versus locking the entire table with MyISAM.
As always, do your own tests and share your results.
Lame for highscalabitlity.com indeed...
For people looking for more insight into techniques he mentioned (and borrowed from) should look at
http://developer.yahoo.com/performance/rules.html
http://www.ibm.com/developerworks/linux/library/l-tune-lamp-3.html
http://www.ibm.com/developerworks/linux/library/l-tune-lamp-2.html
I think this is a great article. All you guys who said it's lame, I'd invite you to post the articles you put together.
I think the list of bullet points is a great start and will try to incorporate some of the suggestions on my next project
I guess making it compatible with W3C may also help in a lot.
-----
http://underwaterseaplants.awardspace.com">sea plants
http://underwaterseaplants.awardspace.com/seagrapes.htm">sea grapes...http://underwaterseaplants.awardspace.com/plantroots.htm">plant roots