Have you ever wondered if the value of shared_buffers is the only setting which controls how much shared memory PostgreSQL is actually using? There was a nice addition committed to PostgreSQL 15 recently, which helps in answering this question (more on that later). shared_buffers for sure is the biggest chunk of shared memory that PostgreSQL is using but there might be other factors which come into the game. You might ask yourself why that is important and one bit you need to keep in mind are huge pages. Those need to be calculated by you and provided by the operating system for PostgreSQL being able to use them. In the default configuration of PostgreSQL you might not even notice that the number of huge pages provided by the OS are not sufficient. PostgreSQL will happily start and you’ll probably never notice that huge pages are not used at all.
Let’s start with the default configuration on Linux: shared_buffers=128MB:
postgres=# show shared_buffers; shared_buffers ---------------- 128MB (1 row)
The default page size on Linux is 4kB:
postgres@debian11pg:/home/postgres/ [pgdev] getconf PAGESIZE 4096
This means all memory is allocated in chunks of 4kB. The default huge page size on Linux is 2MB:
postgres@debian11pg:/home/postgres/ [pgdev] grep Hugepagesize /proc/meminfo Hugepagesize: 2048 kB
The benefit of switching to huge pages should already be clear now: Instead of allocating 32768 ((128*1024)/4) pages of memory it will be only 64 ((128*1024)/2048) with huge pages, so much less effort to manage those lists.
If shared buffers are the only bit to consider we should be able to start PostgreSQL with 64 huge pages provided by the operating system. Let’s try that and install tuned for easier configuration of huge pages:
postgres@debian11pg:/home/postgres/ [pgdev] sudo apt install tuned -y
I am not going into the details of tuned here (check the link above if you want to know more). The currently active profile in my case is this one:
postgres@debian11pg:/home/postgres/ [pgdev] tuned-adm active Current active profile: virtual-guest
The default profiles are located here (you should create your own profiles for your specific needs under /etc/tuned, do not modify the default ones as they might be overwritten with the next system update):
postgres@debian11pg:/home/postgres/ [pgdev] ls /usr/lib/tuned/ accelerator-performance defirqaffinity.py intel-sst network-throughput realtime server-powersave atomic-guest desktop laptop-ac-powersave optimize-serial-console realtime-virtual-guest spectrumscale-ece atomic-host desktop-powersave laptop-battery-powersave oracle realtime-virtual-host spindown-disk balanced enterprise-storage latency-performance postgresql recommend.d throughput-performance cpu-partitioning functions mssql powersave sap-hana virtual-guest default hpc-compute network-latency __pycache__ sap-netweaver virtual-host
Another way to get the available profiles is this:
postgres@debian11pg:/home/postgres/ [pgdev] tuned-adm list Available profiles: - accelerator-performance - Throughput performance based tuning with disabled higher latency STOP states - atomic-guest - Optimize virtual guests based on the Atomic variant - atomic-host - Optimize bare metal systems running the Atomic variant - balanced - General non-specialized tuned profile - cpu-partitioning - Optimize for CPU partitioning - default - Legacy default tuned profile - desktop - Optimize for the desktop use-case - desktop-powersave - Optmize for the desktop use-case with power saving - enterprise-storage - Legacy profile for RHEL6, for RHEL7, please use throughput-performance profile - hpc-compute - Optimize for HPC compute workloads - intel-sst - Configure for Intel Speed Select Base Frequency - laptop-ac-powersave - Optimize for laptop with power savings - laptop-battery-powersave - Optimize laptop profile with more aggressive power saving - latency-performance - Optimize for deterministic performance at the cost of increased power consumption - mssql - Optimize for MS SQL Server - network-latency - Optimize for deterministic performance at the cost of increased power consumption, focused on low latency network performance - network-throughput - Optimize for streaming network throughput, generally only necessary on older CPUs or 40G+ networks - optimize-serial-console - Optimize for serial console use. - oracle - Optimize for Oracle RDBMS - postgresql - Optimize for PostgreSQL server - powersave - Optimize for low power consumption - realtime - Optimize for realtime workloads - realtime-virtual-guest - Optimize for realtime workloads running within a KVM guest - realtime-virtual-host - Optimize for KVM guests running realtime workloads - sap-hana - Optimize for SAP HANA - sap-netweaver - Optimize for SAP NetWeaver - server-powersave - Optimize for server power savings - spectrumscale-ece - Optimized for Spectrum Scale Erasure Code Edition Servers - spindown-disk - Optimize for power saving by spinning-down rotational disks - throughput-performance - Broadly applicable tuning that provides excellent performance across a variety of common server workloads - virtual-guest - Optimize for running inside a virtual guest - virtual-host - Optimize for running KVM guests Current active profile: virtual-guest
As I am currently using the “virtual-guest” profile let’s add the calculated number of huge pages to that profile and then make the system re-apply the settings:
postgres@debian11pg:/home/postgres/ [pgdev] echo "vm.nr_hugepages=64" | sudo tee -a /usr/lib/tuned/virtual-guest/tuned.conf vm.nr_hugepages=64 postgres@debian11pg:/home/postgres/ [pgdev] sudo tuned-adm profile virtual-guest postgres@debian11pg:/home/postgres/ [pgdev] egrep "HugePages_Total|HugePages_Free" /proc/meminfo HugePages_Total: 64 HugePages_Free: 64
This confirms that we now have 64 huge pages available of which all are free currently. Will PostgreSQL use any of those if we restart?
postgres@debian11pg:/home/postgres/ [pgdev] pg_ctl stop postgres@debian11pg:/home/postgres/ [pgdev] pg_ctl start postgres@debian11pg:/home/postgres/ [pgdev] egrep "HugePages_Total|HugePages_Free" /proc/meminfo HugePages_Total: 64 HugePages_Free: 64
No, not at all. Lets try to force it by setting huge_pages to “on”. By doing this PostgreSQL will not start if there are not enough huge pages available (the default is “try”):
postgres@debian11pg:/home/postgres/ [pgdev] psql -c "alter system set huge_pages=on" ALTER SYSTEM 19:12:28 postgres@debian11pg:/home/postgres/ [pgdev] pg_ctl stop 19:12:34 postgres@debian11pg:/home/postgres/ [pgdev] pg_ctl start waiting for server to start....2021-09-08 19:12:36.508 CEST [2542] FATAL: could not map anonymous shared memory: Cannot allocate memory 2021-09-08 19:12:36.508 CEST [2542] HINT: This error usually means that PostgreSQL's request for a shared memory segment exceeded available memory, swap space, or huge pages. To reduce the request size (currently 148897792 bytes), reduce PostgreSQL's shared memory usage, perhaps by reducing shared_buffers or max_connections. 2021-09-08 19:12:36.508 CEST [2542] LOG: database system is shut down stopped waiting pg_ctl: could not start server Examine the log output.
Why is that? If you follow the official documentation for calculating huge pages on Linux, you’ll notice that the procedure is slightly more complicated:
postgres@debian11pg:/home/postgres/ [pgdev] cat /dev/null > $PGDATA/postgresql.auto.conf postgres@debian11pg:/home/postgres/ [pgdev] pg_ctl start postgres@debian11pg:/home/postgres/ [pgdev] pmap 2573 | awk '/rw-s/ && /zero/ {print $2}' 145360K postgres@debian11pg:/home/postgres/ [pgdev] echo "145360/2048" | bc 70
This is 6 more than what we calculated above and confirms that shared buffers is not the only setting to consider. Anyway, lets try with this value:
postgres@debian11pg:/home/postgres/ [pgdev] psql -c "alter system set huge_pages=on" ALTER SYSTEM postgres@debian11pg:/home/postgres/ [pgdev] sudo sed -i 's/64/70/g' /usr/lib/tuned/virtual-guest/tuned.conf postgres@debian11pg:/home/postgres/ [pgdev] sudo tuned-adm profile virtual-guest postgres@debian11pg:/home/postgres/ [pgdev] egrep "HugePages_Total|HugePages_Free" /proc/meminfo HugePages_Total: 70 HugePages_Free: 70
Will this work?
postgres@debian11pg:/home/postgres/ [pgdev] pg_ctl stop postgres@debian11pg:/home/postgres/ [pgdev] pg_ctl start waiting for server to start....2021-09-08 19:23:57.097 CEST [2618] FATAL: could not map anonymous shared memory: Cannot allocate memory 2021-09-08 19:23:57.097 CEST [2618] HINT: This error usually means that PostgreSQL's request for a shared memory segment exceeded available memory, swap space, or huge pages. To reduce the request size (currently 148897792 bytes), reduce PostgreSQL's shared memory usage, perhaps by reducing shared_buffers or max_connections. 2021-09-08 19:23:57.097 CEST [2618] LOG: database system is shut down stopped waiting pg_ctl: could not start server Examine the log output.
No. Add one more and you’ll be fine:
postgres@debian11pg:/home/postgres/ [pgdev] sudo sed -i 's/70/71/g' /usr/lib/tuned/virtual-guest/tuned.conf postgres@debian11pg:/home/postgres/ [pgdev] sudo tuned-adm profile virtual-guest postgres@debian11pg:/home/postgres/ [pgdev] pg_ctl start waiting for server to start....2021-09-08 19:25:11.393 CEST [2633] LOG: starting PostgreSQL 15devel on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit 2021-09-08 19:25:11.395 CEST [2633] LOG: listening on IPv6 address "::1", port 5432 2021-09-08 19:25:11.395 CEST [2633] LOG: listening on IPv4 address "127.0.0.1", port 5432 2021-09-08 19:25:11.413 CEST [2633] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432" 2021-09-08 19:25:11.443 CEST [2636] LOG: database system was shut down at 2021-09-08 19:23:55 CEST 2021-09-08 19:25:11.462 CEST [2633] LOG: database system is ready to accept connections done server started
Why? Because 145360/2048 gives 70.9765625 exactly, so you’ll need 71 instead of 70.
That’s it for the basics, things to remember:
- shared_buffers is not the only configuration you need to take into account for configuring huge pages
- tuned is a nice and easy way to deal with system/kernel configurations, and it is available on Debian as well
- the official way of configuring huge pages for PostgreSQL works, but there is more to know for doing it correctly
In the next post will dig into more details about which configurations change the shared memory requirements for PostgreSQL.