Optimizing Apache and MySQL for Low Memory Usage, Part 2

Posted in General at 4:38 pm by matt

In Optimizing Apache and MySQL for Low Memory Usage, Part 1, I discussed some system tools and Apache optimization. I’ve also discussed mod_deflate, thttpd, and lighttpd in Serving Javascript and Images — Fast. Now, i’ll talk about MySQL.

Tweaking MySQL to use small amounts of memory is fairly straightforward. You just have to know what to tweak to get the most “bank for your buck,” so to speak. I’m going to try to show you the why instead of the what, so you can hopefully tweak things for your specific server.
We’ll look at the following MySQL types of mysql settings:

  • Things We Can Disable
  • The Key Buffer
  • The Table Cache
  • The Query Cache
  • Max Connections

Roughly, the amount of memory mysql uses is defined by a fairly simple formula: query_cache + key_buffer + max_connections * (other buffers). For a low volume site, query cache and key buffer are going to be the most important things, but for a larger site, you’re going to need to look at other things. Additionally, using the key buffer and the query cache are AMAZING performance increasers. I’m only showing you how to lower the amount of ram MySQL uses for if you’re trying to run a few smaller sites that don’t store hundreds of megs of data.

Things We Can Disable

First off, InnoDB requires about 10 megs of memory to run, so disable it. You shouldn’t need it if you’re going small. For those unfamilar, innodb is a different storage engine within mysql that you can use. It supports transactions and most importantly (to me, at least), row level locking. It’s a little bit slower than MyISAM, but it can greatly improve performance later. Basic example: changing a table in a MyISAM table locks the entire table. You can’t do any selects while you’re inserting. If you’re inserting a lot, this can be a problem. InnoDB lets you insert or update a row while still performing selects. It locks just the rows you’re working with, rather than the whole table.

You can disable InnoDB with “skip-innodb”

You can also disable BDB (berkely database, a deprecated alternative to InnoDB) and NDB, MySQL’s clustering database. Do this with “skip-bdb” and “skip-ndbcluster” I haven’t noticed skipping BDB and NDB to reduce memory much, but if you’re not using them, it can’t hurt.

The last thing you can skip is networking, with “skip-networking” I haven’t noticed this lower my RAM utilization, but if you’re not accessing mysql from a remote server, you should use the local unix socket to get better performance as well as better security. If you don’t have mysql listening on a TCP port, then you’re a lot less likely to get hacked. Also, for those of you who might be worried about having to configure PHP to connect to MySQL on the local socket, if you specify localhost as your hostname in mysql_connect() in php, it automatically uses the local unix socket, so there’s no need to worry.

The Key Buffer

This is probably the single most important thing you can tweak to influence MySQL memory usage and performance. The MySQL Reference Manual says about the key buffer:

Index blocks for MyISAM tables are buffered and are shared by all threads. key_buffer_size is the size of the buffer used for index blocks. The key buffer is also known as the key cache.

The maximum allowable setting for key_buffer_size is 4GB. The effective maximum size might be less, depending on your available physical RAM and per-process RAM limits imposed by your operating system or hardware platform.

Increase the value to get better index handling (for all reads and multiple writes) to as much as you can afford. Using a value that is 25% of total memory on a machine that mainly runs MySQL is quite common. However, if you make the value too large (for example, more than 50% of your total memory) your system might start to page and become extremely slow. MySQL relies on the operating system to perform filesystem caching for data reads, so you must leave some room for the filesystem cache. Consider also the memory requirements of other storage engines.

In other words, MySQL tries to put everything that’s indexed into the key buffer. This is a huge performance speedup. If you can get every table column in a specific select statement to be indexed, and your entire index fits into the key buffer, the SQL statement in question will be served directly from RAM. It’s possible to take that kind of optimization overboard, but if you are going for speed (not memory), that’s one way to do it.

