Postgres can be slow sometimes. There could be a whole bunch of reasons why: a slow disk system, suboptimal SQL queries or even more subtle things like inefficient schema layout.
But before diving into the realm of fine SQL tuning it is always worth to make one simple check. It costs nothing yet it can save countless hours of chasing non-existing issues in non-relevant areas.
Where is my cache, dude?
Postgres is sensitive to the filesystem cache. To be completely precise, any RDBMS that stores data in the file system (as opposed to storing it in a separate partition) suffers from the file system cache issues.
Please note, this is in no way related to the Postgres shared buffers, which is a totally different thing. What I am talking about is the operating system file cache, an area in the main memory where the operating system stores often read and written files for quicker access. The size of that area is extremely important for the normal Postgres work.
So, here is the very first step towards blazing fast Postgres:
$ cat /proc/meminfo | grep -P '(^Mem|^Cached)'
You will see something like this:
MemTotal: 509884 kB
MemFree: 177016 kB
Cached: 254920 kB
This is an example of a healthy, albeit small system. In fact, it is one of my virtual machines. It has enough free memory for emergencies like vacuums; it has about 250 megabytes of the file system cache, everything is fine.
Now look at this:
$ cat /proc/meminfo | grep -P '(^Mem|^Cached)'
MemTotal: 1747764 kB
MemFree: 14376 kB
Cached: 7944 kB
See the problem? Nearly all the memory is taken, apparently by some processes. Because no memory is left for caching, there is nothing to speed up file access with. How can Postgres possibly be fast in this environment?
By the way, the same information (and a whole lot more) can be obtained with the top utility. It's an interactive program, but it can be run in the batch mode: top -b -n 1
Here is how the top's output looks like:
top - 18:24:50 up 4:56, 1 user, load average: 0.00, 0.00, 0.00
Tasks: 89 total, 1 running, 88 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.0%us, 0.1%sy, 0.1%ni, 99.6%id, 0.2%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 509884k total, 333180k used, 176704k free, 11976k buffers
Swap: 548568k total, 0k used, 548568k free, 255152k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 10308 716 600 S 0.0 0.1 0:01.18 init
2 root 15 -5 0 0 0 S 0.0 0.0 0:00.00 kthreadd
3 root RT -5 0 0 0 S 0.0 0.0 0:00.00 migration/0
.....
The memory usage information is in the top part.
The line starting with "Mem:" contains some essential memory information. This line is self-explanatory:
Mem: 509884k total, 333180k used, 176704k free, 11976k buffers
The "buffers" field shows how much memory is allocated for... well... the system buffers. It is a specialized area in the memory being kept just in case. When something happens, like a program allocates a lot of memory, this area is used first. The system slices an appropriate chunk of memory off the buffers and gives it to the program. To remedy the loss it usually drops a part of the file system cache.
When there is no cache left, the system starts unloading inactive processes to the disk. This process is called swapping and is very hurtful for the system performance. By any means swapping should be avoided.
The line coming after the memory status is about Swap--the area on the disk where swapped processed are loaded out. It looks like this:
Swap: 548568k total, 0k used, 548568k free, 255152k cached
Zero swap usage, all the swap area is free. That's fine, this is how it supposed to be.
Some people suggest to turn swapping off completely by disabling the swap area. It's not a good idea. Swap is a safety measure designed to prevent system crashes in situations where no physical memory is left. It is pretty much like a safety belt: having it on will not prevent a system from accidents, but will let its data survive.
The last number in the line is for the file system cache. Oddly enough, it appears here among swap metrics, which confuses a lot of people, including myself.
Now a few words on how to make the file system cache bigger. Unfortunately there is no magical config parameter for that. The system will make the decision itself based on how much free memory is available. It's a wise idea not to allow processes take more than 50% of the physical memory. Say there is 1G of RAM. Very roughly the postmaster process takes about 30M, ten connections about 10M each, various Postgres services 50M altogether, then a 150M shared memory area for the Postgres shared buffers. Add 150-200M for various operating system services. All this gives about 500M of the process memory while the rest half of the gigabyte is available for caches and buffers. An ideal situation for a database under 500M.
If processes take more than 1/2 of the main memory, the situation can be improved by eliminating unnecessary ones (slocate is the candidate #1), decreasing the Postgres shared buffers and so on. It's a good idea not to have Postgres and Apache running on the same machine for this very reason.
Surprisingly often, this is all you need to make Postgres a happier, faster program.