{"id":17292828,"url":"https://github.com/lesovsky/postgres-commandments","last_synced_at":"2025-06-21T04:35:43.860Z","repository":{"id":69264960,"uuid":"153616569","full_name":"lesovsky/postgres-commandments","owner":"lesovsky","description":"Short axioms about how to use Postgres","archived":false,"fork":false,"pushed_at":"2019-06-08T08:44:12.000Z","size":12,"stargazers_count":52,"open_issues_count":0,"forks_count":6,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-05-27T07:03:19.347Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lesovsky.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-10-18T11:59:14.000Z","updated_at":"2025-03-24T22:32:03.000Z","dependencies_parsed_at":"2023-09-15T12:02:26.123Z","dependency_job_id":null,"html_url":"https://github.com/lesovsky/postgres-commandments","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lesovsky/postgres-commandments","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lesovsky%2Fpostgres-commandments","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lesovsky%2Fpostgres-commandments/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lesovsky%2Fpostgres-commandments/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lesovsky%2Fpostgres-commandments/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lesovsky","download_url":"https://codeload.github.com/lesovsky/postgres-commandments/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lesovsky%2Fpostgres-commandments/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261064214,"owners_count":23104722,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-15T10:44:29.589Z","updated_at":"2025-06-21T04:35:38.846Z","avatar_url":"https://github.com/lesovsky.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# PostgreSQL Commandments\n\n[\u003cimg src=\"https://wiki.postgresql.org/images/a/a4/PostgreSQL_logo.3colors.svg\" align=\"right\"  width=\"100\"\u003e](https://www.postgresql.org/)\n\nShort axioms about how to use Postgres.\n\n\u003cdetails\u003e\n  \u003csummary\u003eDisclaimer\u003c/summary\u003e\n  \n  - These are not recommendations from the official PostgreSQL community.\n  \n  - They are my personal observations based on my own experience as PostgreSQL DBA for the past 10 years.\n  \n  - You can follow or ignore them but the main course of these commandments - prioritise data safety over performance.\n\u003c/details\u003e\n\n#\n\n\u003cdetails\u003e\n  \u003csummary\u003eAvoid long transactions.\u003c/summary\u003e\n\nLong, idle and especially write transactions acquire and hold locks on tuples, preventing their cleanup by vacuum.\n\nLook at the performance of the pgbench benchmark (8 clients read and write to the database during 1 hour):\n\n```\npgbench -c8 -P 60 -T 3600 -U postgres pgbench\nstarting vacuum...end.\nprogress: 60.0 s, 9506.3 tps, lat 0.841 ms stddev 0.390\nprogress: 120.0 s, 5262.1 tps, lat 1.520 ms stddev 0.517\nprogress: 180.0 s, 3801.8 tps, lat 2.104 ms stddev 0.757\nprogress: 240.0 s, 2960.0 tps, lat 2.703 ms stddev 0.830\nprogress: 300.0 s, 2575.8 tps, lat 3.106 ms stddev 0.891\n```\n...in the end\n```\nprogress: 3300.0 s, 759.5 tps, lat 10.533 ms stddev 2.554\nprogress: 3360.0 s, 751.8 tps, lat 10.642 ms stddev 2.604\nprogress: 3420.0 s, 743.6 tps, lat 10.759 ms stddev 2.655\nprogress: 3480.0 s, 739.1 tps, lat 10.824 ms stddev 2.662\nprogress: 3540.0 s, 742.5 tps, lat 10.774 ms stddev 2.579\nprogress: 3600.0 s, 868.2 tps, lat 9.215 ms stddev 2.569\n```\nAs you can see, performance is dropped dramatically over a short period of time.\n\nNow, look at the vacuum logs. \n```\ntuples: 0 removed, 692428 remain, 691693 are dead but not yet removable, oldest xmin: 62109160\ntuples: 0 removed, 984009 remain, 983855 are dead but not yet removable, oldest xmin: 62109160\ntuples: 0 removed, 1176821 remain, 1176821 are dead but not yet removable, oldest xmin: 62109160\ntuples: 0 removed, 1494122 remain, 1494122 are dead but not yet removable, oldest xmin: 62109160\ntuples: 0 removed, 2022284 remain, 2022284 are dead but not yet removable, oldest xmin: 62109160\ntuples: 0 removed, 2756298 remain, 2756153 are dead but not yet removable, oldest xmin: 62109160\ntuples: 0 removed, 3500913 remain, 3500693 are dead but not yet removable, oldest xmin: 62109160\ntuples: 0 removed, 4631448 remain, 4631354 are dead but not yet removable, oldest xmin: 62109160\ntuples: 0 removed, 5377941 remain, 5374941 are dead but not yet removable, oldest xmin: 62109160\n```\nPay attention on the number of dead but not yet removable rows. Their number increases continuously during the benchmark. Also, you can see that oldest the xmin is constant.\n\n---\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eAvoid idle transactions.\u003c/summary\u003e\n\n\"Idle transactions\" is the special case when an application starts transactions with `BEGIN` command and doesn't close them correctly (with `COMMIT`,`ROLLBACK` or `END` command). This might occur due to many different reasons on the application side: absent or wrong error handling inside application code when working with transactions, working with remote data sources like other databases or API from open transactions, etc.\nNegative effects are the same as in case of long write transactions - performance degradation. See the example above. \n\n---\n\u003c/details\u003e\n\nDon't disable autovacuum.\n\nDon't use 'fsync=off' in production.\n\nDon't remove anything from $DATADIR.\n\n\u003cdetails\u003e\n  \u003csummary\u003eDon't use 'kill -9' against Postgres processes.\u003c/summary\u003e\n\nPostgreSQL’s official documentation states:\n\n\u003e It is best not to use SIGKILL to shut down the server. Doing so will prevent the server from releasing shared memory and semaphores, which might then have to be done manually before a new server can be started.\n\nMoreover, using SIGKILL against even a single Postgres backend forces to immediately terminate all other backends, re-initialize internal structures and run recovery from last check point, at which database cluster is not available for clients and applications until recovery ends.\n\nIn the example below, you can see how Postgres handles SIGKILL:\n1) Process with PID 9774 is terminated by SIGKILL.\n2) Postgres terminates the rest of its processes (20 processes in total).\n3) Reinitializes and runs automatic recovery process (which may take a while in various scenarios).\n4) Finishes recovery and starts accepting connections. (edited)\n\n```\n1549 @ from  [] LOG:  server process (PID 9774) was terminated by signal 9: Killed\n1549 @ from  [] DETAIL:  Failed process was running: SELECT abalance FROM pgbench_accounts WHERE aid = 729760;\n1549 @ from  [] LOG:  terminating any other active server processes\n9773 postgres@pgbench from [local] [idle] WARNING:  terminating connection because of crash of another server process\n9773 postgres@pgbench from [local] [idle] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9773 postgres@pgbench from [local] [idle] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n1816 @ from  [] WARNING:  terminating connection because of crash of another server process\n1816 @ from  [] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n1816 @ from  [] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9768 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9768 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9768 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9782 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9782 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9782 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9764 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9764 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9764 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9770 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9770 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9770 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9769 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9769 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9769 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9772 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9772 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9772 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9779 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9779 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9779 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9780 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9780 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9780 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9775 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9775 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9775 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9776 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9776 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9776 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9771 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9771 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9771 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9766 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9766 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9766 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9765 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9765 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9765 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9781 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9781 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9781 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9777 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9777 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9777 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9767 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9767 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9767 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9778 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9778 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9778 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n9783 postgres@pgbench from [local] [SELECT] WARNING:  terminating connection because of crash of another server process\n9783 postgres@pgbench from [local] [SELECT] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.\n9783 postgres@pgbench from [local] [SELECT] HINT:  In a moment you should be able to reconnect to the database and repeat your command.\n1549 @ from  [] LOG:  all server processes terminated; reinitializing\n9817 @ from  [] LOG:  database system was interrupted; last known up at 2018-12-03 15:01:23 +05\n9817 @ from  [] LOG:  database system was not properly shut down; automatic recovery in progress\n9817 @ from  [] LOG:  redo starts at 8/F3C72E70\n9817 @ from  [] LOG:  invalid record length at 8/F3C7A390: wanted 24, got 0\n9817 @ from  [] LOG:  redo done at 8/F3C7A358\n9817 @ from  [] LOG:  last completed transaction was at log time 2018-12-03 21:39:14.667678+05\n9817 @ from  [] LOG:  checkpoint starting: end-of-recovery immediate\n9817 @ from  [] LOG:  checkpoint complete: wrote 7 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.000 s, sync=0.016 s, total=0.042 s; sync files=7, longest=0.008 s, average=0.002 s; distance=29 kB, estimate=29 kB\n1549 @ from  [] LOG:  database system is ready to accept connections\n```\n---\n\u003c/details\u003e\n\n\nDon't delete rows by billions at a time.\n\nAvoid creating unnecessary indexes.\n\nThe  more indexes table has, the slower you can write into it.\n\nDon't use 'listen_addresses = *' on hosts with public access.\n\nUse connection pooler when there are too many connections.\n\nPostgres doesn't 'hang' at shutdown... It has *checkpoint*.\n\nDon't use minor releases that are less than 3, in production.\n\nVACUUM FULL completely blocks access to the table.\n\nUse CONCURRENTLY as much as possible.\n\nThe bigger the table, the slower the count(*).\n\nHot standby != backup.\n\nDon't trust non-validated backup.\n\n\u003cdetails\u003e\n  \u003csummary\u003eUse partitioning for archived data.\u003c/summary\u003e\nLet’s assume we have some logs or events stored in the database\n\n```\n=# select pg_size_pretty(pg_relation_size('history_log'));\n pg_size_pretty \n----------------\n 155 GB\n\n=# select count(*) from history_log;\n   count  \n-----------\n 2102342910\n```\n\nAt some point, we’ll decide to clean old events\n\n```\n=# delete from history_log where updated_at \u003c '2018-11-01';\nDELETE 1885782465\nTime: 725220.719 ms (12:05.221)\n```\n\nThe query would take twelve minutes to complete. However, during this action there is a less obvious process that takes place - query would generate certain amount of WAL that will then need to be transferred into all standbys.\nOk, let’s check how many rows there are in the table now.\n\n```\n=# select count(*) from history_log;\n   count  \n-----------\n 216560445\n\n=# select 100 * 1885782465::bigint / 2102342910;\n ?column? \n----------\n       89\n```\n\nIt seems we deleted something around of 89% of the table. Let’s check its size.\n\n```\nselect pg_size_pretty(pg_relation_size('history_log'));\n pg_size_pretty \n----------------\n 155 GB\n```\n\nHuh, the size hasn’t been changed?!\n\nThe thing is, Postgres never performs real deletion. It just marks rows as removed. Later on, space occupied by these “removed” rows will be cleared by vacuum and available space can again be used for new rows, however, this space still belongs to table (in some rare circumstances, table can be truncated and free space will returned to the file system).\n\nUsing partitioning with historical data allow you 1) to drop old data quickly, 2) without overhead related to WAL generation 3) and free up space immediately.\n\n---\n\u003c/details\u003e\n\nUpstream and master candidate should be the same.\n\nDon't re-invent queues, use PgQ.\n\n\u003cdetails\u003e\n  \u003csummary\u003eKeep logs outside of $DATADIR.\u003c/summary\u003e\n\nKeeping logs in $DATADIR is configured by default in most of installations and has some drawbacks:\n- logs are included into the backups made by pg_basebackup-based backup tools (barman, wal-g, etc.)\n- in case of writing too many logs:\n  - logs might take too much space and postgres might crash due to 'No left space on device' error.\n  - writing logs might add extra load on IO subsystem and performance of user queries might suffer. \n\nBetter configuration is placing logs outside of $DATADIR, for example into `/var/log/postgresql`\n\n---\n\u003c/details\u003e\n#\n\n:elephant: Contributions welcome. Add commandments or ideas through [pull requests](https://github.com/lesovsky/postgres-commandments/pulls) or create an [issue](https://github.com/lesovsky/postgres-commandments/issues) to start a discussion.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flesovsky%2Fpostgres-commandments","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flesovsky%2Fpostgres-commandments","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flesovsky%2Fpostgres-commandments/lists"}