I can’t say what size you should make your key buffer, because only you know how much ram you have free. However, you can probably get by with 2-3 megs here, bigger if you need it. If you want to play MySQL Memory Limbo (how low can you go!), you can look and see how much your key buffer is being used. Essentially, you’ll need to write a query that uses the SHOW syntax and uses the following equation:

1 – ((Key_blocks_unused × key_cache_block_size) / key_buffer_size)

This yields the percentage of the key buffer in use. After restarting mysql, let your site run a while and have time to fill up the key buffer (assuming it’s live. if not, simulate some use, first). Then, check the usage using the aforementioned equation. If you’re running below, say 0.8 or so, you can probably safely lower your key buffer size.

The Table Cache

MySQL seems to think that this one is the second most important thing to tweak, and it is. However, it’s really important for performance, marginally so for memory usage. In a nutshell, every time you access a table, MySQL loads a reference to a table as one entry in the table cache. This is done for every concurrent access of a table. So, if you have 10 people accessing your website simultaneously, and each of them is accessing a page that does a join across 3 tables, you’ll need to set your table cache to at least 30. If you don’t, MySQL will refuse to perform queries.

You can keep upping the table cache, but you’ll eventually hit a limit on the number of files your operating system can have open, so keep that in mind.

If you have table_cache set a little bit low, you’ll see the “opened_tables” server variable be high. It’s the number of times mysqld has had to open a table. If this is low, you’re never having any cache misses. If your table_cache is set too low, you’ll have cache misses, and you’ll hit the disk. If table cache is set TOO low, mysql will barf on you, and you don’t want that. In summary, hitting the disk occasionally is probably better than paging a lot, so find a balance, lowering table_cache to the point where you’re not hitting the disk on every query and also not using up memory unnecessarily.

The Query Cache
The Query Cache is essentially a mapping of queries to results. If you do the same query two times in a row, and the result fits in the query cache, mysql doesn’t have to do the query again. If you’re going for performance, this can be a huge benefit, but it can also eat up memory. If you’re not doing a lot of the same query, this probably won’t help you much. Chances are, it will help, and there’s probably some benefit for having a 500-1000 kb of query cache, even on a tight memory budget. There are three variables that influence how the query cache works.

  • query_cache_size – This is the total size of the query cache. This much memory will be used for storing the results of queries. You must allocate at least 40k to this before you get any benefit. There’s a 40k data structure overhead, so if you allocate 41k, it “works,” but you don’t have much space to actually get anything done.
  • query_cache_limit – This is the maximum size of an individual query that is cachable. If you have a 10 megabyte query cache, and a 1 megabyte query cache limit, you can have at least 10 one-megabyte queries cached. This is extremely useful to prevent big queries from busting your cache. Precise benchmarking probably will help you decide what’s best. Use your judgement here.
  • query_cache_type – Here, you can turn the query cache totally on or off. Also, if you want to get really sophisticated, you can turn it on or off — but enable or disable it for specific queries. If you want it to default on, leave it on, and disable the query cache for specific queries with something like, “select sql_no_cache * from table” — Alternatively, if you want it to default OFF, set query_cache_type to “2” or “DEMAND” and write queries that look like “select sql_cache * from table”

Maximum Number of Connections

