My last post on this subject was just a collection of notes. I’ve edited it down to a list of actual things that should be done to improve Drupal performance.

Broad Strokes

In descending order of importance:

  1. Plan ahead for performance config & testing so you’re not caught off guard on launch day.
  2. Don’t expect Apache to work out of the box — because it’ll crash your server within 12 hours of launch, guaranteed.
  3. Use the minimum number of modules in Drupal. Turn off devel in live sites.
  4. Turn on APC. Change the defaults.
  5. Make sure MySQL queries are cached, because they’re not by default.
  6. Configure Apache to always fill all available memory.

1. Project-level Considerations (in Summary)

  • Do not wait until the day of launch to consider performance!
  • Staging (aka launch prep) needs to be a part of the build process — don’t go straight from dev to launch.
  • Project managers need to build-in time for performance tuning on client server before launch.

2. Drupal-level Optimizations (in Summary)

  • Always turn built-in caching on.
  • Handle 404 errors better.
    • always add this directive to .htaccess:

        <FilesMatch "\.(png|gif|jpe?g|s?html?|css|js|cgi|ico|swf|flv|dll)$">
          ErrorDocument 404 default

    There is no need for Drupal to return a 404 error for these objects! Most of the time these requests happen in the background anyway, so a Drupal-rendered page is not needed.

  • Watchdog table should never be MyISAM as MyISAM is not write-optimized.
    • A reasonable option is to convert it to InnoDB.
    • Archive, if available, could have much greater performance — but may not be available in all MySQL distributions.
  • Consider turning off the standard Drupal logging and instead rely on syslog.
    • Doesn’t rely on the database layer to store errors, which means Drupal doesn’t have to wait for that database write to complete before moving on. The end result is faster page loads, period.

3. PHP-level Tuning (in Summary)

  • Turn on APC every time.
    • Give APC enough memory to fit Drupal: Increase apc.shm_size from default of 32MB to 48MB
    • /dev/zero fix for shm_size limits in OS layer
    • For very limited resources you can also consider excluding contributed modules so you can cache Drupal core only: apc.filters = "+inc$,+php$,-admin,-sites/all/modules"
  • Use memcache when not using Boost (i.e. when most traffic is authenticated).

4. Apache-level Tuning (in Summary)

  • Disable any Apache LoadModule calls if they’re not needed.
  • Strongly consider using FastCGI over mod_php.
    • Static page requests will use less memory, which means there can be more requests simultaneously.
    • This directly affects all sites, especially those using Boost.
  • If you’re stuck using mod_php:
    1. Follow this mantra: “There is no prize for having a lot of free memory.” If you have memory, use it!
    2. Keep as many idle PHP interpreters hanging around for as long as possible. Example config (tune to server resources):

             StartServers 40
             MinSpareServers 40
             MaxSpareServers 40
             MaxClients 80
             MaxRequestsPerChild 20000

      i.e. Start at 40 child processes, stay at 40 even when idle, burst up to 80 in heavy load.

      If you tune Apache well, and remove all the unneeded modules, and install a PHP op-code cache/accelerator, then you can make each Apache process take as little as 12 MB.

  • Consider not even using Apache at all and use Lighttpd.

5. MySQL-level Tuning (in Summary)

  • Turn query cache on. It’s off by default.
    • No such thing as optimal cache size for all cases.
    • Query cache memory could be put to better use, more web processes, memcache, etc., so don’t go overboard.
  • Analyze lock contention by checking the Table_locks_immediate and Table_locks_waited status variables: SHOW STATUS LIKE 'Table%';
    • If you’re finding long wait times, consider changing table storage engines from MyISAM to InnoDB (or others).

6. Server-level Tuning (in Summary)

  • Reduce the number of services that aren’t directly supporting Apache and MySQL.
    • The more memory available to Apache and MySQL, the better.

7. Reverse Proxy and/or Static Caching (in Summary)

  • Varnish is great, but apply it like real-world varnish: only after all the other cracks have been sealed.
  • Boost is great, but it would be even better if Apache was running with FastCGI because the memory footprint for anonymous views would be next-to-nothing.