POSTGRESQL USING RAM
(disclaimer: all the data and examples in here are on Linux – the same data can be probably obtained on other systems too, it's just that I work on Linux and don't know other systems well).
This question pops occasionally in various places – PostgreSQL is using too much memory, why is that, and how can it be mitigated?
Before we can go to “optimizing", we should understand the problem. But do we? Both standard tools – ps and top – lie. How/why? Let's see.
I have a very simple PostgreSQL instance on my desktop, configured with 4 GB of shared buffers, and 100MB of work_mem. I did run some work, and now the ps shows:
top shows basically the same numbers.
Which is suspicious, because “free" shows:
i.e – only 1.5GB of ram used (and ~ 10GB used as disk cache – ready to be freed if any app would need more ram).
So how come I see such huge numbers in PS?
First of all – we need to disregard VSZ column. The one that's important is RSS. But its still not really useful:
So, this clocks at ~ 15GB, which is more than I have in this box. So, what is the real memory usage. As in: how much memory I would gain, if I'd kill Pg?
Luckily, we can view what exactly is the RAM being used for, thanks to wonderful world of /proc directory/filesystem/mountpoint/fairy.
Each process on Linux has a directory in /proc. In this directory there is a number of files and directories. Don't be fooled by reported size of the files – they all have “0" bytes, but they do contain information. It's magic.
The one file we're interested in is “smaps".
Content of it looks like this:
For this particular process smaps has over 2000 lines, so I will not show you all of it.
So, anyway – this process – 27713 – according to ps, uses 4285716 kilobytes of RAM. So, what is it so big? Quick grep, and we see:
There is only one “block" that is over 100MB of size, and it's size is very close to total size of process.
Full information about it:
Most of this information is more or less cryptic, but we see couple of things:
- it is shared memory (first line contains rw-s, where “s" is for shared)
- by the look of it (/SYSV… deleted) it looks like the shared memory is done using mmaping deleted file – so the memory will be in “Cached", and not “Used" columns in free output.
- Size of the shared block is 4317224, and 4280924 from it is actually resident in memory
That's ok – that's shared_buffers. But the thing is – shared buffers are used by most of the backends. And, what's worse, not always to the same extent. For example, the same shared buffers data from process 27722:
In here we see that this process requested/used only 388MB of the memory.
So calculating will be complex. For exaple – we might have two processes, each using 400MB of shared_buffers, but it doesn't tell us how much memory it is actually using – because it could be that they are using 100MB of the same buffers, and 300MB of different – so in total the memory usage would be 700MB.
We do know that total size of this shared_buffers block is 4317224. Which is great. But what about other things? Libraries for example – they can be shared by kernel between multiple processes.
Luckily, in 2007 Fengguang Wu sent (and previously wrote) a very cool patch for kernel – which added “Pss" info to smaps.
Basically, Pss is at most Rss, but gets decreased if the same pages of memory are used by more than one process.
That's why Pss above was so much lower than Rss/Size. For example – in the last example. Rss was given 388652, but Pss just 95756, which means that most of the pages this backend used was also used by 3 other backends.
So, now – knowing about Pss, we can finally get real memory usage of a running pg cluster:
If you just said “WTF, WHAT DID HE RUN?!", let me explain. First command:
just returns pids of pgdba user (usually you'll want postgres, but I'm different, and run PostgreSQL as pgdba).
Second – sed – changes the pids to paths to smaps files:
Then I do simple grep for ^Pss lines in the files given from sed. Which returns lots of lines like:
And then the awk summarizes 2nd column (which is the size). And I get the number 4329040 – in kilobytes.
So, theoretically, if I'd stop Pg, I would reclaim that much of RAM. Let's see if it's true:
Used memory dropped from 12145424 to 7781960 – meaning I got back 4363464 kB of RAM. Which is even a bit higher than expected 4329040, but it's close enough. And most of it came off disk cache – as expected, because it was used for shared_buffers.
This is all good, but can this method be used to estimate how much RAM I would reclaim by killing single backend?
No and yes. Shutting down whole Pg meant that the shared memory that it was using – could have been freed. In normal environments, when you kill backend – you end up freeing only the memory that was private to this backend. And this is usually depressingly low number.
For example, on another machine, with much more impressive hardware:
I.e. – the process has 1.7GB of RSS (as visible in ps output), but only 52MB of it is Private memory that would be freed if it got killed.
So no – you can't use the Pss for this, but you can use Private_* data from smaps to get the number.
To summarize it all – PostgreSQL uses much less memory than it looks like at first sight, and while it is possible to get quite accurate numbers – you need to do some shell scripting to get them.
And now I brace myself for comments from the people that will point all technical mistakes in this post, or (what's worse) typos.
Comments
Post a Comment