This may or may not be a problem for you, but it’s one of the most important things for optimizing a mysql installation for high usage. If you’re already limiting the number of apache processes, then you’ll be fine. If you’re not, and you need to handle thousands of users simultaneously, you need to increase this number. It’s the number of connections MySQL allows at once. If it’s not set high enough, you’ll get the dreaded, “too many connections” MySQL error, and your users won’t be happy. You want to keep this number in sync with the max number of users apache allows, and you’ll need to budget extra ram for extra MySQL connections. See above for the rough formula used.
I’ll discuss a few more minor tweaks to MySQL in the next article, where I’ll discuss, among other things:

  • Persistent Connections
  • Other Buffers and Caches
  • Miscellaneous Options


  1. Emergent Properties » Blog Archive » New said,

    June 5, 2006 at 7:46 pm

    […] I just wrote part two of my series on optimizing apache/mysql for low memory usage.  This time, i talked about MySQL settings.  See: Optimizing Apache and MySQL for Low Memory Usage on a Virtual Private Server […]

  2. Bloggitation » links for 2006-06-07 said,

    June 6, 2006 at 6:43 pm

    […] Optimizing Apache and MySQL for Low Memory Usage, Part 2 (tags: database mysql tuning sysadmin) […]

  3. ross mclachlan » Optimising performance said,

    December 12, 2006 at 2:50 am

    […] Optimising MySQL and Apache for low memory usage Part 1 Optimising MySQL and Apache for low memory usage Part 2 […]

  4. 黎建溥 / James Lick » Converting mysql InnoDB tables to MyISAM said,

    April 29, 2007 at 10:44 pm

    […] Now that I have a VPS with limited memory available for my ‘main server’, I’ve been looking at how to optimize usage to make sure I keep well under the memory limit. I found a nice article Optimizing Apache and MySQL for Low Memory Usage, Part 2 which explains some great ways of reducing your mysql memory usage. One of their best suggestions is to disable InnoDB which can save up to 100M of memory. […]

  5. Link With Reality Web Log » links for 2007-11-10 said,

    November 9, 2007 at 10:17 pm

    […] Optimizing Apache and MySQL for Low Memory Usage, Part 2 Tips for optimizing MySQL for low memory. (tags: howto mysql administration) Links :: John :: […]

  6. nuefoo / How to help stop Digg from murdering your precious server. [Web Design • CSS • Web 2.0 • PHP] said,

    January 13, 2008 at 5:47 pm

    […] You can optimise MySQL to save time and RAM, there’s tons of tips here for optimising your installation of mysql, and 10 more at WhenPenguinsAttack for optimising your actual queries. And don’t forget PHP, that’s one of the main things to get hammered on a site, and its also the thing which you can probably optimise till the end of time, check this out and tell me you tell me you do everything on the list 100% of the time…. […]

  7. Optimizando… » ingeniuz :: desarrollo web útil said,

    January 14, 2008 at 10:57 am

    […] Apache y MySQL […]

  8. Django, Python frameworks and VPS hosting said,

    March 23, 2008 at 2:24 am

    […] I’ve also been reading up on keeping mysql memory under control. I need to save some memory for sphinx! My view is that if I can get this all running happily on a 256Mb VPS, with some moderate load testing – then at least I’ll have all the factors under control so that when I need to scale up with more memory – I’ll immediately be making the most of it, in terms of user experience. […]

  9. Apache und MYSQL performanter machen | blog@Fabi said,

    July 27, 2008 at 7:47 am

    […] Optimizing Apache and MySQL for Low Memory Usage, Part 2 […]

  10. Rimu Hosting Update | the duane storey said,

    March 8, 2009 at 1:42 pm

    […] seems to work flawlessly, I’ll start posting some of my configuration files. I’ve found this guy’s site is a pretty great resource for configuring MySQL and Apache for low memory configurations. […]

  11. VPS Hosting with HyperVM and Kloxo said,

    October 3, 2012 at 11:03 am

    […] the memory load. If that’s not enough for you, also have a look at these pages for Apache and MySQL […]

  12. simon said,

    May 29, 2013 at 3:20 am

    >> Do this with “skip-bdb” and “skip-ndbcluster”
    Adding these into the my.cnf in [mysqld ]will break mysql 5.5.

  13. Installing MYSQL on Windows Server 2003 - Low Memory Usage Mode - Just just easy answers said,

    September 6, 2013 at 3:51 am

    […] is a decent article on managing MySQL memory usage. It looks like there is no single setting to set a maximum amount of memory usage, but by adjusting […]

Leave a Comment