The short answer to the title of this blog post is: yes, of course you can. The more important question is: should you? The PostgreSQL documentation about tablespaces contains an explicit warning: “Placing a tablespace on a temporary file system like a RAM disk risks the reliability of the entire cluster”. This is for sure true for tablespaces containing persistent data, but what about objects/files that are created in a temporary tablespace like temporary tables or sorts that are going to disk? Does the warning from the documentation apply to these as well? You can check the last blog about temporary tablespaces for getting an idea what actually goes to temporary tablespaces in PostgreSQL.
On Linux a RAM disk can be created quite easily:
postgres@centos8pg:/home/postgres/ [pgdev] sudo mkfs /dev/my_ram_disk 81920 mke2fs 1.44.6 (5-Mar-2019) Creating regular file /dev/my_ram_disk Creating filesystem with 81920 1k blocks and 20480 inodes Filesystem UUID: ecc6d90e-2d59-47f8-a598-7726c309c389 Superblock backups stored on blocks: 8193, 24577, 40961, 57345, 73729 Allocating group tables: done Writing inode tables: done Writing superblocks and filesystem accounting information: done
Of course that needs to be mounted somewhere so that we can put a temporary tablespace on it:
postgres@centos8pg:/home/postgres/ [pgdev] sudo mkdir /my_ram_disk postgres@centos8pg:/home/postgres/ [pgdev] sudo mount /dev/my_ram_disk /my_ram_disk/ postgres@centos8pg:/home/postgres/ [pgdev] df -h | grep my_ram /dev/loop0 78M 1.6M 72M 3% /my_ram_disk postgres@centos8pg:/home/postgres/ [pgdev] sudo chown postgres:postgres /my_ram_disk
I am going to create a new PostgreSQL cluster from scratch:
postgres@centos8pg:/home/postgres/ [pgdev] initdb -D /var/tmp/pg postgres@centos8pg:/home/postgres/ [pgdev] export PGPORT=8888 postgres@centos8pg:/home/postgres/ [pgdev] pg_ctl -D /var/tmp/pg/ start -l /dev/null
Preparing the temporary tablespace and setting it as the default:
postgres@centos8pg:/home/postgres/ [pgdev] psql -X -p 8888 postgres psql (13devel) Type "help" for help. postgres=# \! mkdir /my_ram_disk/tbs/ postgres=# create tablespace my_temp_tbs location '/my_ram_disk/tbs/'; CREATE TABLESPACE postgres=# \db+ List of tablespaces Name | Owner | Location | Access privileges | Options | Size | Description -------------+----------+------------------+-------------------+---------+---------+------------- my_temp_tbs | postgres | /my_ram_disk/tbs | | | 0 bytes | pg_default | postgres | | | | 22 MB | pg_global | postgres | | | | 559 kB | (3 rows) postgres=# alter system set temp_tablespaces = 'my_temp_tbs'; ALTER SYSTEM postgres=# select pg_reload_conf(); pg_reload_conf ---------------- t (1 row) postgres=# show temp_tablespaces ; temp_tablespaces ------------------ my_temp_tbs (1 row)
Creating a new temporary table should now create the underlying file on the RAM disk:
postgres=# create temporary table tmp1 ( a int, b date ); CREATE TABLE postgres=# select pg_relation_filepath('tmp1'); pg_relation_filepath ------------------------------------------------ pg_tblspc/16384/PG_13_202003051/12732/t3_16387 (1 row) postgres-# \! ls -la /var/tmp/pg/pg_tblspc/ total 4 drwx------. 2 postgres postgres 19 Mar 16 21:32 . drwx------. 19 postgres postgres 4096 Mar 16 21:32 .. lrwxrwxrwx. 1 postgres postgres 16 Mar 16 21:32 16384 -> /my_ram_disk/tbs
Everything as expected. What could possibly go wrong with that? Will PostgreSQL start just fine if we put data in that table and then crash the postmaster?
postgres=# insert into tmp1 select * from generate_series(1,10000); INSERT 0 10000 postgres=# \! ps -ef | grep postgres | grep "var/tmp" postgres 25900 1 0 21:31 ? 00:00:00 /u01/app/postgres/product/DEV/db_1/bin/postgres -D /var/tmp/pg postgres 25992 25911 0 21:47 pts/0 00:00:00 sh -c ps -ef | grep postgres | grep "var/tmp" postgres 25995 25992 0 21:47 pts/0 00:00:00 grep var/tmp postgres=# \! kill -9 25900 postgres=# select 1; FATAL: terminating connection due to unexpected postmaster exit server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request. The connection to the server was lost. Attempting reset: Failed. !?> select 1; You are currently not connected to a database. !?>
Can we start normally from here on?
postgres@centos8pg:/home/postgres/ [pgdev] pg_ctl -D /var/tmp/pg/ start pg_ctl: another server might be running; trying to start server anyway waiting for server to start....2020-03-16 21:49:33.034 CET [26010] LOG: starting PostgreSQL 13devel on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.3.1 20190507 (Red Hat 8.3.1-4), 64-bit 2020-03-16 21:49:33.035 CET [26010] LOG: listening on IPv6 address "::1", port 8888 2020-03-16 21:49:33.035 CET [26010] LOG: listening on IPv4 address "127.0.0.1", port 8888 2020-03-16 21:49:33.037 CET [26010] LOG: listening on Unix socket "/tmp/.s.PGSQL.8888" 2020-03-16 21:49:33.041 CET [26011] LOG: database system was interrupted; last known up at 2020-03-16 21:36:18 CET 2020-03-16 21:49:33.263 CET [26011] LOG: database system was not properly shut down; automatic recovery in progress 2020-03-16 21:49:33.264 CET [26011] LOG: redo starts at 0/156D588 2020-03-16 21:49:33.264 CET [26011] LOG: invalid record length at 0/1574240: wanted 24, got 0 2020-03-16 21:49:33.264 CET [26011] LOG: redo done at 0/1574068 2020-03-16 21:49:33.277 CET [26010] LOG: database system is ready to accept connections done server started
All is fine. Of course the temporary table is gone but that would also have been the case if we just ended our session instead of killing the postmaster:
postgres@centos8pg:/home/postgres/ [pgdev] psql -X -p 8888 postgres psql (13devel) Type "help" for help. postgres=# \d tmp1 Did not find any relation named "tmp1". postgres=# \db+ List of tablespaces Name | Owner | Location | Access privileges | Options | Size | Description -------------+----------+------------------+-------------------+---------+------------+------------- my_temp_tbs | postgres | /my_ram_disk/tbs | | | 1024 bytes | pg_default | postgres | | | | 22 MB | pg_global | postgres | | | | 559 kB | (3 rows) postgres=#
Sort operations will just work fine on that tablespace as well:
postgres=# select * from generate_series(1,1000000) order by random(); generate_series ----------------- 943370 301661 ...
… but you need to be careful with the size of the RAM disk:
postgres=# select * from generate_series(1,5000000) order by random(); 2020-03-16 22:04:47.738 CET [26026] ERROR: could not write block 629 of temporary file: No space left on device 2020-03-16 22:04:47.738 CET [26026] STATEMENT: select * from generate_series(1,5000000) order by random(); ERROR: could not write block 629 of temporary file: No space left on device
So actually you can put a temporary tablespace on a RAM disk and I am not aware of serious issues, even if you lose the RAM disk that can easily be fixed:
postgres@centos8pg:/home/postgres/ [pgdev] sudo umount /my_ram_disk postgres@centos8pg:/home/postgres/ [pgdev] pg_ctl -D /var/tmp/pg/ restart waiting for server to shut down....2020-03-16 22:06:23.118 CET [26010] LOG: received fast shutdown request 2020-03-16 22:06:23.124 CET [26010] LOG: aborting any active transactions 2020-03-16 22:06:23.133 CET [26010] LOG: background worker "logical replication launcher" (PID 26017) exited with exit code 1 2020-03-16 22:06:23.133 CET [26012] LOG: shutting down 2020-03-16 22:06:23.148 CET [26010] LOG: database system is shut down done server stopped waiting for server to start....2020-03-16 22:06:23.281 CET [26236] LOG: starting PostgreSQL 13devel on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.3.1 20190507 (Red Hat 8.3.1-4), 64-bit 2020-03-16 22:06:23.282 CET [26236] LOG: listening on IPv6 address "::1", port 8888 2020-03-16 22:06:23.282 CET [26236] LOG: listening on IPv4 address "127.0.0.1", port 8888 2020-03-16 22:06:23.286 CET [26236] LOG: listening on Unix socket "/tmp/.s.PGSQL.8888" 2020-03-16 22:06:23.289 CET [26236] LOG: could not open directory "pg_tblspc/16384/PG_13_202003051": No such file or directory 2020-03-16 22:06:23.293 CET [26237] LOG: database system was shut down at 2020-03-16 22:06:23 CET 2020-03-16 22:06:23.293 CET [26237] LOG: could not open directory "pg_tblspc/16384/PG_13_202003051": No such file or directory 2020-03-16 22:06:23.299 CET [26236] LOG: database system is ready to accept connections done server started
PostreSQL will complain but it will start and once the RAM disk is avaialble again there is business as usual:
postgres@centos8pg:/home/postgres/ [pgdev] sudo mount /dev/my_ram_disk /my_ram_disk/ 22:07:33 postgres@centos8pg:/home/postgres/ [pgdev] psql -X -p 8888 postgres psql (13devel) Type "help" for help. postgres=# create temporary table tmp1 ( a int ); CREATE TABLE postgres=# \! ls -la /my_ram_disk/ total 19 drwxr-xr-x. 4 postgres postgres 1024 Mar 16 21:31 . dr-xr-xr-x. 23 root root 4096 Mar 16 21:28 .. drwx------. 2 root root 12288 Mar 16 21:27 lost+found drwx------. 3 postgres postgres 1024 Mar 16 21:32 tbs postgres=# \! ls -la /my_ram_disk/tbs/ total 5 drwx------. 3 postgres postgres 1024 Mar 16 21:32 . drwxr-xr-x. 4 postgres postgres 1024 Mar 16 21:31 .. drwx------. 4 postgres postgres 1024 Mar 16 22:02 PG_13_202003051 postgres=#
If you know any issues with that or have any comments I would be happy if you can share your thoughts.
Another option would by to use tmpfs:
postgres@centos8pg:/home/postgres/ [pgdev] sudo mkdir /my_ram_disk_2/ postgres@centos8pg:/home/postgres/ [pgdev] sudo mount -t tmpfs -o size=2G tmpfs /my_ram_disk_2/ postgres@centos8pg:/home/postgres/ [pgdev] df -h | grep disk_2 tmpfs 2.0G 0 2.0G 0% /my_ram_disk_2
This can be used for temporary tablespaces as well:
postgres@centos8pg:/home/postgres/ [pgdev] psql -X -p 8888 postgres psql (13devel) Type "help" for help. postgres=# \! sudo chown postgres:postgres /my_ram_disk_2/ postgres=# \! mkdir /my_ram_disk_2/tbs2 postgres=# create tablespace my_temp_tbs2 location '/my_ram_disk_2/tbs2'; CREATE TABLESPACE postgres=# \db+ List of tablespaces Name | Owner | Location | Access privileges | Options | Size | Description --------------+----------+---------------------+-------------------+---------+------------+------------- my_temp_tbs | postgres | /my_ram_disk/tbs | | | 2048 bytes | my_temp_tbs2 | postgres | /my_ram_disk_2/tbs2 | | | 0 bytes | pg_default | postgres | | | | 22 MB | pg_global | postgres | | | | 559 kB | (4 rows) postgres=# alter system set temp_tablespaces = 'my_temp_tbs2'; ALTER SYSTEM postgres=# select pg_reload_conf(); pg_reload_conf ---------------- t (1 row) postgres=# show temp_tablespaces ; temp_tablespaces ------------------ my_temp_tbs2 (1 row)
Same test as above:
postgres=# create temporary table tmp3 ( a int ); CREATE TABLE postgres=# insert into tmp3 select * from generate_series(1,10000); INSERT 0 10000 postgres=# select pg_relation_filepath('tmp3'); pg_relation_filepath ------------------------------------------------ pg_tblspc/24583/PG_13_202003051/12732/t3_24587 (1 row) postgres=# \! ls -la /var/tmp/pg/pg_tblspc/ total 4 drwx------. 2 postgres postgres 32 Mar 16 22:31 . drwx------. 19 postgres postgres 4096 Mar 16 22:32 .. lrwxrwxrwx. 1 postgres postgres 16 Mar 16 21:32 16384 -> /my_ram_disk/tbs lrwxrwxrwx. 1 postgres postgres 19 Mar 16 22:31 24583 -> /my_ram_disk_2/tbs2 postgres=# \! ps -ef | grep postgres | grep "var/tmp" postgres 26236 1 0 22:06 ? 00:00:00 /u01/app/postgres/product/DEV/db_1/bin/postgres -D /var/tmp/pg postgres 26412 26379 0 22:35 pts/0 00:00:00 sh -c ps -ef | grep postgres | grep "var/tmp" postgres 26415 26412 0 22:35 pts/0 00:00:00 grep var/tmp postgres=# \! kill -9 26236 postgres=# select 1; FATAL: terminating connection due to unexpected postmaster exit server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request. The connection to the server was lost. Attempting reset: Failed. !?> select 1; You are currently not connected to a database. !?>
PostgreSQL will start just fine:
postgres@centos8pg:/home/postgres/ [pgdev] pg_ctl -D /var/tmp/pg/ start pg_ctl: another server might be running; trying to start server anyway waiting for server to start....2020-03-16 22:37:44.903 CET [26431] LOG: starting PostgreSQL 13devel on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.3.1 20190507 (Red Hat 8.3.1-4), 64-bit 2020-03-16 22:37:44.903 CET [26431] LOG: listening on IPv6 address "::1", port 8888 2020-03-16 22:37:44.903 CET [26431] LOG: listening on IPv4 address "127.0.0.1", port 8888 2020-03-16 22:37:44.906 CET [26431] LOG: listening on Unix socket "/tmp/.s.PGSQL.8888" 2020-03-16 22:37:44.910 CET [26432] LOG: database system was interrupted; last known up at 2020-03-16 22:31:24 CET 2020-03-16 22:37:45.172 CET [26432] LOG: database system was not properly shut down; automatic recovery in progress 2020-03-16 22:37:45.173 CET [26432] LOG: redo starts at 0/15A1EC8 2020-03-16 22:37:45.174 CET [26432] LOG: invalid record length at 0/15B9E88: wanted 24, got 0 2020-03-16 22:37:45.174 CET [26432] LOG: redo done at 0/15B9CD0 2020-03-16 22:37:45.195 CET [26431] LOG: database system is ready to accept connections done server started
To conclude: I am not sure if I would do this in real life but it seems to work quite well. As long as no persistent objects go into these tablespaces all should be fine. I’ve asked the mailing list for any experience with this, you can follow that here.
Cet article Can I put my temporary tablespaces on a RAM disk with PostgreSQL? est apparu en premier sur Blog dbi services.