Making PostgreSQL run faster
I use Rails and Postgresql at work. We have a dataset that's growing fast, large and frequently transformed.
This becomes a pain when there's a need to pull the data from remote and work on it locally. After a while, restoring on the local machine grew to these timings using pg_restore:
real 2m29.814s
user 0m14.783s
sys 0m2.426s
After trying different ways to tweak the settings and postgresql.conf, I discovered the easiest way to speed this up:
--jobs=number-of-jobs # Try number of CPU cores for a start
# Update: Nice one from my boss
--jobs=#{cpu_cores}
def cpu_cores
Integer(`sysctl -n hw.ncpu`) rescue 1
end
Restoring our db was slow in both IO and processing, giving it a number between 2 - 4 allows concurrency between IO and processor intensive tasks (loading data, creating indices). Since I can't do any work while waiting for the restore to complete, might as well let the fans spin =)
real 2m4.378s
user 0m17.010s
sys 0m7.348s
Saving 20 seconds a day for everyone in the team. I'm happy but just a bit more...
More tweaks?
It finally dawned on me homebrew's default postgresql.conf should be conservative. So I did tweaks that mostly involve giving pg a ludicrous amount of resources, so it has less tendency to swap to disk during operations. The net effect should be a postres that loads slightly faster with warmer caches.
Comes with the following assumptions (WARNING):
- Your postgresql installation is just for development
- You are the sole user of the machine
- At least 4gb ram and letting pg hog ram
Fire up postgresql.conf
, usually in /usr/local/var/postgres
for homebrew users.
Add/append the following lines:
shared_buffers = 968MB # Ram "dedicated" to pg. Should be < 1/4 system ram
temp_buffers = 128MB # Only if you use a lot of temp tables
work_mem = 16MB # Non-shared ram per operation. Beware this is non-shared and in binary steps
checkpoint_segments = 64 # Size of logfile segments
WAIT... on nix based system, increasing the shared buffer requires changing SHMMAX
and SHMALL
. For mac/typical linux, run for immediate effect(otherwise pg can't start)
sudo sysctl -w kern.sysv.shmmax=1073741824
sudo sysctl -w kern.sysv.shmall=1073741824
To commit it permanently, add these to lines to /etc/sysctl.conf
kern.sysv.shmmax=1073741824
kern.sysv.shmall=1073741824
Checkpoint Segments
Before postgresql writes, it writes a logfile of the intended operations before committing. If there's a crash etc, this log helps recovery. On a high transaction volume operation (e.g. pg_restore), pg will moan about checkpoint_segments being too close. On a development box, it's okay to set it to a high value (64 * 16mb/segment), so pg doesn't have to slow down to segment the logfiles.
Results?
real 1m1.690s
user 0m17.114s
sys 0m7.386s
If you're trying my tweaks, I hope it speeds up your postgresql daily use / postgresql mass operations.
Shout out if you have any tips for me or if the tweaks wasted your time =)