Basic PostgreSQL Administration
Basic PostgreSQL Administration
This course keeps the official topic order and exposes the English handout as readable, native Markdown for review and future editorial work.
Source status: 官方 PDF 已提取为原生 Markdown
Translation baseline: English baseline. The current English archive is the working baseline; the Russian edition is retained for chapter-level cross-checking.
Course scope
Installation, server architecture, basic configuration and day-to-day PostgreSQL administration.
Source status
| Language | Current package | Version | Topics | Course page |
|---|---|---|---|---|
| English | DBA1-handouts-16-en.zip | 16 | 17 | HTTP 200 |
| Русский | DBA1-handouts-16.zip | 16 | 17 | HTTP 200 |
Contents
Supplementary materials
catalogs.pdfdba1_new_features.pdfdba1_student_guide.pdfunix_commands.pdf
Editorial status
The available English handout is extracted from the official PDF. Where no current English archive was found, the route remains ready for a later source edition.
00 Lesson 00: PostgreSQL Administration / Basic Course
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_00_introduction.html Archive: DBA1-handouts-16-en.zip PDF pages: 9
Page 1: PostgreSQL Administration / Basic Course
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Aleksei Beresnev Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is”, and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: For Administrators
{#source-figure-002 caption=“Source slide page 2: For Administrators”}
- DBA2, 4 days
Speaker notes
Prior knowledge of the basics of the SQL language is required to complete all courses. There is no course on the SQL language in our line of courses, but there are many books and other educational resources that can help you master SQL.
For administrators, we offer the following courses.
The DBA1 basic course provides general information about the PostgreSQL architecture, installation process, basic server configuration and management. It also considers basic administration tasks and provides an overview of access control, backup and replication mechanisms.
The DBA2 course focuses on determining appropriate configuration parameters based on an understanding of the internals of the server. It also talks about monitoring the server and using feedback for iterative parameter tweaking.
The DBA3 course is devoted to backup, as well as physical and logical replication configuration and scenarios for its use. It also provides an overview of the methods and difficulties of building highly accessible, scalable clusters.
The QPT course examines in detail the mechanisms for planning and executing queries, discusses the configuration of instance parameters related to performance, and examines the options for finding problematic queries and optimizing them.
The PGPRO course discusses additional features provided by the Postgres Pro Enterprise DBMS.
Page 3: For Backend Developers
{#source-figure-003 caption=“Source slide page 3: For Backend Developers”}
Speaker notes
For backend developers, we offer the following courses.
The DEV1 course is the basic course. It provides general information about the architecture of PostgreSQL and the use of basic database objects; server-side programming in SQL and PL/pgSQL languages using basic data types (including the composite type and arrays), as well as client-side interactions.
The DEV2 course examines the features of the server’s internal structure that affect the development of application code, and comprehensively discusses extensibility: the ability to extend server mechanisms with your own code, which allows you to use PostgreSQL to solve a wide variety of tasks.
The QPT and PGPRO courses are intended for both developers and administrators.
Developers may also be interested in administration courses if there is a need to study the internal structure of PostgreSQL in more detail, as well as in the case when there is no dedicated administrator role on the project.
https://postgrespro.com/community/courses
Page 4: About the DBA1 Course
Duration: 3 days
- Prior knowledge requirements
- Basic knowledge of databases and SQL
- Unix fundamentals What competences will be acquired
- general information about the PostgreSQL architecture
- installation, basic configuration, server management
- data organization at logical and physical levels
- basic administration tasks
- access control information
- understanding backup and replication
Page 5: Materials Used
Pre-configured virtual machine
- Xubuntu 22.04 OS
- PostgreSQL 16
- documentation in English
- Course handouts
- presentations, demonstrations, practice tasks and practice keys
- (in html and pdf)
- reference materials: Unix commands used in the course,
- chart of the main tables of the system catalog with psql commands
Speaker notes
If you are taking the course on your own, start with the Student’s Guide. Among other things, it shows where to download and how to use the virtual machine and other materials. All course materials, including the Student’s Guide, are available at: https://postgrespro.com/community/courses/DBA1
Completing practice tasks is very important to gain the basic skills in working with PostgreSQL. Try to complete the practice tasks yourself first, and then review the provided keys, even if the practice task did not raise questions. Practice keys may contain additional information that is not mentioned in presentations and demonstrations.
Course handouts (presentations, demonstrations, practice tasks and keys) are available in two formats. Html is good for browsing and copying code snippets. Pdf is split in pages and is more convenient for printing.
Additional reference materials will help you quickly find the information you need.
Page 6: Course Outline and Schedule
Each training day is approximately 6 hours + lunch
- Each lesson usually consists of
- presentation and demonstration: ~20-60 minutes
- practice: ~20-30 minutes, including a break
Page 7: Day 1
Basic Tools
- Server Installation and Management
- Using psql
- Server Configuration
- Architecture
- PostgreSQL Fundamentals
- Isolation and MVCC
- Vacuuming
Page 8: Day 2
Architecture (Continued)
- Buffer Сache and WAL
- Data Organization
- Databases and Schemas
- System Catalog
- Tablespaces
- Low Level
- Administration
- Monitoring
Page 9: Day 3
Access Control
- Overview
- Backup
- Overview
- Replication
- Overview of Physical Replication
- Overview of Logical Replication
01 Lesson 01: Basic Tools / Server Installation and Management
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_01_tools_install.html Archive: DBA1-handouts-16-en.zip PDF pages: 23
Page 1: Basic Tools / Server Installation and Management
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Aleksei Beresnev Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is”, and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- Basic Сoncepts
- Installation from Source Code
- Server Management
- Package Installation
- Server Management in Ubuntu
- Cloud Solutions
Page 3: DB Cluster and Server
{#source-figure-003 caption=“Source slide page 3: DB Cluster and Server”}
Speaker notes
Before talking about installation, let’s look at some basic concepts.
PostgreSQL is a program that belongs to the class of database management systems.
When this program is running, we call it a PostgreSQL server or a server instance. So far, the server seems to be a “black box” for us, but gradually we will get acquainted with how it works.
The data managed by PostgreSQL is stored in databases. One instance of PostgreSQL simultaneously manages several databases. This set of databases is called a database cluster. We will talk more about databases in the lesson Data Organization. Databases and Schemas.
So, a database cluster is data in files. A server or a server instance is a program that manages a database cluster.
Page 4: Source Code
Installation from source code
- stable server version
- you can use non-standard parameters
- or build the server for a non-standard architecture
- Installation from git
- current server version
- used primarily by kernel developers; requires a broader set of tools
Speaker notes
You should at least have an idea of how to install the program from source code, so that you could build PostgreSQL with non-standard parameters or on a non-standard architecture, if necessary.
You can find the source code in 2 formats: gzip and bzip2 at https://www.postgresql.org/ftp/source, as well as in the VM in the student user’s home directory.
You can also get the source code straight from the project’s git repository: git://git.postgresql.org/git/postgresql.git (there are other mirrors, including github).
This way you can build not just the stable version, but also a version at any specific commit, including the most recent one.
When installing from the git repository, a wider set of tools is required. For example, the lexer and parser are made using Flex and Bison, and git stores the source codes for these tools. They generate C files when built, which are then compiled. The source code archives include the pre-built C files already.
Page 5: Required Software
Hard requirements
- tar, gzip/bzip2, GNU make, C compiler (C89)
- Used but can be disabled
- GNU Readline, zlib, lz4, and zstd libraries
- Extras
- Perl, Python, and Tcl programming languages
- for using PL/Perl, PL/Python, PL/Tcl
- Kerberos, OpenSSL, OpenLDAP, PAM for authentication and encryption
- ICU library for cross-platform UNICODE support
- Separate tools when building from the git repository
Speaker notes
The configuration and installation process is traditionally based on GNU Make and Autoconf:
https://postgrespro.com/docs/postgresql/16/install-make
This course covers the traditional build method.
Starting with version 16, PostgreSQL also supports the Meson build system as an alternative.
https://postgrespro.com/docs/postgresql/16/install-meson
A number of programs and utilities are required when building from source.
The readline library allows you to edit the command line, use the command history and auto-completion. In a server installation, it may not be necessary if you are never going to run psql via the console.
The libraries zlib, lz4, and zstd are used for compression.
Page 6: Unpacking the Archive
The source code package is located in the student user’s home directory. Let’s unpack and open it:
Configuration
We’ll set up the configuration process in the directory containing the unpacked PostgreSQL source code using the configure script.
Important Note! If rebuilding (e.g., with different configuration parameters), you must first remove files from previous configuration to avoid conflicts:
The configure command accepts a number of parameters. Example: –prefix — installation directory, default/usr/local/pgsql –enable-debug — to enable debug information See the documentation for the full list of parameters. The command also makes use of environment variables. For example, CC and CFLAGS configure the C compiler. By default, the configure script prepares compilation and installation, placing all executable files, libraries, and other PostgreSQL files in the directory: /usr/local/pgsql. Typically, the installation directory is accessible for writing only to the OS superuser. For demonstration purposes, we use the –prefix option to specify the server software installation directory in the pgsql16 subdirectory of the student user’s home directory. We are also using port 5555 instead of the default 5432:
The output of this command is extremely verbose, so we are skipping it entirely.
PostgreSQL Compilation and Installation
Options: make — build only the server make world — build the server, all extensions and documentation Let’s compile the server. Depending on your computer performance, this may take minutes or even tens of minutes.
Now, install the server.
To compile the extensions, repeat the make and make install commands from within the contrib directory.
Page 7: Page 7
If you build the server together with the extensions and docs (make world), you can install everything with just the make install-world command.
Page 8: Creating a Сluster
{#source-figure-008 caption=“Source slide page 8: Creating a Сluster”}
Tool
- initdb
- Features
- a new database cluster cannot belong to the OS superuser
- PGDATA is a convenient variable that represents the cluster directory
- configuration files are created in the PGDATA directory
- enabling checksum calculation for data pages is a good idea
Speaker notes
After installing the server, you need to create a database cluster. The initdb tool does that for you.
For security reasons, the directory in which the cluster is initialized cannot belong to the OS superuser. Usually, the cluster ownership is assigned to the postgres user.
The cluster owner can define the PGDATA environment variable pointing to the cluster directory. This variable is used by some server tools when they need to find out the location of the cluster. Such tools include initdb, as well as pg_ctl, the main server management tool, which we will discuss in a bit.
During cluster initialization, initdb creates configuration files in the PGDATA directory. We will talk more about configuration files in another lesson.
Initdb has many keys that affect its operation. One important key is -k or –data-checksums. It enables or disables calculating checksums for data pages. The checksum check is performed when accessing any data page in the cluster. This slightly reduces performance, but allows you to quickly detect data corruption.
For more details, see https://postgrespro.com/docs/postgresql/16/app-initdb
Page 9: Creating a Cluster
Now, let’s create a data directory. In production, it’s a good practice to have a separate OS user as the owner of the data directory. Here, we will just use the student user and the /home/student/pgsql16 directory.
This directory is often referred to as PGDATA. PGDATA is the environment variable pointing to it, and it’s a convenient shortcut.
All server tools and utilities are located in the bin directory. We recommend you to add it into the PATH variable:
initdb is a tool used to initialize a database cluster. In the -U option, you can specify the name of the DBMS superuser. If you do not specify it, then the OS username is used, in our case student. Usually this user is postgres, so let’s set this name explicitly. The -k option includes page checksum calculation, which allows timely detection of data corruption. If PGDATA is not set, specify a directory for the data in the -D option. In our case, we do not actually need to specify this option.
Page 10: Server Management
Tool
- pg_ctl
- Main operations
- starting, stopping and checking the status of the server
- reloading configuration parameters
- switching to a standby
Speaker notes
The main server management operations include starting and stopping the server, getting the current status of the server, reloading the configuration, and some others.
pg_ctl, a stock PostgreSQL tool, is designed to perform these actions.
pg_ctl should be run as the owner of the database cluster.
For more details about server management for database administrators, see:
https://postgrespro.com/docs/postgresql/16/app-pg-ctl
https://postgrespro.com/docs/postgresql/16/runtime
Page 11: Server Management
We can start the server now. In the -l option, specify the server message log file. The -D option is omitted because the PGDATA variable is set.
Let’s verify that the server is running: use the psql tool to connect to the server and return current time:
The following command stops the server:
Page 12: Package Installation
{#source-figure-012 caption=“Source slide page 12: Package Installation”}
Package installation is usually preferable
- Linux ( , Debian, Red Hat, SUSE and others)
- included in the OS distribution
- repository (yum, apt) or RPM/DEB packages
- FreeBSD, OpenBSD
- packages from the Ports and Packages Collection
- Mac OS X
- Windows
Speaker notes
The preferred option is to use ready-made packages, since in this case a clear, supported and easily updated installation is obtained.
Packages exist for most popular systems. Each of them may have its own features that you should get to know before installing.
Package repositories of some systems already include PostgreSQL, but usually not the latest version. The latest version is always available in the PostgreSQL Global Development Group (PGDG) repository:
https://www.postgresql.org/download/
We will be working with the Ubuntu PostgreSQL package.
Page 13: Creating a Cluster
{#source-figure-013 caption=“Source slide page 13: Creating a Cluster”}
Tool
- pg_createcluster
- initdb
- Features
- initialization is performed when installing the package, creates
- a cluster named “main”
- checksum calculation is not enabled by default
- defines the location of server configuration and log files
- sets up automatic server startup at OS startup
Speaker notes
pg_createcluster is a wrapper for the initdb tool designed to initialize a cluster in Ubuntu.
The help for the pg_createcluster command can be obtained using man: $ man pg_createcluster
pg_createcluster is executed automatically when the package is installed and creates a database cluster named “main”.
Note that cluster initialization is done with checksum calculation for data pages disabled.
Executables, configuration files and the server log are placed in accordance with the Ubuntu practices.
In addition, the installation sets up the PostgreSQL server to start and stop when Ubuntu starts and stops.
To delete a cluster, the pg_dropcluster tool is used.
Both pg_createcluster and pg_dropcluster are specific to Ubuntu. In other systems, you need to explicitly initialize a cluster using initdb and use the appropriate operating system tools.
Page 14: Creating a Cluster in Ubuntu
In the provided VM, PostgreSQL is installed from the distribution package:
PostgreSQL directory:
The owner of the server software is the root user. pg_config shows the parameters the server was compiled with:
A database cluster called main is already initialized and is located in /var/lib/postgresql/16/main. The owner of the directory is postgres user. Here’s what the directory contains:
Page 15: Page 15
Page 16: Server Management
Tool
- pg_ctlcluster
- pg_ctl
- Main operations
- starting, stopping and checking the status of the server
- reloading configuration parameters
- switching to a standby
Speaker notes
In the Ubuntu PostgreSQL package, pg_ctl is not run directly, but through a special wrapper pg_ctlcluster. The help for pg_ctlcluster can be obtained using man: $ man pg_ctlcluster
In other systems, pg_ctl can be used directly.
Page 17: Server Management in Ubuntu
With the package installation, PostgreSQL is automatically added to the OS startup list. So, there is no need to start it manually. You can explicitly control the server using the pg_ctlcluster tool, which must be run either as the OS postgres user or via sudo. The pg_ctlcluster tool can manage multiple servers of different versions. It has the following parameters: 16 — server version number main — server name Command Stop:
Start:
Restart:
Get server status:
Reload configuration:
For a package installation, the server log file is located at:
Let’s look at the last messages:
Page 18: Cloud Solutions
PostgreSQL in a virtual environment
- virtualization overhead
- the administrator has full access to the instance
- PostgreSQL as a Service
- offered by many cloud providers
- the provider takes over the administration routine
- limited management, backup and monitoring tools
Speaker notes
Virtualization solutions can be used to run databases, including PostgreSQL. The trade off for the convenience of this approach is considerable overhead costs. For high-load systems, any additional intermediate layers between the DBMS and the hardware are undesirable.
In addition, many leading cloud providers offer PostgreSQL as a service (Database as a Service, managed database).
In this case, the provider takes over most of the administration tasks. This limits your control over the instance. Moreover, management capabilities for tasks such as performance monitoring or backup and recovery are limited by the tools offered by the service provider.
We will not consider the features of individual cloud solutions within the course. You can study the documentation offered by different service providers to learn more.
Page 19: Takeaways
Two installation options — package or source code
- Cloud solutions available
- The server is run by a dedicated OS user
- Initialize a database cluster before using the server
- Specific commands for server management
Page 20: Practice
Enabling checksum calculation for a cluster.
- Stop the server.
- Check whether checksums are calculated for the cluster.
- Enable checksum calculation.
- Start the server.
Speaker notes
2, 3. Use the pg_checksums tool. To run it, enter the full path: /usr/lib/postgresql/16/bin/pg_checksums
https://postgrespro.com/docs/postgresql/16/app-pgchecksums
Page 21: 1. Stopping the Server
2. Verification
To see if page checksum calculation is enabled, run the pg_checksums utility with the –check option:
3. Enabling Checksum Calculation
Run pg_checksums with the –enable option:
Checksum calculation is now enabled.
4. Starting the Server
Page 22: Practice+
- Install PostgreSQL from source code as shown in the demo.
- Create a database cluster, start the server.
- Make sure the server is running.
- Stop the server.
Page 23: 1. Compiling PostgreSQL from Source Code
All necessary commands were shown during the demo, but were not executed. Repeat the process by yourself within the provided virtual machine.
Note Note that compiling of the source code may take a while (several to tens of minutes, depending on your computer resources).
02 Lesson 02: Basic Tools / Using psql
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_02_tools_psql.html Archive: DBA1-handouts-16-en.zip PDF pages: 17
Page 1: Basic Tools / Using psql
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Aleksei Beresnev Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss ofincome, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is,” and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- Launching psql and Connecting to the Database
- Getting Help
- Working with psql
- Configuring psql
Page 3: Purpose of psql
Terminal client for working with PostgreSQL
- Comes with the DBMS
- Used by administrators and developers for interactive work and script execution
Speaker notes
There are other third-party tools available, but they are not considered in the scope of the course.
The psql terminal client will be used throughout the course. Those who are used to working with GUI tools may find it uncomfortable at first. Nevertheless, it is very powerful if you get used to it.
This is the only client supplied with the DBMS. The knowledge of psql will be useful to both developers and DB administrators, regardless of which tool they choose to work with at the end of the day.
https://postgrespro.com/docs/postgresql/16/app-psql
Page 4: Connection
Launch
- New connection in psql
- Information about the current connection
Speaker notes
The required connection parameters include: database name, user name, server name, port number. If these parameters are not specified, psql will try to connect using the default values:
● database — matches the user name;
● user — matches the OS user name;
● node — connection via Unix socket;
● port — usually 5432.
To make a new connection without leaving psql, run the \connect command. It uses the current connection’s parameters as defaults.
The \conninfo command provides information about the current connection.
Additional information about connection configuration options:
https://postgrespro.com/docs/postgresql/16/libpq-envars
https://postgrespro.com/docs/postgresql/16/libpq-pgservice
https://postgrespro.com/docs/postgresql/16/libpq-pgpass
Page 5: Getting Help
In the OS command line
- In psql
- => \?
- list of psql commands
- => \? variables
- psql variables
- => \h[elp]
- list of SQL commands
- => \h command
- syntax of the SQL command
- => \q
- quit
Speaker notes
Reference information on psql can be obtained not only from the documentation, but also from within the system directly.
psql with the –help option displays a startup help message. If the documentation package is installed with the system, you can view the manual for psql using the man psql command.
psql can execute SQL commands as well as its own commands. All psql commands start with a backslash and, as a rule, can be abbreviated to their first letter.
Inside psql, you can get a list and a brief description of all psql commands: \?.
The \help command provides a list of SQL commands that the server supports, as well as the syntax of an SQL command (if specified).
Another command that is useful to know, although it has nothing to do with the help, is \q — exit psql. Alternatively, you can use the exit and quit commands to quit.
Page 6: Executing SQL Commands and Formatting the Output
Run psql:
Check the connection:
Using the default parameters, we have connected to the student database as the student user. You will learn more about databases and users in later modules. The \c[onnect] command creates a new connection without leaving psql. SQL commands, unlike psql ones, may span multiple rows. To send an SQL command for execution, end it with a semicolon:
psql can give output in different formats. Here are some of them: Aligned format Unaligned format Extended format The aligned format is the default. It sets each column’s width based on its contents. There is also the header and the total row. psql commands to switch display modes: \a — switches aligned format and unaligned format mode \t — switches the header and footer display Let’s switch to non-aligned, turn the header and the total row off, and use a whitespace as the separator:
The extended format is convenient for displaying multiple columns for one or several records:
The extended mode can be set for a single query. To do that, add \gx at the end instead of a semicolon:
Page 7: Page 7
You can see all the formatting options by using the \pset command. If used with no parameters, it will display current settings:
Interacting with the OS
psql can run shell commands:
You can set OS environment variables:
It can write output into a file with the \o[ut] command:
There is nothing on the screen! Let’s check the file:
Let’s get the output back to the screen:
Executing Scripts
Another way to run a query is with the \g command. You can specify parameters for this particular query in the brackets. Query output can be redirected to OS command by appending the command after a vertical bar. For example, display query output with line numbers:
The \g command can specify a filename to redirect output:
Page 8: Page 8
This is what we get:
We can execute this file as a script using \i[nclude]:
Other ways to run commands from a file: psql < filename psql -f filename You can skip creating a file in the last example if you end the query with \gexec:
gexec considers the contents of each column of each row an SQL-operator, and tries to execute them one by one.
psql Variables and Control Structures
Not unlike shell, psql has its own variables, including some pre-defined ones (with special meaning for psql). Store the value of the OS USER environment variable in a psql variable:
Set a variable Test:
To substitute a variable’s value, use a colon prefix before the variable name:
To reset a variable, use:
Query results can be saved to variables. For this purpose use the \gset command termination:
The query must return only one record. If used with no parameters, \set displays all currently set variables and their values:
Page 9: Page 9
In scripts, you can use conditional operators. For example, let’s check if working_dir has a value, and if it does not, set it as the current directory. The following command checks if the value is set and returns a Boolean value:
This conditional psql operator checks if the variable exists and sets the default value if needed:
Now we can be sure that the working_dir variable is defined:
System Catalog Commands
There is a set of commands (mostly starting with \d) used to quickly and conveniently get information about database objects. Example:
We will see more of these commands later.
Configuring psql
On startup, psql runs two scripts (if they exist): First a common system psqlrc script Then a custom .psqlrc file
Page 10: Page 10
The user configuration file must be in the home directory. The system script’s location can be discovered with the following command:
Neither file exists by default. You can use the files to configure your session parameters, for example: psql prompt Program for page-by-page viewing of query results Variables for storing the text of frequently used commands For example, let’s store a query that returns 5 largest tables in a variable top5:
Now we can execute the query by just typing:
If you write the \set command into the ~/.psqlrc file, the top5 variable will be available immediately after psql startup. Thanks to readline support, psql can autocomplete keywords and object names, and it also stores the command history. The name and the size of the history file are set by HISTFILE and HISTSIZE variables.
Page 11: Takeaways
psql is a terminal client for working with PostgreSQL
- Connection parameters are required at startup
- Executes SQL and psql commands
- Includes tools for interactive work, as well as for preparing and executing scripts
Page 12: Practice
- Run psql and check the current connection information. 2. Display a detailed list of databases. 3. By default, psql uses less command for page-by-page output. Replace it with less -XS and display the detailed database list again. 4. The default prompt shows only the name of the database. Configure the prompt to display in the following format: user@database=#. 5. Configure psql to display the execution time for all commands. Make sure that this setting is saved when you restart.
Speaker notes
- When starting psql, if you omit the connection parameters, the default values will apply.
The pager program is configured using the PSQL_PAGER environment variable. The setting can be configured in the .psqlrc file using the \setenv command. This will set the value to ’less -XS’ specifically for psql sessions, while maintaining the OS default settings in all other cases. By default, less wraps long lines during viewing and clears its output upon exit. The -XS parameter disables this default behavior.
Prompt customization is described in the documentation:
https://postgrespro.com/docs/postgresql/16/app-psql#APP-PSQL-PROMPTI NG
5.The psql command to output the duration of a query execution can be found in the PostgreSQL documentation or within psql itself with the \? command.
Page 13: 1. Running psql and Displaying Connection Information
2. Paging Results Using less
When query output exceeds terminal dimensions, psql sends it to the less pager. You can navigate through query results using standard navigation keys. The h command displays the help file. The q command exits the display mode.
Note Note that by default less will wrap long lines, which can make the results difficult to read. Besides, the output will be cleared after quitting less. For example, \l+ output becomes unreadable due to wrapping.
3. Configuring Page View in .psqlrc
When using the less pager with -XS flags, long lines will not wrap and output remains visible after exiting the less pager. For such configuration, simply set the PSQL_PAGER environment variable using the \setenv command. We will save this setting in the ~/.psqlrc script:
4. Customizing Prompts
To include role information in your prompts, prepend %n@ to both PROMPT1 and PROMPT2 variables.
The PROMPT1 variable controls the primary prompt displayed for the first line of a user’s query input. For multi-line queries, PROMPT2 controls the prompt display from the second line onward. While both variables share identical default values, you can configure distinct prompts for initial and following lines. Note that PROMPT3 serves only for COPY command operations.
5. Output of SQL Execution Timing
The complete contents of your .psqlrc file will be like this:
Page 14: Page 14
For the changes to take effect, restart your psql session.
After restarting, verify the new configuration: Prompt (must include role name) Displaying detailed information about databases Output of command execution time
Page 15: Practice+
- Open a transaction and execute a command that ends with any error. Make sure that no other commands can be executed inside this transaction.
- Set the ON_ERROR_ROLLBACK parameter to on and make sure that after the error, you can continue executing commands inside the transaction.
Speaker notes
- To open a transaction, run the command
- Setting the ON_ERROR_ROLLBACK parameter to ON causes psql to create a SAVEPOINT before each SQL command inside an open transaction and, in case of an error, roll back to this savepoint.
https://postgrespro.com/docs/postgresql/16/sql-savepoint
Page 16: 1. psql and In-Transaction Errors
The psql tool autocommits transactions by default, so each SQL command is executed within a separate transaction. So each SQL command is executed within a separate transaction. To start a transaction explicitly, the BEGIN command is used:
Note Note that the psql prompt has changed. The asterisk character shows that the transaction is currently open.
Consider that we have made a typo in the following command:
The asterisk will change to an exclamation mark, indicating an error. Now, rewrite the command:
But PostgreSQL cannot roll back just a single command, so it terminates and rolls back the whole transaction. To continue, we must send a command that says that the transaction is complete. It can be either COMMIT or ROLLBACK, since the transaction is already cancelled.
Creating the table was cancelled, so there is no such table in the database:
The ON\_ERROR\_ROLLBACK Variable
We can change how psql behaves here.
Now, before every transaction command, there will be a savepoint created. In case of an error, it will roll back to the last savepoint. This way, transaction commands can continue executing.
Page 17: Page 17
The ON_ERROR_ROLLBACK variable can be set to interactive. This will make such behavior work only in the interactive mode, but not when executing scripts.
03 Lesson 03: Basic Tools / Server Configuration
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_03_tools_configuration.html Archive: DBA1-handouts-16-en.zip PDF pages: 22
Page 1: Basic Tools / Server Configuration
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Aleksei Beresnev Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is,” and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- Configuration Parameters
- Configuration Files
- Parameter Management at the Instance and Session Levels
Page 3: Configuration Parameters
Purpose
- managing DBMS operation and behavior
- Setting parameters
- for an instance via configuration files
- for a separate database or user
- for the current session
Speaker notes
There are multiple parameters in PostgreSQL that control the DBMS behavior. These parameters affect resource management, server processes, and much more.
For example, the max_connections parameter limits the number of concurrent connections to the server.
The full list of configuration parameters and their descriptions is available in the documentation: https://postgrespro.com/docs/postgresql/16/runtime-config
In this topic, we will not cover any specific configuration parameters, but rather discuss how to set their values.
Configuration parameters are generally managed in configuration files. The parameter values set in the configuration files affect the whole DBMS instance, unless explicitly configured otherwise.
Some parameters can be set for a specific database or for a specific user’s sessions. Such settings will overrule those declared in configuration files. These types of settings will be discussed in further lessons of the course.
Lastly, many parameters can be changed at the session level, during client operaton.
Page 4: postgresql.conf
Main configuration file
- loaded when the server starts
- option to load additional configuration files
- located in the data directory (PGDATA) by default
- After any changes to the parameters, the file has to be reloaded
- => SELECT pg_reload_conf();
- changes to some parameters require a server restart to apply
Speaker notes
The main configuration file is postgresql.conf.
The default file’s location is defined during initial PostgreSQL compilation. The server executable accepts -c config_file as a command line argument to set the configuration file path.
By default, the file is located in the data catalog (PGDATA), but package distributions usually place it somewhere else, depending on the OS-specific conventions.
This is a well-documented plaintext file which stores parameters in a key-value format.
Additional configuration files can be included. By default, PostgreSQL on Ubuntu automatically includes all .conf files from /etc/postgresql/16/main/conf.d.
If the same parameter is defined in the configuration files multiple times, only the most recently read value will be used.
For any changes to parameters to apply, the file must be reloaded. Some parameters require a server restart to apply.
Page 5: The postgresql.conf File and the pg_file_settings View
The configuration file name is a value of the read-only config_file parameter. The configuration file can be specified using a command-line switch when starting postgres.
Take a look at a part of a config file.
The main configuration file postgresql.conf can include additional configuration files. Directives: include_dir is a directory with additional configuration files. include includes an additional configuration file. include_if_exists Includes an additional configuration file, if one exists. These directives are typically placed at the end of postgresql.conf:
To see current settings in configuration files, query the pg_file_settings view:
Page 6: Page 6
The view displays non-comment lines of configuration files. The applied column indicates whether the specified value will be applied upon reloading the configuration. Specifically, the column will show false if: The change requires a restart of the server. There is a line with the same parameter that will be read later. There is an error in one of the lines where the parameter is specified. The view also displays the configuration file name and line number, making it easy to locate errors.
The pg\_settings View
For example, let’s take the work_mem parameter. It defines how much memory is allocated for operations like sorting or hash join. The default value can be insufficient for some queries. To learn more about the work_mem parameter, check out our Query Performance Tuning course (QPT). Active configuration parameter values are shown in the pg_settings view. For example, here’s its output regarding the work_mem parameter:
Page 7: Page 7
{#source-figure-007 caption=“Source slide page 7: Page 7”}
Below are the main columns of the pg_settings view: name, unit — parameter name and unit setting — current value boot_val — default value reset_val — initial value for sessions source — source of the current parameter value sourcefile, sourceline — configuration file and line number, if the current value was specified in the file pending_restart — true if the value is changed in the configuration file, but server restart is required to be applied The context column shows what action has to be taken for the parameter change to take effect. Possible values include: internal — cannot be changed, the value is set during installation postmaster — server restart required sighup — requires a reload of the configuration files superuser — superuser can change for their session user — any user can change for their session
Order of Applying Settings
When reloading the configuration, the main file is read first, followed by additional files. If the same parameter is defined multiple times, the latest value is applied. For example, let’s specify the work_mem parameter twice in an additional configuration file:
Contents of the /etc/postgresql/16/main/conf.d/work_mem.conf file:
The applied = f for the first line indicates that it will not be applied. The context field of the work_mem parameter says “user”. This means that the parameter can be changed by the user during their session. We will discuss how to do it in a bit. To change the value for all sessions, you just need to reload the configuration file:
Verify that work_mem now has taken the value from the second line of the configuration file:
Page 8: Page 8
Page 9: postgresql.auto.conf
Configuration file managed by SQL commands
- ALTER SYSTEM
- adds or changes a line
- SET parameter TO value;
- ALTER SYSTEM RESET parameter;
- deletes a line
- ALTER SYSTEM RESET ALL;
- deletes all lines
- Location
- always in the data directory (PGDATA)
- Actions needed to apply
Speaker notes
The file postgresql.auto.conf is always loaded last. This file is always located in the data catalog (PGDATA).
It should never be modified manually, but only with the ALTER SYSTEM command. ALTER SYSTEM is an SQL interface for managing configuration parameters.
For any changes made with ALTER SYSTEM to take place, the server must reload the configuration files, as it does with postgresql.conf.
The contents of both files (postgresql.conf and postgresql.auto.conf) can be checked using the pg_file_settings view, and the current parameter values are shown in the pg_settings view.
More about the ALTER SYSTEM command:
https://postgrespro.com/docs/postgresql/16/sql-altersystem
Page 10: The ALTER SYSTEM Command and the postgresql.auto.conf File
Let’s set a new value for the work_mem parameter:
What happened? ALTER SYSTEM checks if the provided value is valid.
Now it works! The command saves the new value 16MB to a file named postgresql.auto.conf:
However, the value has not been applied yet:
For the new work_mem setting to apply, reload the configuration file first:
Lines are removed from postgresql.auto.conf with the ALTER SYSTEM RESET command:
Page 11: Page 11
Reload the configuration file again. The old value from work_mem.conf is now applied:
Page 12: Current Session
{#source-figure-012 caption=“Source slide page 12: Current Session”}
set until the end of the session or transaction
- setting parameters is transactional
- custom parameters are allowed
Speaker notes
Parameter values can be changed directly during the session with the SET command or the set_config function. The SHOW command or the current_setting function display the current parameter value.
By setting a new value, you can specify its expiration date: until the end of the session (by default) or until the end of the transaction (SET LOCAL).
In any case, setting parameters is transactional: if the current transaction is rolled back, any parameters modified within it will return to the state they were in at the start of the transaction.
In addition to the PostgreSQL system parameters, the same commands and functions can be used to create and get the values of custom parameters.
Page 13: Setting Parameters for the Current Session
The SET command is used to set parameter values for the duration of the current session:
The set_config function also works:
The third parameter of the function defines if the parameter should be used for the duration of the current transaction (true) or until the end of the current session (false). This may be important when using a connection pool, when transactions by multiple users may be performed within the same session.
Checking Parameter Values During a Session
There are multiple ways to check the current parameter value:
Reset the value to what was active at the start of the session:
Setting Parameter Values within a Transaction
Open a transaction and set a new work_mem value:
Page 14: Page 14
If the transaction is rolled back, so is the new parameter value. If the transaction commits, however, the parameter remains changed.
You can set a new parameter value just for the duration of the current transaction:
When the transaction commits, the old value returns:
Custom Parameters
Custom parameters can be defined during session. You can also check if such a parameter is already defined. Custom parameter names must include the dot (.) in order to distinguish them from standard parameters.
Now myapp.currency_code can be used as a global variable during the session:
Page 15: Page 15
Custom parameters can be defined in configuration files. This will make them initialize in all sessions.
Page 16: Takeaways
The main configuration file is postgresql.conf
- ALTER SYSTEM is an SQL interface for managing configuration parameters stored in postgresql.auto.conf
- When configuration files are modified, they have to be reloaded
- Some parameters can be changed just for the current session
- Changes to some parameters require a server restart to apply
Page 17: Practice
- Get a list of parameters that require a server restart to apply.
- In the additional included configuration file, make an error when changing the max_connections parameter.
- Restart the server. Make sure that the server does not start and
- check the message log.
- Fix the error and start the server.
Speaker notes
- To get the location of the postgresql.conf file, check the value of the config_file parameter, as shown in the demonstration.
At the end of this file, you will find include_dir directive that loads additional configuration files.
Create a file named max_connections.conf in the directory specified by this directive. Edit this file either as the owner (the postgres user) or as a superuser.
To obtain the necessary privileges, use the sudo command.
Use your preferred text editor. For example: sudo nano /etc/postgresql/16/main/conf.d/max_connections.conf
To exit nano, use the Ctrl+X key combination.
Page 18: 1. Parameters that Require a Server Restart to Apply
2. Setting the max\_connections Parameter
The current value of max_connections:
Page 19: Page 19
Imagine we try to set it to 50, but mistype and enter the letter O instead of the zero:
The error can be discovered by looking at the pg_file_settings view:
But here, we didn’t look at pg_file_settings and restarted the server right away:
The server does not start. The reason is recorded in the server log. Here is what it says:
Let’s fix the error:
The server is not running, so we’ll use an operating system command to verify.
Restart the server:
The server starts. Check the max_connections value:
Page 20: Page 20
Page 21: Practice+
- Set the parameter work_mem = 32MB in the psql tool’s command line options. 2. In the Ubuntu package distribution, the postgresql.conf file is not located in the PGDATA directory. How does the server find this configuration file at startup?
Speaker notes
- Use either the options key in the connection string or the PGOPTIONS environment variable.
More on how connection strings are formed:
https://postgrespro.com/docs/postgresql/16/libpq-connect#LIBPQ-CONNST RING
- To see the location of the postgresql.conf file, check the config_file parameter.
To find where the parameter is set, run the ps command for the postgres main process. The process ID (PID) is the first line of the postmaster.pid file, which is located in the data directory (PGDATA).
Page 22: 1. Setting Parameters at Application Start
If the app uses the libpq library to connect to the server, there are two ways you can set parameters at application start. The first way is to add the options key to the connection string:
The second way is set the PGOPTIONS environment variable:
2. The config\_file Variable
The file postgresql.conf isn’t located within the data catalog:
How does the server know where postgresql.conf is? In the Ubuntu package distribution, the config_file value is defined in the server startup command text. This allows the postgresql.conf file to be stored outside of PGDATA. To check the server’s startup command line, you can use the ps command. Find the PID of the main server process – it’s listed in the first line of the postmaster.pid file located in the PGDATA directory:
Now we can retrieve the postmaster process command line:
04 Lesson 04: Architecture / PostgreSQL Fundamentals
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_04_arch_general.html Archive: DBA1-handouts-16-en.zip PDF pages: 20
Page 1: Architecture / PostgreSQL Fundamentals
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Igor Gnatyuk Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is,” and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- Client-Server Protocol
- Transactionality and its Implementation
- Query Processing and Execution
- Processes and Memory Structures
- Storing Data on Disk and Data Processing
- System Extensibility
Page 3: Client and Server
{#source-figure-003 caption=“Source slide page 3: Client and Server”}
- connection
- authentication
- query generation
- query execution
- transaction management
- transactionality support
Speaker notes
A client application, such as psql or any other program written in any programming language, connects to the server and “communicates” with it. In order for the client and the server to understand each other, they must use the same communication protocol. Usually, the client uses a driver that implements the protocol and provides a set of functions to use in the program. Internally, the driver can use the standard protocol implementation (the libpq library), or can implement the protocol itself.
The language the client is written in is unimportant, as the functionality behind the syntax is defined by the protocol. As an example, we will use the SQL language and the psql client. Of course, no one really would program a client in SQL, but we will use it here purely for educational purposes. It should not be that difficult to substitute any of the SQL commands provided below with corresponding statements in your programming language of choice.
Generally speaking, a connection protocol allows the client to connect to a database in a cluster. The server performs authentication: decides if the client should be allowed to connect, i.e. by demanding a password.
Then, the client sends the server queries in the SQL language, the server executes the queries and sends back the results. A powerful and convenient query language is one of the fundamentals of relational databases.
Another one is the ability to maintain transaction support.
https://postgrespro.com/docs/postgresql/16/protocol
Page 4: Transactions
{#source-figure-004 caption=“Source slide page 4: Transactions”}
- atomicity — everything or nothing consistency — integrity constraints and user restrictions isolation — parallel processes impact
- durability — no data loss after a failure
Speaker notes
A transaction is a sequence of operations that preserves the consistency of data, provided that the operations are performed completely and without interference from other transactions.
Transactions must satisfy four properties collectively known as ACID:
● Atomicity. A transaction is either executed in full or not executed at all. To that end, the beginning of a transaction is marked with the BEGIN command, and the end with either COMMIT (commit changes) or ROLLBACK (undo changes).
● Consistency. Transactions move the database from one consistent state to another consistent one (consistency here means that certain restrictions are fulfilled).
● Isolation. Transactions running simultaneously should not affect the specific one.
● Durability. Once data is committed, it should not be lost even after a server failure.
In PostgreSQL, the client application is the side usually responsible for transaction management (that is, for determining what commands make up a transaction, and for committing or canceling the transaction). Transactions can be managed on the server side by stored procedures.
https://postgrespro.com/docs/postgresql/16/sql-begin
https://postgrespro.com/docs/postgresql/16/sql-savepoint
https://postgrespro.com/docs/postgresql/16/transactions
Page 5: Transaction Management
By default, psql runs in autocommit mode:
This means that any single command sent without explicitly opening the transaction is committed immediately. Check if this mode is enabled by default in the PostgreSQL driver for your favourite programming language. Create a table with one row:
Will another transaction see this table and row?
Yes. Compare the result:
What will the other transaction see now?
The changes are not yet committed so the other transaction does not see them.
What about now?
When autocommit is off, a transaction is implicitly opened upon command input. The command then must be committed manually.
What do we see now?
Page 6: Page 6
The changes are not there, as the transaction was opened implicitly.
Now, finally:
Turn the default autocommit mode back on.
Individual changes can be rolled back without interrupting the entire transaction (although this is rarely used).
Note Note how the transaction sees its own changes, even the uncommitted ones. Now, roll back to the save point. Rolling back to a save point does not imply a transfer of control (that is, it does not work as GOTO); only those changes to the database state that were made from the moment the point was set and up to the current moment are canceled.
Check the table:
The changes have been rolled back, but the transaction is still open:
Page 7: Page 7
Page 8: Query Processing
{#source-figure-008 caption=“Source slide page 8: Query Processing”}
parsing ← system catalog rewriting ← rules planning ← statistics execution ← data
Speaker notes
Query execution is complicated. First, a query is sent from a client to the server as text. The server parses the text, analyzing its syntax (whether letters are formed into words, and words into commands) and semantics (whether there are tables and other objects in the database that the query refers to by name). To do that, the server needs data on what is actually stored in the database. This meta-data is called the system catalog and is stored in special tables in the same database.
A query can be rewritten (transformed). For example, a view name can be substituted with the query text. Users can implement their own transformations using the rule system.
SQL is a declarative language: a query defines what data to get, but not how to get it. It is at this point when the query (already parsed and presented in the form of a tree) is passed on to the planner, which develops an execution plan. For example, the planner can decide whether or not to use indexes to find the data for the query. To plan the execution efficiently, the planner needs certain information about the tables it is going to work with, such as the size of the tables and the distribution of data within them. Together, this information is called statistics.
When a plan is selected, the query is executed in accordance with it, and the result is returned to the client in its entirety.
https://postgrespro.com/docs/postgresql/16/query-path
This is convenient and simple when we are talking about just a row or two, provided by the simple mode of the protocol.
Page 9: Prepared Statements
{#source-figure-009 caption=“Source slide page 9: Prepared Statements”}
parsing rewriting
- binding ← parameter values planning execution
Speaker notes
Each query goes through the steps listed above: parsing, rewriting, planning and execution. But if the same query (possibly with different parameters) is executed over and over again, there is no point in parsing it anew every time.
Therefore, in addition to the usual query execution process, the PostgreSQL protocol provides an extended mode that can control statement execution more precisely.
One of its features is the ability to prepare a statement. When a statement is prepared, it is parsed and rewritten as usual and its parse tree is saved.
When the statement is executed, specific parameter values are bound to it. If necessary, planning is redone (in some cases, PostgreSQL remembers the query plan and does not repeat this step). Then, the statement is executed.
Another advantage of prepared statements is that they are protected from possible SQL injections.
https://postgrespro.com/docs/postgresql/16/sql-prepare
https://postgrespro.com/docs/postgresql/16/sql-execute
Page 10: Prepared Statements
In SQL, you can prepare a statement by using the PREPARE command (it is a PostgreSQL extension not present in the SQL standard):
The statement is parsed and rewritten, and the parse tree is saved. A prepared statement can be called by its name using arbitrary parameters:
For non-parametric statements, an execution plan is saved as well. For queries with parameters, as in the example, the parameter values are taken into account at the planning stage. The planner may decide that the plan built without considering the parameters is good enough. In this case, it will not try to plan the query again. How do you prepare and execute a statement using your favourite programming language? Can you execute a statement WITHOUT preparing it? All prepared statements in the current session can be displayed with:
Page 11: Cursors
{#source-figure-011 caption=“Source slide page 11: Cursors”}
parsing rewriting
- binding ← parameter values planning execution
- output
Speaker notes
The client may not want to get all the output at once. There can be too much data, and not all of it may be needed.
This issue is solved by cursors, another feature of the extended mode. The protocol can open a cursor for any operator, and then receive the output row by row.
A cursor can be imagined as a sliding window that shows only a part of the output at a time. When an output row is received, the window shifts down. In other words, cursors allow you to work with relational data (that comes in sets) iteratively, row by row.
An open cursor is represented on the server by a so-called portal. This term is mentioned in the documentation, but in general, the words “cursor” and “portal” can be considered synonyms.
A statement used within a cursor is implicitly prepared (that is, its parsing tree and possibly execution plan are saved).
https://postgrespro.com/docs/postgresql/16/sql-declare
https://postgrespro.com/docs/postgresql/16/sql-fetch
Page 12: Cursors
When the SELECT command is executed, the server sends and the client receives all the rows at once:
Cursors let the client to retrieve data in batches of rows.
You can set the size of the batch:
The batch size is paramount for huge outputs, when processing them one row at a time is inefficient. What if we reach the end of the table?
FETCH will just stop returning rows. All regular programming languages have a way to check for this condition. How do you get data row by row using the cursor in your favorite programming language? Is it possible NOT to use the cursor and get all the rows at once? How is the cursor batch size set? You can close your cursor when done, freeing up some resources:
However, cursors are automatically closed when the transaction ends, so explicit closing is optional (except WITH HOLD cursors.)
Page 13: Page 13
Page 14: Processes and Memory
{#source-figure-014 caption=“Source slide page 14: Processes and Memory”}
Speaker notes
Between processing queries from clients, the server must store technical information, such as parsed queries and their plans and the status of open cursors (portals). Where is it stored and how?
Under the hood, a PostgreSQL server consists of several interacting processes. First of all, when the server starts, a process traditionally called postmaster is started. It starts all other processes (using the fork system call in Unix). It also “babysits” them: if any of the processes crashes, postmaster will restart it (or restart the entire server if it considers that the failed process could have damaged any of the shared data).
Operations of the server are maintained by a number of background processes. The main ones will be discussed in the following lessons.
In order for the processes to exchange information between them, postmaster allocates shared memory that all the processes can access. In addition to shared memory, each process has its own local memory, accessible only to itself.
Postmaster also listens for incoming connections. For each connecting client, postmaster generates a designated backend process for the client to communicate with on the server side, and each client gets its own process. The backend process performs authentication, among other things.
The space required to execute a client’s query (parsed queries and their plans, cursor states, system catalog cache, a place to sort data, etc.) is allocated in the local memory of the backend process of this client.
Page 15: Multiple Clients
{#source-figure-015 caption=“Source slide page 15: Multiple Clients”}
Speaker notes
When multiple clients connect to a server, each one gets a backend process created for it. As long as there are not too many clients, RAM is sufficient, and connections do not occur too often, sustaining many connections at once is not a problem in itself.
However, when multiple processes try to access the same database object, things must be done to ensure that one process will not change the data while another is in the process of reading it.
For objects in shared memory, this is ensured by short-term locks. PostgreSQL does this carefully enough so that the system scales well with an increase in the number of processors (cores).
Tables are more complicated. Locks will have to be held until the end of transactions (that is, potentially for a long time), so scalability may suffer. To avoid that, PostgreSQL uses a multiversion concurrency control mechanism (MVCC) and snapshot isolation: multiple versions of the same data can exist simultaneously, and each process sees only its own (but always consistent) data snapshot. Now, only those processes that are trying to change data that has already been changed, but not yet committed by other processes, will be locked.
MVCC is the main mechanism that enables the first three properties of transactions (atomicity, consistency, and isolation). We will talk about it more in its dedicated lesson.
Page 16: Connection Pool
{#source-figure-016 caption=“Source slide page 16: Connection Pool”}
Speaker notes
If there are too many clients, or connections are established and broken too often, using a connection pool may help. This functionality is usually provided by the application server or third-party pool managers (the most popular of which is pgBouncer).
With a connection pool in place, clients connect not to the PostgreSQL server directly, but to the pool manager. The manager keeps several connections to the database server open and uses free ones to fulfill client queries. From the server’s point of view, the number of clients remains constant regardless of how many clients access the pool manager.
The drawback is that multiple clients end up sharing the same backend process, which, as we remember, stores client-specific state in its local memory (such as parsed queries for prepared statements). Therefore, care should be taken when developing applications for such deployments.
One of pgBouncer’s features is the ability to temporarily pause client connections without disconnecting them. This pause can be used for server software updates or other operations that require a server restart.
The use of connection pools is discussed in greater detail in the DEV2 course.
Page 17: Data Storage
{#source-figure-017 caption=“Source slide page 17: Data Storage”}
Speaker notes
Data is stored as regular OS files on disks. How exactly the data is distributed among the files is discussed in one of following lessons.
Logically, the files are divided into pages (sometimes the term block is used). A page is usually 8KB in size. It can be changed within some limits (16 KB or 32 KB), but only during server compilation. A cluster that has been compiled and started up this way can work with pages of only one size.
Each page has a certain internal structure. It contains a header and actual data. There may be free space between them if the page is not fully occupied.
Since disks work much slower than RAM (especially HDD, but SSD too), data heading to disk is cached first. A buffer cache, a certain amount of space in RAM, is allocated for recently read pages. The idea is that the system may want to read the same pages multiple times, and keeping them on hand may save time compared to repeated disk scans. Any recently changed data is also cached for some time before being written on disk.
Page 18: Data Storage
{#source-figure-018 caption=“Source slide page 18: Data Storage”}
Speaker notes
The PostgreSQL buffer cache is located in shared memory so that all processes have access to it.
PostgreSQL does not directly access disks storing its data. Instead, it relies on the operating system. The operating system also has its own data cache. Therefore, if a page is not found in the buffer cache, there is a chance that it is in the OS cache and access to the disk will be avoided.
In case of a failure (for example, power supply dies), the contents of the RAM are lost, and the data changed but not yet written to disk will be lost. This is unacceptable and breaks the durability property of transactions. To avoid that, PostgreSQL keeps a log that allows it to redo lost operations and restore data to a consistent state. We will talk about the buffer cache and the write-ahead log in the dedicated lesson later on.
Page 19: Extensibility
{#source-figure-019 caption=“Source slide page 19: Extensibility”}
Speaker notes
PostgreSQL is designed with extensibility in mind. It allows to create new data types based on existing ones, write stored procedures for data processing, and offers a convenient toolkit for administration, monitoring, and performance tuning.
You can always write an extension that adds the necessary functionality that you need. Most extensions can be installed “hot”, without stopping the server. Thanks to this architecture, there are plenty of existing extensions doing things such as:
● adding support for programming languages (in addition to standard SQL, PL/pgSQL, PL/Perl, PL/Python and PL/Tcl);
● introducing new data types and operators to work with them;
● creating new index types that work more efficiently with specific data types (in addition to standard B-trees, hash indexes, GiST, SP-GiST, GIN, BRIN);
● starting background workers to perform additional tasks;
● connecting to external data sources;
● collecting system load information, performing monitoring, and generating reports;
● exploring system data structures.
Extensibility is discussed in more detail in the DBA2 and DEV2 courses.
Page 20: Takeaways
A server manages a database cluster
- The protocol allows clients to connect to the server, transmit queries and manage transactions
- Each client is served by a dedicated backend process
- Data is stored in files and accessed via the operating system
- Data is cached both in local memory (system catalog, parsed queries) and in shared memory (buffer cache)
05 Lesson 05: Architecture / Isolation and MVCC
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_05_arch_mvcc_overview.html Archive: DBA1-handouts-16-en.zip PDF pages: 18
Page 1: Architecture / Isolation and MVCC
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Igor Gnatyuk Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is”, and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- Multiversion Concurrency Control
- Data Snapshot
- Isolation Levels
- Vacuuming and its Horizon
- Locking
- Transaction Status
Page 3: MVCC
{#source-figure-003 caption=“Source slide page 3: MVCC”}
Storing multiple versions of the same row
- versions have different validity time
- timestamp = transaction ID (IDs are given in ascending order)
Speaker notes
When multiple sessions are running at the same time, two transactions may access the same row at the same time. If both transactions are just reading the row, there is no problem. If both transactions try to write, no problem either (in this case, they line up and make the changes one after the other). The tricky part is when one transaction wants to read a row and another one wants to change it at the same time.
There are two simple ways about it. You can let such transactions block each other, but then performance suffers. Otherwise, you can let the reading transaction immediately see the changes made by the writing transaction, even if they are not committed (this is called a “dirty read”). This is dangerous, because the changes can be rolled back.
PostgreSQL goes the hard way and utilizes what is known as Multiversion concurrency control. In essence, the system stores multiple versions of each row. So, a writing transaction operates on its own version, while a reading transaction sees its own version.
To distinguish between the versions, PostgreSQL marks each one with two timestamps, which together specify a version’s “validity time”.
The timestamps are essentially just transaction IDs, which always come in ascending order. (In reality, the whole thing is a bit more complicated, but not worth getting into right now.) Upon creation, a row version is marked with the ID of the transaction that executed the INSERT command. When deleted, the version is marked with the ID of the transaction that did the DELETE command (but is not physically deleted). An UPDATE command is a DELETE and an INSERT executed back to back.
https://postgrespro.com/docs/postgresql/16/mvcc-intro
Page 4: Data Snapshot
{#source-figure-004 caption=“Source slide page 4: Data Snapshot”}
Representation of database data in a consistent state at a specific point in time
- transaction ID defines the point in time
- list of active transactions helps the system
- to exclude changes that have not been committed
Speaker notes
PostgreSQL uses snapshot-based transaction isolation.
A transaction accessing a table should see only one of the versions of each row (or none at all). To achieve this, PostgreSQL presents the transaction with a data snapshot created at a certain point in time. The snapshot includes the most recent versions of all committed rows but does not include any non-committed changes from active transactions. In other words, the snapshot takes the version of each row that corresponds to the moment when the snapshot was created.
A snapshot is not a physical copy of the data, but just a few numbers:
● the ID of the last committed transaction at the time of snapshot,
● list of active transactions at that point in time.
The list is needed in order to exclude from the snapshot any changes those transactions may have made but not yet committed.
With just these numbers, we can always tell which row version will be visible in the snapshot. Sometimes it will be the current (most recently committed) version, as with row 1 in the diagram. Sometimes not: row 2 has been deleted after the snapshot has been created (and the change has already been committed), but the transaction still continues to see it while working with the snapshot. This is the correct behavior, the snapshot gives a consistent representation of data at the selected point in time.
Some rows will not get into the snapshot at all: row 3 was deleted before the snapshot was made, so it is not included.
Page 5: Isolation Levels
Read Uncommitted
- not supported by PostgreSQL: works as Read Committed
- Read Committed (default)
- the snapshot is created as of the time a statement starts
- repeated query may return different data
- Repeatable Read
- the snapshot is created as of the time the first transaction statement starts
- a transaction may fail with a serialization error
- Serializable
- total isolation, but additional overhead
- a transaction may fail with a serialization error
Speaker notes
The SQL standard defines four isolation levels: the stricter the level, the less concurrent transactions affect each other. At the time when the standard was adopted, it was believed that the stricter the level, the more difficult it is to implement and the stronger its impact on performance (since then, these views have changed somewhat).
The most lax level of Read Uncommitted allows dirty reads. It is not supported by PostgreSQL, because it is of no practical value and does not give a performance gain.
The Read Committed level is the default isolation level in PostgreSQL. At this level, data snapshots are created at the beginning of each SQL statement execution. Thus, the statement works with an unchanged and consistent data snapshot, but two identical queries following one after the other may show different data.
At the Repeatable Read level, the snapshot is built at the beginning of a transaction (when executing the first statement). This makes all queries inside the same transaction see the same data. This level is convenient, for example, for generating reports from several queries.
The Serializable level guarantees total isolation. At this level, you can use any statements as if the transaction is running alone. The drawback is that some transactions will fail, and your application must be able to repeat such transactions.
https://postgrespro.com/docs/postgresql/16/transaction-iso
Page 6: Row Version Visibility
How do you verify that the same row can exist in multiple versions? Create a table:
And insert one row. Remember that if you do not explicitly start a transaction with BEGIN, psql executes the command and immediately commits the result:
Start the transaction and get its ID:
The transaction sees the first (and so far the only) version of the row:
Here, the hidden columns show the transaction IDs that limit the visibility of the row version: xmin is the ID of the previous transaction which created the version, and xmax=0 means that this version is current. Now start another transaction in another session:
The transaction sees the same and only version:
Now change the row from within the second transaction.
This is what we get:
Page 7: Page 7
What will the first transaction see?
Since the change is not yet committed, the first transaction continues to see the first version of the row.
Note the xmax value: it indicates that another transaction is currently changing the row. Generally speaking, such “peeping” violates isolation, so the xmin and xmax fields are hidden and should not be used in production. Now commit the changes.
What will the first transaction see now?
The first transaction also sees the second version of the string. After commit, the first version of the row is no longer visible in any transaction.
Page 8: Vacuuming and its Horizon
{#source-figure-008 caption=“Source slide page 8: Vacuuming and its Horizon”}
There is a single horizon for each database
- Long-running transactions can hold back the horizon
- thereby blocking the vacuuming of outdated row versions
Speaker notes
MVCC makes it possible to effectively implement snapshot-based isolation, but as a result, old (“dead”) row versions (“dead tuples”) accumulate in table pages.
Historical versions are needed for some time so that transactions can work with their data snapshots and, in case of rollback, get back to old values. For each database, there is a transaction ID (xid), so that all historical versions deleted by transactions with lower xids are no longer visible in any snapshot. This xid is called vacuum horizon.
Vacuuming is performed by special background processes; but can also be executed manually using the VACUUM command.
Long-running transactions and queries can hold back the horizon, which prevents the removal of accumulated row versions. If you do not vacuum historical data in a timely manner, tables and indexes will bloat, consuming excessive disk space, and the search for current row versions will slow down.
https://postgrespro.com/docs/postgresql/16/routine-vacuuming
Page 9: Locking
Row locks
- reading never locks rows
- changing rows locks them for changes, but not for reads
- Table locks
- prohibit changing or deleting a table while it is being worked on
- prohibit reading the table when rebuilding or moving
- etc.
- Lock lifetime
- set as needed or manually
- released automatically upon transaction completion
Speaker notes
So what does MVCC provide? It allows the system to have only the most necessary minimum of locks, thereby increasing performance.
The main locks are set at the row level. Reading never blocks either reading or writing transactions. Changing a row does not lock it for reading. The only case when a transaction will wait for a lock to be released is if it tries to change a row that has already been changed by another transaction that has not been committed yet.
Locks can also be set at a higher level, particularly on tables. They are needed so that no one can delete the table while other transactions are reading data from it, or to prohibit access to the table being rebuilt. Such locks generally do not cause problems, since deleting or rebuilding tables is only done once in a while. However, some changes of table structure trigger implicit table rebuilding, completely blocking access to the table and its indexes. Locks are explained in detail in the Locking module of the DBA2 course.
All necessary locks are set automatically and automatically removed at the end of the transaction. However, if a lock was acquired after the savepoint was set, it will be released immediately when rolling back to that savepoint.
You can also set additional custom locks, but this is rarely necessary.
https://postgrespro.com/docs/postgresql/16/explicit-locking
Page 10: Locking
Repeat the experiment, but now let both transactions try to change the same row.
And in the second transaction:
The second transaction “hung up”: it cannot change the row until the first transaction releases the lock.
Now the second transaction can continue:
Both transactions have committed their changes. The first session reads the table again and sees the current row: this is the result committed by the second transaction:
Page 11: Transaction Status
Transaction status (clog)
- service information; two bits per transaction
- stored in files on disk
- cached in shared memory
- Commit
- the “transaction committed” bit is set
- Termination
- the “transaction aborted” bit is set
- performed as fast as commit (no data rollback needed)
Speaker notes
For multiversion concurrency control to work, the server needs to understand the status of transactions. A transaction can be active or finished. A transaction can end either in a commit or in an abort. Therefore, two bits are required to represent the state of each transaction.
Transaction statuses (commit log, clog) are stored in special service files in the PGDATA/pg_xact directory and worked upon in the server’s shared memory, as to avoid constantly accessing the disk.
At any transaction completion (either successful or not), it is enough to set the appropriate status bits. Both transaction commit and abort occur equally quickly.
If an aborted transaction managed to create new row versions, these versions are not destroyed (there is no “physical” rollback of data). Thanks to the status information, other transactions will see that the transaction that created or deleted the row versions was actually aborted, and will not take changes made by it into account.
Page 12: Takeaways
Multiple versions of each row can be stored in data files
- Transactions work with data snapshots, representations of database data in a consistent state at a specific point in time
- Isolation levels have different snapshot creation times
- Dead row versions beyond the vacuum horizon must be periodically vacuumed
- Writers do not block readers, readers do not block anyone
Page 13: Practice
- Create a table with one row. Begin a transaction at the Read Committed isolation level and query the table. In another session, delete the row and commit the changes. How many rows will the first transaction see after executing the same query again? Try and see. Complete the first transaction. 2. Repeat the same thing, but now let the transaction work at the isolation level Repeatable Read:
- BEGIN ISOLATION LEVEL REPEATABLE READ;
- Explain the differences.
Page 14: 1. Read Committed Isolation Level
Create a table:
Query from the first transaction (default isolation level is Read Committed):
Delete the row in the second transaction and commit the changes:
Repeat the query:
The first transaction sees the change: the row has been deleted.
2. Repeatable Read Isolation Level
Return the row:
Query from the first transaction:
Delete the row in the second transaction and commit the changes:
Repeat the query:
Page 15: Page 15
At this isolation level, the first transaction does not see any changes: the row still exists for it.
Page 16: Practice+
- Begin a transaction and create a new table with one row. Without completing the transaction, open a second session and query the table in it. Check what the transaction will return in the second session.
- Commit the transaction in the first session and repeat the query
- to the table in the second session.
- Repeat task 1, but roll back rather than commit the transaction in the first session. What has changed?
- In the first session, start a transaction and make a query to the previously created table. Will it be possible to delete this table in the second session before the transaction is completed? Try and see.
Page 17: 1. Transactions and DDL Commands: Commit
Start a transaction and create a new table:
In the second session, query the table:
Until the transaction that created the table is completed, all other transactions do not see the table. The table will be visible only after the completion of the transaction that created it:
2. Transactions and DDL Commands: Rollback
Start a transaction and create a new table:
The query in the second session sees no changes, as expected:
When the first transaction is rolled back, the table creation command is also rolled back:
In PostgreSQL, DDL commands are transactional.
3. Table Locks
Page 18: Page 18
Start a transaction, query the table:
The second transaction, which is trying to remove the table is locked: you cannot remove a table that is being used.
The table will be removed only after the first transaction is completed:
06 Lesson 06: Architecture / Vacuuming
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_06_arch_vacuum_overview.html Archive: DBA1-handouts-16-en.zip PDF pages: 21
Page 1: Architecture / Vacuuming
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Aleksei Beresnev Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is,” and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- Routine Tasks
- Autovacuum
- Vacuum and Analysis
- Table and Index Bloating
- Full Vacuum and Rebuilding of Indexes
Page 3: Routine Tasks
Cleaning pages to remove MVCC historical data
- dead row versions are vacuumed out of tables
- index entries referencing dead versions are vacuumed from indexes
Speaker notes
MVCC makes it possible to effectively implement snapshot isolation, but as a result, old versions of rows accumulate in table pages, and references to these versions accumulate in index pages. Historical versions are needed for some time so that transactions can work with their data snapshots. But over time, a row version will no longer have any snapshot that needs it. Such a version is called “dead”.
The vacuum procedure cleans out dead row versions from table pages, as well as unnecessary index entries that reference such versions.
If historical data is not vacuumed in a timely manner, tables and indexes will bloat uncontrollably and the search for current row versions in them will slow down.
https://postgrespro.com/docs/postgresql/16/routine-vacuuming
Page 4: Routine Tasks
Updating the visibility map
- tracks pages where all row versions are visible in all snapshots
- used to optimize vacuuming and speed up index access
- exists only for tables
Speaker notes
In addition to this main task, vacuuming also performs other instance maintenance tasks. Vacuuming updates the visibility map and the free space map. This is service information that is stored alongside the main data.
The visibility map shows pages that contain only the current row versions visible in all data snapshots. In other words, these are pages that have not changed for long enough to be cleared out of outdated row versions.
The visibility map has several uses:
● Vacuuming optimization.
The marked pages cannot contain dead row versions, so they can be skipped during vacuuming.
● Index-Only access speedup.
Versioning information is stored only for tables, but not for indexes (that is why indexes do not have visibility maps). After getting a reference to a row version from an index, you usually need to read the table page to check its visibility. But if the index itself already has all the necessary columns, and at the same time the page is marked in the visibility map, then reading the table page can be skipped.
If the visibility map is not updated regularly, index access can slow down. This is described in more detail in the Query Performance Tuning (QPT) course.
Page 5: Routine Tasks
Updating the free space map
- tracks the free space in the pages after vacuuming
- used when inserting new row versions
- exists for both tables and indexes
Speaker notes
The free space map tracks available free space within pages. This space is constantly changing, decreasing when new row versions are added and increasing when they are removed.
When inserting new row versions, the map helps to quickly find a suitable page to record the data into. The free space map has a complex tree-like structure designed to improve search speed.
Indexes can have free space maps as well. However, since index entries are inserted into specific positions within an index, the map only tracks empty pages, which form when all index entries are deleted from them. These pages are excluded from the index and later can be included again into the proper part of the index.
Page 6: Routine Tasks
Updating statistics
- used by the query optimizer
- calculated based on a random sample
Speaker notes
The query optimizer requires statistical information about the data it is working with, such as the number of rows in tables and the distribution of data in columns. The process of collecting the statistics is called analysis.
For analysis, a random sample of data of a certain size is read from the table. This way, the system can quickly collect statistics even on very large tables. The result is not accurate, but it is not expected to be. The data constantly changes, so it is impossible to maintain absolutely accurate statistics all of the time anyway. It is sufficient to keep it relatively up-to-date and relatively accurate.
If statistics are not updated regularly, they will no longer represent the data accurately, leading to the optimizer proposing inefficient execution plans. Because of this, queries may start executing orders of magnitude slower than they could.
Page 7: Routine Tasks
{#source-figure-007 caption=“Source slide page 7: Routine Tasks”}
Freezing
- prevents the consequences of 32-bit transaction counter overflow
Speaker notes
As already mentioned, PostgreSQL orders events by transaction ID. The counter has 32 bits allocated for it, and sooner or later it will overflow.
This is why the transaction ID scope is looped. For each transaction, half of the IDs are considered to be in the future, half in the past.
But when the counter wraps around to zero, the order of transactions will be disrupted. To prevent this, sufficiently old row versions are marked as frozen. This means that they were created so far in the past that their transaction ID no longer means anything and can be reused. Frozen row versions are visible in all snapshots.
To avoid scanning extra pages, the visibility map has a bit that marks the pages where all row versions are frozen.
Without regular freezing, the server may end up with no available transaction IDs for new transactions. This is an emergency: the server stops, all active transactions are aborted, and the administrator has to manually start the server and perform the freezing.
Page 8: Autovacuum
{#source-figure-008 caption=“Source slide page 8: Autovacuum”}
Autovacuum launcher
- background process
- periodically launches
- worker processes
- Autovacuum worker
- vacuums tables of a
- specific database that
- require processing
Speaker notes
All the maintenance tasks discussed above are taken care of by the autovacuum process. It dynamically reacts to the frequency of table updates, and the more active the changes, the more often the table will be vacuumed.
The autovacuum launcher process is permanently running in the background. It schedules the vacuuming work and launches the required number of autovacuum workers working in parallel.
Vacuuming works page-by-page, it does not block other transactions, though it still does require additional I/O resources.
Autovacuum will not work if either of the two parameters autovacuum or track_counts is switched off. It may seem that disabling autovacuum can increase system performance by eliminating “unnecessary” I/O operations, but it cannot. Failure to vacuum will lead to the consequences described above: uncontrolled bloating, slower query processing, and the risk of an emergency server shutdown. Ultimately, this will lead to a complete system paralysis.
Autovacuuming is absolutely necessary. There is a large number of configuration parameters that allow tweaking the autovacuum process. They are discussed in detail in the DBA2 Configuration and Monitoring course.
Page 9: Manual Vacuuming
Vacuuming
- VACUUM [table, …]
- vacuum specific tables
- VACUUM
- vacuum the entire database
- $ vacuumdb
- wrapper for the OS
- Analysis
- Vacuum and analysis
Speaker notes
If necessary, vacuuming and analysis can be started manually using the following commands:
VACUUM (vacuuming only), ANALYZE (analysis only), and VACUUM ANALYZE (both vacuuming and analysis).
Autovacuum is different from running scheduled vacuuming and analysis as it reacts to the frequency of data changes. Running vacuum on a schedule too often will create unnecessary load on the system. On the other hand, if you vacuum too rarely, and data is changed often, the files may have time to bloat significantly between vacuums.
https://postgrespro.com/docs/postgresql/16/sql-vacuum
https://postgrespro.com/docs/postgresql/16/sql-analyze
Page 10: Vacuuming
Let’s create a table and turn autovacuum off so that we can control the vacuuming manually in our experiments:
Fill the table with data and build an index:
Each table row has only one version, the last one. Now, let’s update some rows:
Run vacuum and have it tell us what it is doing:
The important lines are: Dead versions of rows have been removed from the table (tuples: 10000 removed…). References to them have been removed from the index (index scan needed… 10000 dead item identifiers removed).
Page 11: Bloating
Vacuuming does not reduce the size of tables and indexes
- the “holes” in the pages are used for new data,
- but the space is never returned to the operating system
- Causes of bloating
- incorrect autovacuum configuration
- massive changes of data
- long-running transactions
- Negative consequences
- inefficient disk space use
- slower sequential table scan
- less efficient index access
Speaker notes
Vacuuming cleans out outdated row versions from pages. This creates spots of free space in the pages, which is then used to store new data. But the free space is not returned to the operating system, so, from the point of view of the OS, the size of the data files does not decrease.
In the case of indexes (B-trees), it is complicated by the fact that if there is not enough space in the page to place an index entry, the page is split into two. The resulting pages are never merged again, even if all index entries are removed from them.
If autovacuuming is configured correctly, the data files grow by a certain constant amount due to updates between vacuumings. But if a large amount of data is being changed at the same time, or there are active long transactions (keeping old data snapshots active and not allowing you to vacuum out old row versions), vacuuming will not be able to free up the space in time. As a result, the tables and indexes may continue to grow in size.
File bloating leads not only to disk space overuse (including for backups), but also to a decrease in performance.
https://wiki.postgresql.org/wiki/Show\_database\_bloat
https://postgrespro.com/docs/postgresql/16/pgstattuple
Page 12: Table and Index Bloating
There are several ways to assess bloating and its impact: Queries to the system catalog Using the pgstattuple extension
The extension helps monitor the state of a table:
tuple_percent is the proportion of useful information (not 100% due to overhead). Same for an index:
leaf_pages is the number of leaf pages of the index. avg_leaf_density is storage density of leaf pages. leaf_fragmentation is characteristic of physical order of leaf pages (0 — order, 100 — disorder). Let’s update half the rows at once:
Check the table again:
The density went down. To avoid scanning the whole table, pgstattuple can display approximate information:
Page 13: Page 13
Check the index:
Leaf page density remained the same, but the number of pages has increased.
Page 14: Rebuilding Objects
Full vacuum
- completely rebuilds the contents of tables and indexes
- locks the table completely
- Rebuilding indexes
- rebuilds indexes
- locks the index completely
- and locks the associated table for write operations
Speaker notes
In order to reduce the physical size of bloated tables and indexes, a full vacuum is required.
The VACUUM FULL command completely rewrites the contents of the table and its indexes, minimizing the space occupied. However, this process requires an exclusive table lock and therefore cannot be executed in parallel with other transactions.
https://postgrespro.com/docs/postgresql/16/sql-vacuum
You can rebuild an index or several indexes without touching the table. This is done by the REINDEX command. It locks the table for writing (reading is still available), so transactions trying to change the table or plan a query on it will be blocked.
https://postgrespro.com/docs/postgresql/16/sql-reindex
If prolonged exclusive locking is undesirable, you can consider pg_repack, a third-party extension (https://github.com/reorg/pg\_repack) that allows you to rebuild tables and their indexes on the fly.
Page 15: Rebuilding Objects
Non-blocking index rebuilding
- rebuilds indexes without locking tables for writing
- takes longer and may fail
- not transactional
- does not work for system indexes
- does not work for indexes associated with exclusion constraints
Speaker notes
The REINDEX … CONCURRENTLY command can work without locking the table for writing. However, non-blocking rebuilding takes longer and may fail (due to deadlocks). In this case, the index will need to be rebuilt again.
Non-blocking index rebuilding has some limitations: it cannot be performed inside a transaction, and it cannot rebuild system indexes and indexes for exclusion constraints (EXCLUDE).
Page 16: Rebuilding Objects
You can rebuild an index using the REINDEX command with the CONCURRENTLY clause. It rebuilds the index without stopping the system.
Check the index again:
The page count and density have returned to their initial values. To rebuild a table together with its indexes, you can use VACUUM FULL. However, unlike REINDEX CONCURRENTLY, this operation fully blocks all access to the table.
The density has increased, the freed up space is returned to the operating system.
Page 17: Takeaways
Row versions are accumulated, so periodic vacuuming is necessary
- Vacuuming serves multiple goals:
- updating visibility maps and free space maps
- collecting statistics for the optimizer
- freezing old row versions
- Autovacuuming is necessary, but requires configuration
- Full vacuum may be necessary to combat bloating
Page 18: Practice
- Disable autovacuuming and make sure it does not work. 2. In a new database, create a table with one numeric column and an index for this table. Insert 100,000 random numbers into the table. 3. Change half of the table rows several times. Write down the size of the table and the index each time. 4. Run a full vacuum. 5. Repeat step 3, running a regular vacuum each time you change the values. Compare the results. 6. Turn autovacuuming back on.
Speaker notes
Set the autovacuum parameter to off and reload the configuration files.
Use pg_table_size(table-name) and pg_indexes_size(table-name) functions. For more information about calculating the sizes of various objects, see the Data organization module.
Set the autovacuum parameter back to on (or reset its value with the RESET command), then reload the configuration files.
Page 19: 1. Switching Autovacuum Off
Autovacuum is running for now:
We disable autovacuum and reload the configuration settings.
The autovacuum launcher process is no longer present.
2. Database, Table and Index
Create a database, a table and an index:
Insert rows:
3. Changing Rows without Vacuuming
Store a query to calculate the size of the table and the index as a psql variable so that we can use it later:
Page 20: Page 20
The table and index sizes keep growing.
4. Full Vacuum
The table size is almost back to where it was initially, and the index size decreased (building an index for a large data set is more efficient than adding data to the index row by row).
5. Changing Rows with Vacuuming
The size increased once and then stabilized. The example demonstrates that removing (or changing) large amount of data should be done in multiple transactions, if possible. This will allow autovacuum to clean up unneeded row versions in time, avoiding table bloating.
6. Turn Autovacuum Back On
Page 21: Page 21
07 Lesson 07: Architecture / Buffer Cache and WAL
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_07_arch_wal_overview.html Archive: DBA1-handouts-16-en.zip PDF pages: 17
Page 1: Architecture / Buffer Cache and WAL
Copyright
© Postgres Professional, 2017–2025 Authors Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Igor Gnatyuk Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo by: Oleg Bartunov (Phu monastery, Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is,” and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- Buffer Cache
- Replacement Algorithm
- Write-Ahead Log
- Checkpoint
- Processes Related to the Buffer Cache and WAL
Page 3: Buffer Cache
{#source-figure-003 caption=“Source slide page 3: Buffer Cache”}
Buffer array
- data page (8 KB)
- additional information
- “Dirty” buffers
- asynchronous write
- Locks in memory
- for shared access
Speaker notes
The buffer cache is used to smooth out the difference between the RAM and disk speed. It consists of an array of buffers which contain data pages and some additional information (for example, the file name and the position of the page inside this file).
The page size is usually 8 KB; the size can only be changed when building PostgreSQL.
Any work with data pages goes through the buffer cache. If any process is going to work with the page, it first tries to find it in the cache. If the page does not exist, the process requests the operating system to read this page and places it in the buffer cache. (Note that the OS can read the page either from disk or from its own cache.)
After the page is written to the buffer cache, it can be accessed repeatedly without the overhead of operating system calls.
If a process has changed the data in the page, the corresponding buffer becomes “dirty”. The modified page must be written on disk, but for performance reasons, the recording occurs asynchronously and may be delayed.
The buffer cache, like other shared memory structures, is protected by locks to control concurrent access. Although locks are implemented effectively, access to the buffer cache is not nearly as fast as simply accessing RAM. Therefore, in general, the less data a query reads and modifies, the faster it will work.
Page 4: Replacement
{#source-figure-004 caption=“Source slide page 4: Replacement”}
Least Recently Used replacement
- dirty buffer is
- written on disk
- another page is read
- into the vacant space
Speaker notes
The buffer cache size is usually not so large as to fit the entire database. It is limited by the available RAM. Also, the larger the buffer cache, the greater the overhead. Therefore, when reading the next page, sooner or later the buffer cache has to run out of space. In this case, page replacement happens.
The replacement algorithm selects a page in the cache that has been used less often than others. If the selected buffer is dirty, the page is written on disk first to store the changes made to it. Then, a new page is written into the buffer.
This replacement is called LRU (Least Recently Used). It keeps the most frequently accessed data in the cache. Such “hot” blocks of data are not very common, and this approach helps to significantly reduce the number of requests to OS (and disk operations), provided enough cache memory.
Page 5: The Impact of Buffer Cache on Query Execution
Creating a new database in the cluster and connecting to it (for more details about databases, see the Data Organization module):
Create a table:
Populate it with rows:
The shared_buffers parameter indicates the buffer cache size:
The default value is too low. In the real world, you should increase it immediately after server installation (it will be applied after restart). Restart the server to wipe the cache clean.
Now, let’s compare the behaviour of the system as we run a query once, and then the same query again. Query plans is not the topic of this course, but we will peek into them every now and again. The EXPLAIN ANALYZE command used below will execute the query as well as display the execution plan and some extra details:
The “Buffers: shared” line shows the buffer utilization. read — the number of buffers where pages had to be read from disk.
hit — the number of buffers where requested pages were found.
Page 6: Page 6
Note Note that on the second query execution, not only the execution time went down, but the planning time too (because system catalog pages are cached as well).
Page 7: Write-ahead Log (WAL)
Problem: when a crash occurs, data from RAM that is not written on disk is lost
- WAL
- stream of records of the actions being performed;
- can be used to redo the steps lost during the crash
- records are written to disk earlier than the changed data
- WAL tracks changes to
- pages in tables, indexes and other objects
- transaction status (clog)
- WAL does not track changes to
- temporary and unlogged tables
Speaker notes
Having a buffer cache (and other buffers in RAM) increases performance at the cost of reliability. When a crash happens, all buffer cache content is lost. If the crash occurs on the OS or hardware level, the content of OS buffers will also be lost (the OS may have its own failsafes for this).
To increase reliability, PostgreSQL uses the Write-ahead log. When performing any operation, the WAL records minimum necessary information about the operation to be able to perform it again. The record must be written into the disk (or another persistent storage) before the data modified by the operation is (that is why it is called Write-ahead log).
WAL files are located in the PGDATA/pg_wal directory.
All objects that are being worked on in RAM have their operations logged. These include tables, indexes and other objects, as well as transaction statuses. Operations with temporary tables (tables which exist only during the scope of a session or a transaction and are only available to the user who has created them) are not logged. You can also set a regular table to be explicitly unlogged. The table will be quicker to work with, but will be wiped on crash.
https://postgrespro.com/docs/postgresql/16/wal-intro
Page 8: Write-Ahead Log
The WAL can be considered as a continuous stream of records. Each record has a unique ID called an LSN (Log Sequence Number). This 64-bit number represents the record’s byte offset from the start of the WAL. The current WAL position can be seen with pg_current_wal_lsn function:
The position is displayed as two 32-bit numbers separated by a slash. Let’s save it for future reference. Now let’s perform some operations and see what’s changed.
It’s not the absolute values we’re interested in, but the distance between them, as it shows the size of generated WAL records in bytes:
The WAL is stored in files in a separate catalog (PGDATA/pg_wal). By default, the files are 16 MB each, but you can change that during cluster initialization. In addition to browsing the files by means of the OS, you can also display them by the following command:
Page 9: Checkpoint
{#source-figure-009 caption=“Source slide page 9: Checkpoint”}
Regular flushing of all dirty buffers to disk
- ensures that all data changes before the checkpoint get to the disk
- limits the size of the WAL required for recovery
- Crash recovery
- starts from the last checkpoint
- WAL records are replayed one-by-one to restore data
Speaker notes
When PostgreSQL crashes, it enters the recovery mode on the next start. The data on disk at this point is inconsistent. Changes to hot pages were in the buffer cache, and are now lost, while some of the later changes have been flushed to disk already.
To restore consistency, PostgreSQL sequentially reads the WAL records, replaying the changes that did not make it to the disk. This way, the state of all transactions at the time of the crash is restored. Then, any transactions that have not been logged as committed are considered aborted.
However, logging all changes throughout a server’s lifetime and replaying everything from day one after each crash is impractical, if not impossible. Instead, PostgreSQL uses checkpoints. Every now and then, it forces all dirty buffers to disk (including clog buffers, which store transaction statuses).
A checkpoint is the moment in time when the flushing of all data to disk is started. However, you only have a valid checkpoint when the flushing of all such buffers is complete. It ensures that all data changes up to this point are safe in persistent memory.
In production environments with a large buffer cache, a checkpoint can flush many dirty buffers, so the server spreads this flushing over time to smooth out the I/O load.
When a crash occurs, recovery is started from the last completed checkpoint. Consequently, it is sufficient to store WAL files only as far back as the last completed checkpoint goes.
Page 10: Crash Recovery Using WAL
Modified table pages exist in the buffer cache but haven’t been written to disk yet. During normal shutdown, the server performs a checkpoint to flush all dirty pages to disk. However, we’ll simulate a system crash by sending a signal to the postmaster process.
When the server comes back up, it should begin the recovery. Let’s try:
All the changes have been recovered. After performing a checkpoint, PostgreSQL automatically deletes WAL files that are no longer necessary for recovery.
Page 11: Performance
{#source-figure-011 caption=“Source slide page 11: Performance”}
Synchronous mode
- write on commit
- backend
- Asynchronous mode
- background write
- walwriter
Speaker notes
The WAL approach is faster than working directly with disk without a buffer cache. Firstly, a WAL record is smaller than an entire page of data. Secondly, the WAL is written sequentially (and usually not read until a crash occurs), which is better for basic hard disk drives.
Performance can also be managed via configuration settings. If the records are stored to disk immediately (synchronous mode), this guarantees that the committed transaction will not be lost. But recording to disk is expensive and forces the committing backend process to wait in line. To prevent WAL records from getting “stuck” in the OS cache, PostgreSQL relies on call of the fsync function, which forces the data into persistent storage.
There is also asynchronous mode, which has a background process walwriter constantly flushing WAL records to disk, with a certain delay. It is more efficient at the cost of some reliability, but still ensures consistency after crash recovery.
In fact, both modes work together. WAL records of a long transaction are written asynchronously (to free up WAL buffers). And if a page is getting flushed to disk and the corresponding WAL record is not there yet, it will immediately be flushed in synchronous mode.
Page 12: Main Processes
{#source-figure-012 caption=“Source slide page 12: Main Processes”}
WAL writer
- Checkpointer
- flush all
- dirty buffers
- Background writer
- flush some
- dirty buffers
- Backends
- flush replaced
- dirty buffer
Speaker notes
Let’s take a step back and look at the processes that maintain the buffer cache and the WAL.
First, there is walwriter. This process writes WAL records to disk in asynchronous mode. In synchronous mode, this job is handled by the backend that commits the transaction.
Second, checkpointer, the checkpoint process. It periodically flushes all dirty buffers to disk.
Third, bgwriter (or background writer). It operates similarly to checkpointer, but it only flushes some of the dirty buffers, specifically, those that are most likely to be replaced soon. It frees up buffer space so that when backend selects a buffer to put a new page in, it does not have to flush the old contents of the buffer to disk itself.
Fourth, there are backends that put data into the buffer cache. Whenever a buffer being replaced is still dirty (despite the efforts of checkpointer and bgwriter), the backend will flush it to disk.
Page 13: WAL Levels
Minimal
- guarantees crash recovery
- Replica (default)
- backup
- replication: transmit the WAL on another server and replay it there
- Logical
- logical replication: information about adding, changing,
- and deleting table rows
Speaker notes
WAL was developed as a data protection tool to mitigate the risk of data loss due to crashes.
However, the WAL mechanism turned out to have other applications, if it is supplemented with additional information.
The data stored in the WAL is controlled by the wal_level parameter.
● The minimal level is sufficient to recover after a crash, and nothing else.
● The replica level stores additional information that allows WAL to be used for backup and replication. During replication, WAL records are transmitted to another server and applied there, creating an exact copy (replica) of the original server.
● At the logical level, information is added to the WAL that allows decoding “physical” WAL records and forming “logical” records of adding, changing and deleting table rows. This enables logical replication (see corresponding lessons of DEV2 and DBA3 courses for details).
Page 14: Takeaways
Buffer cache increases performance by reducing the number of disk operations
- WAL ensures reliability
- WAL size is kept in check by checkpoints
- WAL has multiple uses:
- crash recovery
- backup
- replication
Page 15: Practice
- Using the OS tools, find the processes responsible for the buffer cache and the WAL. 2. Stop PostgreSQL in fast mode; start it again. Check the server message log. 3. Now stop PostgreSQL in immediate mode; start it again. Check the server message log and compare with the previous one.
Speaker notes
- To stop in fast mode, use the command
This makes the server abort all open connections and perform a checkpoint before shutting down, so that all data is flushed to disk and consistent. In this mode, the shutdown may take some time, but on startup the server will be good to go right away.
- To stop in immediate mode, use the command
The server will also abort open connections, but will not perform a checkpoint. Data on disk will be inconsistent, like after a crash. In this mode, the server shuts down quickly, but will have to restore data consistency using WAL on startup.
If your PostgreSQL is compiled from source code, the fast stop command will be
and the immediate stop command will be
Page 16: 1. Operating System Processes
First, get the postmaster process ID. It is stored in the first line of postmaster.pid. This file is located in the data directory and is created every time the server starts.
Now, find all the processes spawned by postmaster:
The processes that serve the buffer cache and the WAL include: checkpointer background writer walwriter
2. Stop in Fast Mode
To easily separate old messages from new ones, clear the message log before restarting the server. Do not do this on a real production server.
Server message log:
3. Stop in Immediate Mode
Page 17: Page 17
Server message log:
Before starting to accept connections, the DBMS performed an automatic recovery.
08 Lesson 08: Data Organization / Databases and Schemas
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_08_data_databases.html Archive: DBA1-handouts-16-en.zip PDF pages: 24
Page 1: Data Organization / Databases and Schemas
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Aleksei Beresnev Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is,” and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- Databases and Templates
- Schemas and Search Path
- Special Schemas, Temporary Objects
- Managing Databases, Schemas, and Objects
Page 3: Database Cluster
{#source-figure-003 caption=“Source slide page 3: Database Cluster”}
Cluster initialization creates three databases
- A new database is always cloned from an existing one
Speaker notes
A PostgreSQL instance manages a database cluster which comprises multiple databases. When a cluster is initialized, three identical databases are created in a specific way.
https://postgrespro.com/docs/postgresql/16/bki
All other databases created by users are cloned from an existing one.
The template1 database is used by default when creating new databases. Any objects and extensions added to the template will be copied into any database created from it.
The template0 must never be modified. It is required in at least two scenarios. Firstly, it is necessary when restoring a database from a backup created via pg_dump (since the copy will include not only the database objects, but also any objects from template1). Secondly, it is used when creating a new database with an encoding different from the one specified during cluster initialization.
The postgres database is used to connect to by default by the postgres user. It is not a hard requirement to have it, but some utilities expect it to be there, so removing it is not a good idea, even if you never use the database directly.
https://postgrespro.com/docs/postgresql/16/manage-ag-templatedbs
Page 4: Databases
The \l[ist] command shows a list of all databases:
The list includes a fourth database, student, which is not created during cluster initialization. This database was specifically added for course purposes — it allows users to omit the database name when launching psql, as it defaults to a database matching the OS username. You can also view the list of databases in the database itself:
datistemplate — whether the database is a template datallowconn — whether database connections are allowed datconnlimit — maximum number of connections (-1 = unlimited)
Creating a Database from a Template
Connect to the template1 database:
Check if we can use the digest function, which calculates the hash code of a text string:
There is no such function. In fact, the digest function is defined in the pgcrypto extension. Install it:
Now, we can use the functions provided by the pgcrypto extension. For example, the MD5 digest calculation function:
Page 5: Page 5
In order to be used to create a database from, the template must have no active connections. So, disconnect from template1 first.
New databases are created with the CREATE DATABASE command:
A new database can also be created from the OS by using the createdb tool.
Since template1 is the template used by default, the newly created database will have the pgcrypto extension already installed:
Database Management
The created database can be renamed (must have no active connections):
Other parameters can also be changed, such as the maximum number of concurrent connections:
Page 6: Database Size
A database size is shown by the following function:
The output can be displayed in a more readable form:
The database has no user objects so far (except for pgcrypto), so this is the size of an “empty” database.
Page 7: Schemas
Database object namespace
- each object belongs to a schema
- Purposes
- dividing objects into logical groups
- prevent name conflicts between applications
- Schemas and users are different entities
Speaker notes
Schemas are namespaces for database objects. They separate objects into groups for easier management and serve to prevent name conflicts when multiple users or applications access the same database.
Every object that exists in the database belongs to a schema.
In PostgreSQL, schema and user are different entities (although the default settings allow users to conveniently operate schemas of the same name).
https://postgrespro.com/docs/postgresql/16/ddl-schemas
Page 8: Databases and Schemas
{#source-figure-008 caption=“Source slide page 8: Databases and Schemas”}
Speaker notes
A cluster comprises multiple databases. Each database contains various schemas across which database objects are distributed.
There are several standard schemas that exist in any database. More schemas can be added by users.
Clients may connect to only one database at a time, but within the database, the client can work with objects in any schema.
Page 9: Schemas
The psql command (dn = describe namespace) shows a list of schemas in a database:
Create a new schema:
Now create a table (it will get into the public schema by default):
You can get a list of tables with the \dt command:
Objects can be moved between schemas. Since this is the logical level, the moving takes place only in the system catalog; physically, the data remains in place.
Now, the table t can be accessed using its schema name:
Without the schema name, the table is not found.
So, how do you access objects located in various schemas?
Page 10: Page 10
Page 11: Search Path
Determining an object’s schema
- a schema is explicitly defined by a qualified name (schema.name)
- a name without a qualifier is looked up in the schemas specified in the search
- path
- Search path
- defined by the search_path parameter
- non-existent schemas and schemas with restricted access are excluded;
- implicit schemas are included
- the actual value is shown by the current_schemas function
- the first explicitly specified schema in the path is where objects are created
Speaker notes
When specifying an object, the schema it belongs to must be determined, since different schemas may contain objects of the same name.
When the object name is qualified (with the schema name), the object is looked up in the given schema, as shown on the previous slide. If the name is used without a qualifier, PostgreSQL tries to look the name up in one of the schemas listed in the search path, which is determined by the search_path configuration parameter.
The actual search path may differ from the search_path parameter value: any non-existent schemas are excluded, as well as schemas to which the user does not have access (see the Access Control module for details). In addition, some special schemas are implicitly added to the beginning of the search path.
The actual search path, including implicit schemas, is returned by a call to the current_schemas(true) function. The schemas are searched in the order specified in the search path, from left to right. If the desired object name is not found in the first schema, the next one is searched, and so on.
When an object is created with an unqualified name, it is placed into the first explicitly defined schema in the path.
The concept of search_path is similar to the PATH variable in operating systems.
https://postgrespro.com/docs/postgresql/16/ddl-schemas#DDL-SCHEMAS-P ATH
https://postgrespro.com/docs/postgresql/16/runtime-config-client#GUC-SEA RCH-PATH
Page 12: Special Schemas
Public schema
- included in the search path by default
- all objects will belong to this schema, unless configured otherwise
- Schema named after the user
- included in the search path by default but is not created automatically
- if created, the user objects will belong to this schema
- pg_catalog schema
- a schema for system catalog objects
- if not explicitly included in the path, implicitly included as the first one
Speaker notes
There are several special schemas usually present in any database.
The public schema is used by default for storing objects, unless intentionally configured otherwise.
The pg_catalog schema contains system catalog objects. The system catalog is a collection of tables containing metadata about objects belonging to the cluster. information_schema is another schema with an alternative representation of the system catalog (as defined in the SQL standard).
If pg_catalog is not specified in the search path, the schema will be implicitly placed in the front of the path so that system objects remain visible.
Page 13: Search Path
First, let’s find out why the table was created in the public schema. To do that, look at the search path:
Construction “$user” defines the schema with the same name as the current user (in this case, student). Since there is no such schema, it is ignored, and the table is created in public. To avoid the headache of tracking which schemas exist, which do not, and which are not explicitly declared, use the following function:
When the function is called with a true argument, the result includes system schemas (e.g., pg_catalog). We can define the search path, for example:
Now, the table t will be found:
We have just set a configuration parameter at the session level. If we restart the session, it will have reverted to default. Setting it on the cluster level would not be a good solution either, since this path may not be needed often and not by all users. Thankfully, you can set such parameters for a specific database:
Now, it will be applied for all new connections to appdb. Let’s try:
Another function current_schema() returns the first non-system schema name. This identifies the regular schema where new objects will be created by default.
Page 14: Page 14
Page 15: Special Schemas
Temporary tables
- exist for the duration of the session or transaction
- not logged (no recovery after a crash)
- do not utilize the shared buffer cache
- pg_temp_N schema
- created automatically for temporary tables
- pg_temp: a link to a specific temporary schema for the session
- if not explicitly included in the path, implicitly included as the very first one
- at the end of the session, all objects of the temporary schema are dropped;
- the schema itself remains and is reused for other sessions
Speaker notes
PostgreSQL can work with temporary tables. Temporary tables store data that should only be available to the current session and only for as long as the session is active (or even for the duration of a single transaction within the session).
When recovering after a crash, previous sessions do not exist, so temporary tables contents will be lost. Therefore, such tables are unlogged. Additionally, temporary table pages are never stored in the shared buffer cache, residing in the internal memory of their backend process instead. Thanks to that, temporary tables can be accessed a bit more quickly than regular ones.
Temporary tables are organized using schemas. When a session is started, a temporary schema named pg_temp_N (pg_temp_1, pg_temp_2, etc.) is created for it. It can be accessed by the name pg_temp (without a number, the name will always refer to the temporary schema specific for the session).
If pg_temp is not specified in the path, it is searched before all others. Otherwise, you can specify its position in the path explicitly, like with pg_catalog.
After the session ends, all objects belonging to the temporary schema are dropped, and the schema itself remains to be reused later on.
There are other special schemas, more technical in nature.
Page 16: Temporary Tables and pg_temp
Create a temporary table:
The table is created in a special schema. Each session has its own temporary schema, so that sessions cannot access each other’s temporary schemas. But, where did the regular table t disappear? The answer lies in the extended search path. Now, the search path includes the temporary schema, and the object in it “overshadows” the object in the app schema by the same name.
Still, you can access objects in either schema by providing the schema name explicitly. For the temporary table, use the pseudo-schema pg_temp: it will automatically transform to the current session’s pg_temp_N:
Temporary schemas are not limited to just tables.
Temporary schemas and data in them may have different scopes of life (depending on the clauses ON COMMIT DELETE, PRESERVE, and DROP). In any case, all temporary objects are deleted upon reconnecting to a database:
Page 17: Deleting Objects
A schema cannot be deleted as long as any objects remain in it:
But you can delete a schema together with all its objects:
If the database is no longer needed, it can be deleted as well.
Page 18: Takeaways
On the logical level
- cluster contains databases,
- database contains schemas,
- schema contains specific objects (tables, indexes etc.)
- Databases are created by cloning existing ones
- A schema can be specified explicitly or determined using the search path
- Some schemas have specific purposes
Page 19: Practice
- Create a new database and connect to it. 2. Check the size of the created database. 3. Create two schemas. Name one app, and the other after your user name. Create several tables in both schemas and populate them with data. 4. Check how much the database size has increased. 5. Modify the search path variable value so that when connecting to the database, tables from both schemas are accessible by an unqualified name. The “username” schema should have priority.
Page 20: 1. Database
2. Database Size
Save the value in a psql variable:
3. Schemas and Tables
Which schema receives tables created without explicit schema qualification?
student schema tables:
app schema tables:
Page 21: 4. Database Size Change
The size has changed to:
5. Search Path
With the current search path configuration, only student schema tables are visible:
Modify the search path:
Now, tables from both schemas are visible, but student takes priority:
Page 22: Page 22
Page 23: Practice+
- Create a database. For all sessions connecting to this database, set the temp_buffers parameter to four times the default value.
Speaker notes
- Use the command ALTER DATABASE … SET:
https://postgrespro.com/docs/postgresql/16/sql-alterdatabase
More about the temp_buffers parameter:
https://postgrespro.com/docs/postgresql/16/runtime-config-resource#GUC-T EMP-BUFFERS
Page 24: 1. Setting the temp_buffers Parameter
The temp_buffers parameter determines the amount of memory allocated for the local cache for temporary tables during any session. If the temporary tables data does not fit into temp_buffers, the pages are evicted, like with the regular buffer cache. Too low of a value may affect the server performance if temporary tables are used frequently. The default temp_buffers value is 8MB:
Set the value to 32MB for all new sessions:
The parameter changes made by ALTER DATABASE are stored in the pg_db_role_setting table. You can view them in psql with the following command:
Naturally, you do not have to set the temp_buffers parameter just at the database level. For example, you can set them for the whole cluster if you define it in postgresql.conf.
09 Lesson 09: Data Organization / System Catalog
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_09_data_catalog.html Archive: DBA1-handouts-16-en.zip PDF pages: 18
Page 1: Data Organization / System Catalog
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Aleksei Beresnev Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is,” and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- What Is the System Catalog and how to Access It
- System Catalog Objects and their Location
- Object Naming Rules
- Special Data Types
Page 3: System Catalog
A set of tables and views describing all objects in a database cluster
- Schemas
- primary schema: pg_catalog
- alternative representation: information_schema (SQL standard)
- SQL access
- view: SELECT
- change: CREATE, ALTER, DROP
- psql access
- commands for convenient data visualization
Speaker notes
The system catalog is a collection of tables and views that describe all database objects. It is metadata for the contents of the cluster: https://postgrespro.com/docs/postgresql/16/catalogs
Starting from version 14 of PostgreSQL, primary keys and unique constraints have been added for most system catalog tables.
You can access this metadata using regular SQL queries. SELECT commands can give you a description of an object, and DDL (Data Definition Language) commands let you add and modify objects.
All system catalog tables and views are located in the pg_catalog schema. There is another schema, as defined by the SQL standard: information_schema. It is more stable and portable than pg_catalog, but does not reflect specific features of PostgreSQL.
Client programs can read the contents of the system catalog and display it to the user in a convenient way. For example, GUI-based development and management environments usually come with a hierarchical object navigation tool.
The psql client also offers a number of convenient built-in commands specifically designed for working with the system catalog. Most of these commands start with \d (as in “describe”). For the full list of commands and their descriptions, see: https://postgrespro.com/docs/postgresql/16/app-psql#APP-PSQL-META-CO MMANDS
We will look at the most commonly used ones in the demo. The course materials also include the catalogs.pdf file that features a diagram of the main system catalog tables and related psql commands.
Page 4: Cluster-Level Objects
{#source-figure-004 caption=“Source slide page 4: Cluster-Level Objects”}
Speaker notes
In a database cluster, each database has its own set of system catalog tables. However, there are several system catalog objects that are shared between all cluster databases. The most obvious example is the list of the databases themselves.
These tables are stored outside of any single database, but at the same time they are accessible from any database within the cluster.
Page 5: Naming Rules
Object (table, view) and column name prefixes
- column prefix
- (usually derived from
- common prefix
- for all objects
- the object name)
- Object names are always lowercase
Speaker notes
All system catalog tables and views begin with the prefix pg_. In order to avoid potential conflicts, it is not recommended to create your own objects starting with pg_.
Column names have a three-letter prefix, which is usually derived from the name of the table. There is no underscore after the prefix. There are some exceptions to this rule, such as the oid column and others.
Object names are always stored in lowercase.
Page 6: Specific pg_catalog Objects
Create a database and some test objects:
We are familiar with some of the system catalog tables from the previous lesson. This is databases:
And schemas:
pg_class is an important table that stores descriptions for multiple types of objects: tables, views, indexes, sequences. All these objects in PostgreSQL are called relations, thus the prefix “rel” in the column names:
Page 7: Page 7
The object type is determined by the relkind column, while relpersistence distinguishes temporary objects from permanent ones. When actively using temporary objects, the system catalog tables accumulate numerous obsolete row versions, which may degrade query performance at all query processing stages. In such cases, timely catalog table vacuuming becomes essential. Naturally, only a subset of columns in pg_class are relevant for each object type. Moreover, examining object names (rather than identifiers like relnamespace, relowner, etc.) is more practical. For this purpose, there are specialized system views such as:
Using psql
psql has a set of built-in commands for obtaining information from the system catalog. These short commands are more convenient than making direct queries to system tables and views. A list of all tables is obtained with the command:
This command returns a list of all views in the public schema:
List of tables, views, indexes, and sequences:
Page 8: Page 8
Appended with the + modifier, these commands will return more detailed data:
To get detailed information about a specific object, use the \d command (without any additional letters):
The + modifier still works:
You can use the command not only on relations, but other objects as well, such as schemas (\dn) and functions (\df). The S modifier makes the command display system objects in addition to user ones. You can use wildcard patterns to filter the output:
Usually, such psql commands have mnemonic names. For example, \df is describe function, \sf is show function:
Page 9: Exploring the System Catalog Structure
You can get the full list of commands from the documentation or with the psql \? command. All psql commands that describe objects query system catalog tables. To view these queries, set the psql variable ECHO_HIDDEN. For example, to examine the employees table structure:
Most system catalog tables feature primary keys (typically the oid column) and uniqueness constraints. As an example, the pg_attribute table (containing relation attribute information) implements these constraints:
Page 10: Page 10
Referential integrity is maintained through foreign-key-like constraints with additional complexity: the referencing column may be an array of elements, zero may represent undefined references. The pg_get_catalog_foreign_keys() function lists these pseudo-foreign keys. For instance, pg_attribute references:
fktable, fkcols — referencing table and its columns pktable, pkcols — key referenced is_array — whether the referencing column is an array is_opt — whether the referencing column can contain 0
Page 11: Special Data Types
oid type – object identifier
- oid column ensures object uniqueness in system catalog tables
- integer with an auto increment
- reg* types
- oid aliases for some system catalog tables
- (regclass for pg_class, etc.)
- converting the text name of an object to the oid type and vice versa
Speaker notes
Most system catalog tables use a column with oid name and data type of the same name as a primary key.
The oid (Object Identifier) type is a 32 bit integer (about 4 billion possible values) with an auto increment.
There are several special data types (in fact, oid aliases) starting with reg that are used to convert object names to oids and back.
https://postgrespro.com/docs/postgresql/16/datatype-oid
Page 12: oid and reg* Data Types
As shown before, table and view descriptions are stored in pg_class table, and column descriptions are in a separate pg_attribute table. So, to get a list of columns in a specific table, you need to join pg_class and pg_attribute:
Using reg* types, the query can be simplified by omitting the explicit access to pg_class:
Here, the string “employees” was converted to the oid type. Similarly, oid can be displayed as a text value:
A list of all reg* types:
Page 13: Takeaways
The system catalog contains cluster metadata stored within the cluster itself
- SQL access and additional psql commands
- Some system catalog tables are stored in databases, some are shared across the entire cluster
- The system catalog uses special data types
Page 14: Practice
- Get a description of the pg_class table.
- Get a detailed description of the pg_tables view.
- Create a database and a temporary table in it. Get a complete list of schemas in the database, including system schemas.
- Get a list of views in the information_schema.
- What queries does the following psql command perform? \d+ pg_views
Page 15: 1. Description of pg_class
2. Detailed Description of pg\_tables
Page 16: 3. List of All Schemas
Temporary tables are stored in schemas named pg_temp_N, where N is a number. Such schemas are created for each session where temporary objects appear, so there can be multiple schemas. To get the name of the schema for the current session, use the following system function:
In general, the exact name of the schema is not required: you can access temporary objects in your session by using just pg_temp:
We already know what some of the schemas are there for, and we will learn more about the rest (pg_toast*) in a later lesson.
4. Get a List of Views in the information\_schema
Use the template:
Page 17: Page 17
5. Queries to the System Catalog
To see the queries psql runs, use the ECHO_HIDDEN variable:
Page 18: Page 18
psql ran five queries to display this result.
10 Lesson 10: Data Organization / Tablespaces
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_10_data_tablespaces.html Archive: DBA1-handouts-16-en.zip PDF pages: 14
Page 1: Data Organization / Tablespaces
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Aleksei Beresnev Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is,” and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- Tablespaces and Directories
- Creating, Modifying, and Deleting Tablespaces
- Storing Data in the File Hierarchy
- Moving Data
Page 3: Tablespaces
{#source-figure-003 caption=“Source slide page 3: Tablespaces”}
Speaker notes
Tablespaces are used to organize the physical storage of data and determine the location of data in the file hierarchy.
For example, one tablespace can use slow disks for archived data, and another one can use fast disks with frequent activity.
On cluster initialization, two tablespaces are created: pg_default and pg_global.
A tablespace can be used by multiple databases, and a database can use multiple tablespaces at once.
Each database has a default tablespace where all database objects are created (unless specified otherwise). System catalog objects are also stored in the default tablespace. Databases will use the pg_default tablespace as their default, unless another one is set by the user.
The pg_global tablespace is special as it stores only those objects that are shared across the whole cluster.
https://postgrespro.com/docs/postgresql/16/manage-ag-tablespaces
Page 4: Directories
{#source-figure-004 caption=“Source slide page 4: Directories”}
Speaker notes
Essentially, a tablespace is a reference to the directory in which the data is located. The standard tablespaces pg_global and pg_default are always located in PGDATA/global/ and PGDATA/base/, respectively. When a custom tablespace is created, an arbitrary catalog can be specified. For its convenience, PostgreSQL also creates a symbolic link to the directory in PGDATA/pg_tblspc/.
The PGDATA/base/ directory comprises different directories for each database (unlike PGDATA/global/, which stores data referring to the whole cluster).
Inside a custom tablespace directory, there is another level for different PostgreSQL server versions. This is helpful during server upgrade.
Actual objects are stored in files in these directories, one or more files per object.
Page 5: System Tablespaces
Upon a cluster initialization, two tablespaces are created:
pg_global — shared cluster objects pg_default — default tablespace
Custom Tablespaces
A new tablespace needs an empty directory owned by the user postgres.
Now, a new tablespace can be created:
The following psql command returns a list of all tablespaces:
Each database has a “default” tablespace. Let’s create a database and assign ts as its default:
This makes all tables and indexes created within the database fall into ts, unless specified otherwise. Connect to the database:
Create a table:
When creating objects, you may explicitly specify a tablespace for it:
Page 6: Page 6
An empty tablespace field means that the default tablespace is used. The second table has this field filled in. You can also specify a tablespace for an index:
Another way to assign a tablespace without defining it at object creation is to preemptively set a value of default_tablespace parameter. A single tablespace may contain objects from multiple databases.
This database’s default tablespace will be pg_default.
For temporary tables and their indexes, you can specify a separate tablespace as the default:
The temp_tablespaces parameter accepts multiple tablespace names. When set, the server selects one randomly for each temporary object.
Managing Objects within Tablespaces
Tables (and other objects, such as indexes) can be moved between tablespaces.
Page 7: Page 7
Indexes can also be moved to a different tablespace during rebuild:
You can move all objects from one tablespace to another:
Keep in mind that moving objects between tablespaces (unlike moving between schemas) is a physical operation that involves moving actual files from one directory to another. Access to the objects being moved is completely blocked for the duration of the operation.
Tablespace Size
We already know how to get the size of a database. Now, we can learn how to get the size of objects in a tablespace:
Why is the size so large, despite the tablespace containing just a few empty tables? This is because ts is the default tablespace for the database appdb, so it is where the system catalog objects are stored. They take up space. You can get the size of the tablespaces by the psql command:
Dropping a Tablespace
You can only delete a tablespace that is empty:
Page 8: Page 8
Unlike with schemas, there is no CASCADE clause in the DROP TABLESPACE command. Objects within the tablespace may belong to multiple databases, while we are only connected to one. You can still figure out what databases contain dependent objects. This is where the system catalog comes in. First, find out and remember the tablespace ID:
Next, get a list of databases that have objects in the tablespace we are going to remove:
Then, connect to each of the databases and get a list of objects from pg_class:
The table is no longer needed, drop it.
Now, for the second database. Since ts is the default tablespace, the tablespace ID of the objects in pg_class equals zero. These are the system catalog objects, as we already know:
You can set another tablespace as default. This will physically move all the tables into the new one. You need to disconnect from the database first.
Finally, the tablespace can be deleted.
Page 9: Page 9
On behalf of the postgres user, we remove the directory created for the tablespace.
Page 10: Takeaways
Tablespaces organize physical data storage
- Logical (databases, schemas) and physical (tablespaces) forms of data organization are independent
Page 11: Practice
Why does pg_default become the default tablespace when creating a database without the TABLESPACE keyword?
- Create a new tablespace.
- Set it as the default tablespace for the template1 database.
- Create a new database. Check which default tablespace is set for the new database.
- Find the symbolic link to the tablespace directory in PGDATA.
- Delete the created tablespace.
Page 12: 1. New Tablespace
2. Default Tablespace for template1
3. New Database, Verification
The default tablespace is ts. Conclusion: the default tablespace is determined by the template from which the new database is cloned.
4. Symbolic Link
5. Dropping the Tablespace
Page 13: Practice+
- Set the random_page_cost parameter for the pg_default tablespace to 1.1.
Speaker notes
- Use the ALTER TABLESPACE … SET command:
https://postgrespro.com/docs/postgresql/16/sql-altertablespace
The seq_page_cost and random_page_cost parameters are used by the planner. They refer to the approximate cost of reading one page of data from disk with sequential and random access, respectively.
The lower the ratio between these parameters, the more often the planner will prefer index access over sequential table scanning.
The parameters *_cost and, more specifically, random_page_cost are discussed in more detail in the Query Optimization course (QPT).
Page 14: 1. Setting Random_page_cost for a Tablespace
The default seq_page_cost and random_page_cost values are suitable for slower HDD drives. It is assumed that random page access is four times as costly as sequential page access:
If you use drives with different properties, you can create different tablespaces with appropriate seq_page_cost and random_page_cost values for each. For example, quick SSD drives can have the random_page_cost value almost as low as seq_page_cost.
Configuration adjustments made using the ALTER TABLESPACE command are stored in the pg_tablespace table. You can view them in psql with the following command:
The *_cost parameters can also be set in postgresql.conf. This will apply them to all tablespaces.
11 Lesson 11: Data Organization / Low Level
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_11_data_lowlevel.html Archive: DBA1-handouts-16-en.zip PDF pages: 20
Page 1: Data Organization / Low Level
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Aleksei Beresnev Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is,” and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- Data Files
- Forks: Main, Visibility Map, Free Space Map
- Oversized Row Versions and TOAST
Page 3: Object Forks
{#source-figure-003 caption=“Source slide page 3: Object Forks”}
Speaker notes
Usually, each database object that stores data (table, index, sequence, materialized view) has several corresponding forks. Each fork contains a specific type of data.
Initially, each fork contains a single file. The file name is a numeric identifier and may include a suffix derived from the fork name.
The file gradually increases in size until it reaches 1 GB, at which point the next file for the same fork is created. Such files are sometimes called segments.The segment sequence number is appended to the end of the file name. The pg_relation_size function displays the total size of a fork.
The 1 GB file size limit was established in the past to support file systems that cannot operate with larger file sizes. A different file size limit can be set during source code compilation with the –with-segsize option.
So, a single database object may consist of multiple files on disk. A small table will have three corresponding files on disk, and an index will have two. All object files belonging to the same tablespace and the same database are stored in the same directory. This may become an issue as some file systems may perform poorly on directories with a large number of files.
Page 4: Forks
Main fork
- actual data (row versions)
- exists for all objects
- Initialization fork (init)
- “template” of the main fork
- used in case of failure; exists only for unlogged tables
- Visibility map (vm)
- exists only for tables
- Free space map (fsm)
- exists for both tables and indexes
Speaker notes
There are several types of forks.
The main fork contains the data itself, namely table row versions and index records. The main fork file names match the identifier. All objects have a main fork.
The file names of the initialization fork end with the _init suffix. This fork exists only for unlogged tables (created with the UNLOGGED clause) and their indexes. Such objects do not differ from regular ones, except that actions performed on them are not logged in WAL. This makes operations on them faster, but their content cannot be recovered if a failure occurs. During a recovery PostgreSQL just removes all the forks of such objects and writes the initialization fork in place of the main fork. The result is an empty table.
https://postgrespro.com/docs/postgresql/16/storage-init
The vm (visibility map) fork’s filenames end in _vm. The fork exists only for tables, separate MVCC for indexes is not supported.
The fsm (free space map) fork’s filenames end in _fsm. This fork exists for both tables and indexes.
These two maps were discussed in the Architecture module.
https://postgrespro.com/docs/postgresql/16/storage-fsm
https://postgrespro.com/docs/postgresql/16/storage-vm
Page 5: File Locations
Create a table and look where its files are.
To form additional forks, we will execute vacuuming:
The path to the main file relative to PGDATA is shown with the following function:
Since the table is located in the pg_default tablespace, the path starts with “base”, followed by the database directory:
Then follows the file name. You can get it using the query:
But the pg_relation_filepath function is more convenient because it returns the full path so that you don’t need to run several queries on the system catalog. Let’s have a look at the files themselves. Only the OS user postgres has access to PGDATA, so run the ls on their behalf:
There are three forks: the main fork, the free space map (fsm) and the visibility map (vm). You can view the index files in a similar way:
Page 6: Page 6
And files for the sequence that has been created for the primary key:
For an index, the free space map is built only when empty pages exist, while a sequence has only a main fork. Temporary tables are stored in the same way as permanent tables.
A prefix matching the schema number is added to the filename for temporary objects.
The oid2name additional supplied extension lets you quickly and easily find out which database objects relate to which files. You can view all databases:
All objects in a database:
Page 7: Page 7
All tablespaces in a database:
Find the file name by table name:
Or the table name by file name:
Fork Sizes
You can get the size of the files that comprise a fork from the file system, but there is an easier way to get the size of each fork individually:
Page 8: TOAST
A row version must fit into one page
- some fields can be compressed
- some fields can be moved into a TOAST table
- fields can be both compressed and moved
- TOAST table
- located in the pg_toast (pg_toast_temp_N) schema
- supported by its own index
- contains chunks of oversized values, each chunk is smaller than a page
- retrieved only when the oversized field is accessed
- has its own row versions
- used transparently for the application
Speaker notes
Any row version in PostgreSQL must fit entirely into one page. Oversized row versions are stored using TOAST, The Oversized Attributes Storage Technique. TOAST comprises several approaches to storing oversized field values. Firstly, the value can be compressed so that the row version fits into the page. Secondly, the value can be moved from the row version to a separate service table. Both strategies can be applied to the same row versions: some values would be compressed, some moved, some compressed and moved.
Any table can have a separate TOAST table (with a dedicated index) created for it, if necessary. Such tables and indexes are located in a separate schema named pg_toast and, therefore, are usually not visible (for temporary tables, pg_toast_temp_N schema is used, similarly to the regular pg_temp_N).
The row versions in the TOAST table must also fit into one page each, so longer values are split into multiple chunks, and are transparently “glued together” by PostgreSQL when the application demands.
TOAST table is used only when oversized values are accessed. Besides that, TOAST tables have their own row versions. Whenever a data update in the main table does not affect the oversized value, the new row version will refer to the same TOAST value, saving disk space.
https://postgrespro.com/docs/postgresql/16/storage-toast
Page 9: TOAST
The table t has a numeric type column. This type can hold very large numbers. For example:
However, when inserted into the table, this humongous value does not change the table size:
Since the row version does not fit into a single page, the value of attribute n is stored in a separate TOAST table. TOAST tables and their indexes are created automatically for all tables that include potentially “oversized” data types and are used as needed. You can find the name and oid of the TOAST table:
And here are the TOAST table files:
When it comes to oversized values, there are several strategies that can be employed. The name of the current strategy is listed in the Storage column:
plain — TOAST is not used (type has a fixed length)
Page 10: Page 10
extended — both compression and out-of-line storage are used external — compression is not used, only out-of-line storage main — such fields are processed last and are moved to the toast table only if compression is not enough A storage strategy is assigned for each column when creating a table. It can be specified explicitly, and the default value depends on the data type. If necessary, the strategy can be modified later. For example, if you know that data in a column is already compressed, you can switch the strategy to external. For example:
This operation does not change the existing data, but defines the strategy to be used for new row versions.
Page 11: Table Size
{#source-figure-011 caption=“Source slide page 11: Table Size”}
Speaker notes
As already mentioned, the size of a single fork can be obtained by the pg_relation_size function. To get the total object size, other functions can be used:
● pg_table_size shows the size of the table and its TOAST part (the TOAST table and its index), but not the regular index sizes. The same function can be used to find the size of an individual index: both tables and indexes are relations, and despite the name, the function accepts any relation as an input.
● pg_indexes_size sums up the sizes of all table indexes except the TOAST table index.
● pg_total_relation_size shows the full size of the table, along with all its indexes.
Page 12: Table Size
The size of a table (including the TOAST table and its index):
Total size of all table indexes:
You can get the size of a single index by using the pg_table_size function. Indexes have no TOASTs, so the function only shows the size of all index forks (main, fsm). Currently, the table t has just the primary key index, so its size matches the size returned by pg_indexes_size:
Total table size, including TOAST and all indexes:
Page 13: Takeaways
An object comprises several forks
- A fork consists of one or more segment files
- Oversized row versions are stored using TOAST
Page 14: Practice
- Create an unlogged table in a custom tablespace and make sure that it has an init fork.
- Delete the created tablespace. 2. Create a table with a column of the text type. What storage strategy is used for this column?
- Change the strategy to external and insert a short and a long row
- into the table.
- Check if the rows are in the TOAST table by making a direct
- query to it. Explain why.
Page 15: 1. Unlogged Тable
Let’s look at the table files.
Note Note that the ls command is executed on behalf of the postgres user. You can open a second terminal window and switch to another user with the following command:
Now, in the same window, run:
Drop the created tablespace:
2. Table with a Text Column
Dy default, the extended strategy is used for text data.
Page 16: Page 16
Change the strategy to external:
Check the TOAST table:
The TOAST table is “hidden”, because it is located in a schema that is excluded from the search path. This is a good thing, because TOAST is intended to work transparently for the user. However, there still are ways to view the table:
Only the long string went into the TOAST table (two chunks, total size matches the string size). The short string wasn’t TOAST’ed: there is no need, as it fits into one page.
Page 17: Practice+
- Create a database.
- Compare the database size returned by the pg_database_size
- function with the total size of all tables in the database.
- Explain the result. 2. TOAST supports two compression methods: pglz and lz4. Use SQL to check whether PostgreSQL was compiled with these methods support. 3. Create a text file of at least 10 MB size. Load its contents into a table with a text field, first without compression, and then using each of the algorithms. Compare the final table size and the data loading time of all three options.
Speaker notes
You can get the list of database tables from the pg_class table.
Using the pg_config view, you can find out which options were set for the configure script when the server software was compiled. The string containing the list of options is long; you can extract the necessary options using the string_to_table function.
To obtain text for the experiment, you can take a sufficiently large binary file (for example, the postgres executable) and convert it to text. For the conversion, you can use the Base32 algorithm (the -w0 option disables line breaks): base32 -w0 < binary-file > text-file
Page 18: 1. Comparing the Size of a Database to the Total Size of its Tables
Even an empty database contains some system catalog tables. The list of all relations is stored in pg_class. Exclude from the calculation: Cluster-wide tables (they do not belong to the current database) Indexes and toast tables (they are automatically taken into account when calculating the size)
The size of the database is a bit larger:
This is because the pg_database_size function returns the size of the catalog in the file system, and the catalog contains some service files.
Note Note that the ls command is executed on behalf of the postgres user. You can open a second terminal window and switch to another user with the following command:
Now, in the same window, run:
pg_filenode.map — mapping oid of some tables to file names; pg_internal.init — system catalog cache; PG_VERSION — PostgreSQL version. As some functions operate on the database object level, and others on the file system level, it is sometimes hard to compare the results directly. The same goes for the pg_tablespace_size function.
2. TOAST Compression Methods Support
The pg_config view displays options passed to the configure script during PostgreSQL compilation.
Page 19: Page 19
Which TOAST compression method is used by default?
What methods are available?
3. Comparison of Compression Methods
Let’s compare compression methods using text data as an example. To obtain a large text volume, we take the postgres executable file and convert it to text using the Base32 algorithm (commonly used in email encoding).
The resulting text file is sufficiently large.
We create a table to load the text data. For the txt column, we set the external storage strategy, which allows out-of-line storage but prohibits compression.
Next, we load the data from the text file.
We check the table size, including TOAST storage.
After emptying the table, we activate compression using pglz.
Page 20: Page 20
Now, the extended strategy is applied, allowing both compression and out-of-line storage. We reload the data.
The table size is significantly reduced, but loading time has been increased noticeably. After clearing the table again, we set lz4 compression.
We reload the data once again and compare the results.
The lz4 algorithm provides slightly worse compression than pglz but operates much faster. Finally, we delete the text file.
12 Lesson 12: Administrative Tasks / Monitoring
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_12_admin_monitoring.html Archive: DBA1-handouts-16-en.zip PDF pages: 30
Page 1: Administrative Tasks / Monitoring
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Aleksei Beresnev Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is,” and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- OS Tools
- Сumulative Statistics
- Server Message Log
- External Monitoring Systems
Page 3: OS Tools
Processes
- ps, pgrep…
- update_process_title parameter for updating the status of processes
- cluster_name parameter for setting the cluster name
- Resource usage
- iostat, vmstat, sar, top…
- Disk space
- df, du, quota…
Speaker notes
PostgreSQL runs on an operating system and to a certain extent depends on its configuration.
PostgreSQL process information is accessible via OS tools. The server parameter update_process_title (on by default) displays the state of each process next to its title. The cluster_name parameter specifies the instance name used to identify it among running processes.
Various tools are available to monitor the use of system resources (CPU, RAM, disks) in Unix: iostat, vmstat, sar, top, etc.
Disk space monitoring is also necessary. The space occupied by the database on disk can be viewed both from the database itself (see the Data Organization module) and from the OS (with the du command). The amount of disk space available is also displayed with the df command in the OS. If disk quotas are used, they must also be taken into account.
The tools and approaches to monitoring differ significantly between various OS and file systems, so we will not discuss them in detail.
https://postgrespro.com/docs/postgresql/16/monitoring-ps
https://postgrespro.com/docs/postgresql/16/diskusage
Page 4: Сumulative Statistics
Statistics Collection
- Current System Activities
- Command Execution Monitoring
- Extensions
Speaker notes
There are two primary sources of information about the state of the system. The first one is statistical information collected by PostgreSQL and stored inside the cluster.
Page 5: Statistics Collection
Settings of cumulative statistics
- parameter
- action
- track_activities
- monitor current commands
- track_counts
- collect table and index access statistics
- track_functions
- monitor user function calls
- off by default
- track_io_timing
- monitor block read and write timing statistics
- off by default
- track_wal_io_timing
- monitor write timing of WAL operations
- off by default
Speaker notes
The cumulative statistics system in PostgreSQL collects and provides data on server operations. Cumulative statistics track access to tables and indexes at both the disk block and row levels. Additionally, they record details such as the number of rows, vacuum and analyze operations for each table.
It is also possible to track the number of user function calls and their execution time.
The amount of information collected is controlled by several server parameters, since the more information is collected, the greater the overhead.
https://postgrespro.com/docs/postgresql/16/monitoring-stats
Page 6: Architecture
{#source-figure-006 caption=“Source slide page 6: Architecture”}
Speaker notes
Backends collect statistics in running transactions. The process stores this data in shared memory, updating it at most once per second (compile-time setting).
Cumulative statistics are stored in PGDATA/pg_stat/ during a normal server shutdown and reloaded upon startup. In case of a crash shutdown, all counters are reset.
Backend may cache statistical data. The caching level is controlled by the stats_fetch_consistency parameter.
● none — no caching; statistics reside only in shared memory.
● cache — statistics for a single object are cached.
● snapshot — statistics for the entire database are cached.
Cache is the default mode, which balances consistency and performance efficiency.
Cached statistics are not refreshed and are discarded at the end of a transaction, or when pg_stat_clear_snapshot() is called.
Due to latency and caching, the backend will not always have the latest statistics, but it is seldom necessary.
Page 7: Cumulative Statistics
Enable collection of I/O statistics first:
Monitoring server activity only makes sense if there is any activity. We can imitate load with pgbench, a standard benchmarking utility. First, it creates a number of tables and fills them with data.
Let’s reset the previously accumulated database statistics.
Including instance I/O statistics:
Start the TPC-B test and let it run for a few seconds:
Page 8: Page 8
Now, let’s check the statistics on table access in terms of rows:
And in terms of tables:
There are similar views for indexes:
Page 9: Page 9
These views can be used to pinpoint unused indexes. Such indexes not only occupy useful space on the disk, but also waste resources on updates every time data in the table changes. There are also views for user-defined and system objects (all, user, sys), current transaction statistics (pg_stat_xact*), and more. You can view global statistics across the whole database:
It provides a lot of data on the number of deadlocks occurred, committed and cancelled transactions, utilization of temporary files, and checksum errors. It also maintains total session count and statistics on sessions terminated for various reasons. The numbackends column specifically indicates the current number of active backend processes connected to this database. For monitoring I/O operations at the server level, administrators can query the pg_stat_io view. Let’s first execute a checkpoint operation, then examine the resulting page read and write counts categorized by process type:
Page 10: Page 10
Page 11: Current Activities
Configuration
- statistics
- parameter
- current activities and waits
- track_activities
- of backends
- on by default
- and background processes
Speaker notes
The current activities of all backends and background processes are displayed in the pg_stat_activity view. We will focus on it more in the demo.
This view depends on the track_activities parameter (enabled by default).
Page 12: Current Activities
{#source-figure-012 caption=“Source slide page 12: Current Activities”}
Let’s imitate a scenario when one process blocks another, and then figure it out using system views. Create a table with one row:
Start two sessions, one of which changes the table and does not complete the transaction:
And the other tries to change the same row and gets blocked:
View data about backend processes:
The state “idle in transaction” means that the session has started a transaction, but isn’t doing anything at the moment, and the transaction isn’t closed. This could become a problem if the situation comes up regularly (for example, because of poor application code or driver errors), because an open transaction holds a data snapshot and prevents vacuuming. The administrator has a parameter idle_in_transaction_session_timeout at their disposal to force sessions to close after their transaction is idle for a certain period of time. The idle_session_timeout parameter forcibly terminates sessions that remain idle beyond the specified duration. You can also terminate a blocking session manually. First, you need the blocked process ID. The function pg_blocking_pids can help you with that:
Page 13: Page 13
Instead, you can use a query on the locks table. It will return two rows in this case: one transaction has been granted the lock, while the other is waiting for it.
Generally, you have to keep the lock type in mind. Running query can be cancelled with the pg_cancel_backend function. The transaction is idle in our case, so we use the pg_terminate_backend to terminate the session:
The unnest function is necessary because pg_blocking_pids returns an array of process IDs that block the specified process. There is only one in our examples, but there can be multiple. Locks are discussed in more detail in the DBA2 course. Check the backends:
Only two remain, and the blocked one has completed its transaction successfully. The pg_stat_activity view shows the information not only about backend processes, but also about the system background processes running on the instance:
Page 14: Page 14
Compare that to what the OS reports:
Page 15: Command Execution
Views for monitoring command executions
- command
- execution
- ANALYZE
- pg_stat_progress_analyze
- CREATE INDEX, REINDEX
- pg_stat_progress_create_index
- VACUUM
- pg_stat_progress_vacuum
- including autovacuuming
- CLUSTER, VACUUM FULL
- pg_stat_progress_cluster
- Create base backup
- pg_stat_progress_basebackup
- COPY
- pg_stat_progress_copy
Speaker notes
You can monitor the progress of some potentially long-running commands using the corresponding views.
The structures of the views are described in the documentation:
https://postgrespro.com/docs/postgresql/16/progress-reporting
Backup is discussed in the Backup module.
Page 16: Additional Statistics
Additional supplied extensions
- pg_stat_statements
- query statistics
- pgstattuple
- row versions statistics
- pg_buffercache
- buffer cache status
- Other extensions
- pg_wait_sampling
- statistics for waits
- pg_stat_kcache
- CPU and I/O statistics
- pg_qualstats
- predicate statistics
- …
Speaker notes
There are extensions, both additional supplied and third-party, that enable the collection of additional statistics.
For example, the pg_stat_statements extension collects information about queries executed by the system, pg_buffercache provides tools for monitoring the buffer cache, etc.
Many key extensions are discussed in more detail in the DBA2 and DEV2 courses.
Page 17: Server Message Log
Log Record Configuration
- Log File Rotation
- Log Analysis
Speaker notes
The other primary source of information about the state of the server is the message log.
Page 18: Server Message Log
Message receiver (log\_destination = list)
- stderr
- error stream
- csvlog
- CSV format (if the collector is enabled)
- jsonlog
- JSON format (if the collector is enabled)
- syslog
- the syslog daemon
- eventlog
- Windows event log
- Message collector (logging_collector = on)
- can provide additional info
- never loses messages (unlike syslog)
- writes stderr, csvlog and jsonlog to the log_directory/log_filename
Speaker notes
The server log can be output in various formats and forwarded to various destinations. The format and the destination are determined primarily by the log_destination parameter (you can list multiple receivers separated by a comma).
The stderr value (on by default) sends messages to the standard error stream as plain text. The syslog value forwards messages to the syslog daemon (for Unix systems), and the eventlog value does the same for the Windows event log.
The message collector is an auxiliary process that collects additional information from all PostgreSQL processes to supplement the basic log messages. It is designed to keep track of every message, therefore it can become the bottleneck in high-load environments.
The message collector is switched on and off by the logging_collector parameter. When stderr is enabled, the messages are written into the file defined by the log_filename parameter, which is located in the directory defined by the log_directory parameter.
When the collector is on and csvlog is selected as a receiver, the info will also be output into a CSV file log_filename.csv. With the jsonlog output enabled, log files are written in JSON format and use the .json file extension.
Page 19: What to Log?
Settings
- information
- parameter
- level of messages
- log_min_messages
- long command execution time
- log_min_duration_statement
- command execution time
- log_duration
- application name
- application_name
- checkpoints
- log_checkpoints
- connections and disconnections
- log_(dis)connections
- long lock waits
- log_lock_waits
- command executed
- log_statement
- temporary file usage
- log_temp_files
- …
Speaker notes
A lot of useful information can be output to the server message log. By default, almost all output is disabled so as not to turn logging into the bottleneck for the I/O subsystem. The administrator should decide what information is important, provide the necessary disk space to store it, and evaluate the impact of the log output on the overall system performance.
Page 20: Log File Rotation
By the message collector
- setting
- parameter
- file name pattern
- log_filename
- rotation time, minutes
- log_rotation_age
- rotation file size, KB
- log_rotation_size
- overwrite file
- log_truncate_on_rotation = on
- combining file name patterns and rotation times allow for different rotation
- schemes:
- ‘postgresql-%H.log’, ‘1h’
- 24 files a day
- ‘postgresql-%a.log’, ‘1d’
- 7 files a week
- External tools
- logrotate system utility
Speaker notes
If all the log output goes into a single file, sooner or later the file will grow to an unmanageable size, making administration and analysis highly inconvenient. Therefore, a log rotation scheme is usually employed.
https://postgrespro.com/docs/postgresql/16/logfile-maintenance
The message collector has its own rotation tools. Some of the parameters that configure them are listed on the slide.
The log_filename parameter can specify not just a name, but a file name pattern using designated date and time characters.
The log_rotation_age parameter determines how long a file is used before the output switches to a new one (and log_rotation_size is the file size at which to switch to the next one).
The log_truncate_on_rotation flag determines if PostgreSQL should overwrite existing files or append messages to them.
Different rotation schemes can be defined by using various file name patterns and switch time combinations.
https://postgrespro.com/docs/postgresql/16/runtime-config-logging#RUNTIM E-CONFIG-LOGGING-WHERE
Alternatively, rotation can be managed by external tools. For example, Ubuntu package uses logrotate system utility (it is configured through the /etc/logrotate.d/postgresql-common file).
Page 21: Log Analysis
OS tools
- grep, awk…
- Special analysis tools
- pgBadger — requires a certain log configuration
Speaker notes
There are different ways to analyze logs.
You can search for certain information using OS tools or specially designed scripts.
The de facto standard for log analysis is the PgBadger application https://github.com/darold/pgbadger, but it imposes certain restrictions on the contents of the log. In particular, only messages in English are allowed.
Page 22: Log Analysis
Let’s consider a simple case. For example, display all messages of the FATAL level:
The “terminating connection” message is caused by us terminating the blocking process. Logs are usually used to analyse the queries that execute the longest. We can make the log display all executed commands and their execution times:
Now, run a command:
Check the log:
Page 23: External Monitoring
Universal monitoring systems
- Zabbix, Munin, Cacti…
- cloud-based: Okmeter, NewRelic, Datadog…
- PostgreSQL monitoring systems
- pg_profile, pgpro_pwr
- PGObserver
- PostgreSQL Workload Analyzer (PoWA)
- Open PostgreSQL Monitoring (OPM)
- …
Speaker notes
In practice, you need a full-fledged monitoring system that collects various metrics from both PostgreSQL and the operating system, stores the history of these metrics, displays them as readable graphs, notifies when certain metrics exceed certain limits, etc.
PostgreSQL does not come with such a system by itself, it only provides the means by which such information can be acquired. We have gone over them already. Therefore, for full-scale monitoring, an external system is required. There are quite a few such systems on the market. Some are universal and come with PostgreSQL plugins or agents. These include Zabbix, Munin, Cacti, cloud services such as Okmeter, NewRelic, Datadog, and others.
There are also systems specifically designed for PostgreSQL: PGObserver, PoWA, OPM, etc. The pg_profile extension allows you to build snapshots of static data and compare them, identifying resource-intensive operations and their dynamics. pgpro_pwr is its extended, commercially available version.
https://postgrespro.com/docs/enterprise/16/pgpro-pwr
An incomplete but representative list of monitoring systems can be viewed here: https://wiki.postgresql.org/wiki/Monitoring
Page 24: Takeaways
Monitoring collects data on server operations both from the operating system and from the database points of view
- PostgreSQL provides cumulative statistics and the server message log
- Full-scale monitoring requires an external system
Page 25: Practice
- In a new database, create a table, insert several rows, and then delete all rows.
- Look at the table access statistics and reference the values (n_tup_ins, n_tup_del, n_live_tup, n_dead_tup) against your activity. Perform a vacuum, check the statistics again and compare with the previous figures. 2. Create a deadlock with two transactions.
- See what information is recorded in the server message log.
Speaker notes
- Deadlock is a situation when two (or more) transactions are waiting for each other to complete first. Unlike a normal lock, transactions have no way to get out of deadlock, and the DBMS has to resolve it by forcibly interrupting one of the transactions.
The easiest way to reproduce a deadlock is on a table with two rows. The first transaction changes (and locks) the first row, and the second one locks the second row. Then the first transaction tries to change the second row, discovers that it is locked, and starts waiting. And then the second transaction tries to change the first row, and also waits for the lock to be released.
Page 26: Table Access Statistics
Create a database and a table:
Check access statistics.
We inserted 1000 rows (n_tup_ins = 1000), then removed 1000 rows (n_tup_del = 1000). No live row versions remain (n_live_tup = 0), all 1000 rows are dead (n_dead_tup = 1000). Run vacuuming.
Page 27: Page 27
Dead row versions vacuumed (n_dead_tup = 0), vacuuming performed one time (vacuum_count = 1).
2. Deadlocks
One transaction locks the first row of the table…
The other locks the second row…
Now, the first transaction tries to change the second row and waits for it to release…
While the second transaction waits for the first row to release…
…and so a deadlock occurs. The server terminates one of the transactions:
Page 28: Page 28
The other transaction gets unblocked:
Check the message log:
Page 29: Practice+
- Install the pg_stat_statements extension.
- Execute several queries.
- See what information gets into the pg_stat_statements view.
Speaker notes
- To install the extension, before executing the CREATE EXTENSION command, change the value of the shared_preload_libraries parameter, and restart the server.
https://postgrespro.com/docs/postgresql/16/pgstatstatements
Page 30: 1. The pg_stat_statements Extension
The extension collects planning and execution statistics for all queries. For the extension to work, a module with the same name has to be loaded. To do that, add the module name to shared_preload_libraries and restart the server. This is usually done through postgresql.conf, but for the purpose of the demo we will set it using the ALTER SYSTEM command.
Now, run some queries:
Check the statistics for the most frequently executed query.
The shared library is no longer required — restoring the original parameter value:
13 Lesson 13: Access Control / Overview
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_13_access_overview.html Archive: DBA1-handouts-16-en.zip PDF pages: 30
Page 1: Access Control / Overview
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Igor Gnatyuk Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is,” and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- Roles and Attributes
- Connecting to a Server
- Password Authentication
- Privileges and Privilege Management
- Role Categories
- Group and Predefined Roles
- Default Privileges
- Privileges and Routines
Page 3: Roles and Attributes
Role can be a DBMS user
- not associated with the OS user
- Roles can be included into other roles
- simplifies access setup
- Attributes define the properties of a role
- LOGIN
- can log in
- SUPERUSER
- superuser privileges
- CREATEDB
- can create databases
- CREATEROLE
- can create roles
- and others
Speaker notes
Roles in PostgreSQL are used for two purposes. Firstly, a role can be a DBMS user. Secondly, roles can be members of other roles — it is convenient when setting up access.
Formally, roles are not associated with operating system users in any way, but many programs imply it when choosing default values. For example, if psql is started on behalf of the student OS user, the connection is established on behalf of the database role with the same name, i.e., student (unless another role is explicitly specified in the psql options).
At the time of cluster initialization, an initial role is defined, which has superuser privileges (this role is usually called postgres). Later on, you can create, modify, and delete roles.
https://postgrespro.com/docs/postgresql/16/database-roles
A role has several attributes which define its general properties and rights (unrelated to particular objects).
Generally, attributes come in two opposite variations; for example, CREATEDB (can create databases) and NOCREATEDB (not allowed to create databases).
A role with the LOGIN attribute is considered a user role. A role with NOLOGIN cannot connect to the server and is typically used for grouping other roles.
The table lists only some of the possible attributes.
https://postgrespro.com/docs/postgresql/16/role-attributes
https://postgrespro.com/docs/postgresql/16/sql-createrole
Page 4: Roles and Attributes
Create a role for the user Alice. Two attributes are specified.
Note Note which role the commands are executed as. The name of the current role is in the prompt.
You can get the list of roles with the command:
Note Note that the student role is a superuser. This is why we did not have any access issues so far. Create a database:
Page 5: Connecting to a Server
1. The lines of pg\_hba.conf are searched from top to bottom
- The first line that corresponds to the provided connection settings (type, database, user, address) will be used
- local — socket all — any role host — TCP/IP role name
- listen_addresses
- all — any DB
- all — any IP
- database name
- IP/mask
- domain name
Speaker notes
For each new client, the server has to evaluate whether a database connection should be allowed. Connection settings are defined in the pg_hba.conf configuration file (hba stands for host-based authentication). As with the main configuration file (postgresql.conf), changes come into effect only after the server reloads this file, either via the pg_reload_conf() SQL function or the reload command of the management utility.
When a new client appears, the server reads the configuration file from top to bottom to find the line that matches the requested connection. The match is defined by four fields: connection type, database name, user name, and IP address.
Here we list only the main basic options.
Connection: local (unix sockets) or host (a TCP/IP connection).
Database: all (corresponds to any database) or the name of a particular database.
User: all or the name of a particular role.
Address: all, IP address range, or a domain name. The address is omitted for the local connection type. By default, PostgreSQL listens only for connections coming from localhost; the listen_addresses parameter is usually set to * (listen on all interfaces), while the access is controlled using pg_hba.conf settings.
https://postgrespro.com/docs/postgresql/16/client-authentication
Page 6: Connecting to a Server
3. The server performs authentication using the chosen method
- If successful, access is allowed; otherwise, it is forbidden (if no rows match the given parameters, access is forbidden)
- trust — allow
- reject — forbid
- scram-sha-256 и md5 — request a password
- peer — ask OS
Speaker notes
Once the server finds an appropriate line in the file, it performs client authentication using the method specified in this line, and checks for the LOGIN attribute and the CONNECT privilege. If everything is OK, the connection is allowed; otherwise, it is forbidden (other lines won’t be considered in this case).
If no appropriate line is found, the access is also forbidden.
Thus, more specific connection lines should precede more generic ones.
There are a lot of different authentication methods:
https://postgrespro.com/docs/postgresql/16/auth-methods
Here are some of the main ones.
The trust method allows connections unconditionally. If security is not a concern, you can specify the trust method and use all for all the other parameters; then all connections will be allowed.
The reject method, on the contrary, unconditionally forbids connections.
The scram-sha-256 method asks for a password and checks that the provided password matches the one stored in the system catalog of the database cluster. The md5 method is considered deprecated.
The peer method checks the name of the operating system user and allows connections on behalf of the database user with the same name (you can also define a different name mapping).
Page 7: Password Authentication
At the server side
- the password is set when the role is created and can be changed later
- a user that has no password won’t be able to connect
- the password is stored in the pg_authid system catalog
- Entering the password on the client
- manually
- using the PGPASSWORD environment variable
- using the ~/.pgpass file (lines format: node:port:database:role:password)
Speaker notes
If password authentication is used, there must be a reference password stored for the user; otherwise the connection will be rejected.
Password hashes are stored in the pg_authid table in the system catalog.
The user can either enter the password manually, or automate password input using one of the following options.
First, the password can be set in the PGPASSWORD environment variable (on the client). However, it is inconvenient if you have to connect to several databases, and it is not recommended for security reasons.
The second option to store passwords on the client is to use the ~/.pgpass file. The access to this file must be allowed to its owner only (chmod 600), otherwise PostgreSQL will ignore it.
Page 8: Connection
In order for a role to connect to the database, it must have both the LOGIN attribute and the permission in the file pg_hba.conf. You can usually find it right beside the main configuration file:
And you can read it directly from SQL:
(The contents may vary depending on the server build.) We will use a TCP/IP (host) connection to localhost. Such connection corresponds to the second string in the output, expecting password-based authentication. The role alice was created with a password right away, but the password can be changed at any time:
Attempt to connect using a connection string with all the info:
Success!
Page 9: Privileges
Privileges define access rights of roles to objects
- Tables and views
- SELECT
- read data
- INSERT
- insert rows
- can be set per column
- UPDATE
- change rows
- DELETE
- delete rows
Speaker notes
Privileges establish a relation between subjects (roles) and objects in the cluster. They determine the actions that roles can perform with these objects.
There are different privileges available for different object types. This slide and the following one list privileges for basic database objects.
The widest choice of privileges is available for tables and views. Some of these privileges can be set not only at the table level, but also at the column level.
https://postgrespro.com/docs/postgresql/16/ddl-priv
https://postgrespro.com/docs/postgresql/16/sql-grant
Page 10: Privileges
{#source-figure-010 caption=“Source slide page 10: Privileges”}
Tablespaces, database, schemas
- Sequences
- SELECT
- currval
- UPDATE
- nextval
- setval
- USAGE
- currval
- nextval
Speaker notes
Sequences have a somewhat unexpected set of privileges. They serve to allow or restrict access to the three control functions.
For tablespaces, there is a CREATE privilege that allows the creation of objects in this tablespace.
For databases, the CREATE privilege allows you to create schemas in this database, and for a schema, the CREATE privilege allows you to create objects in this schema.
Since the exact name of the schema for temporary objects is unknown in advance, the privilege to create temporary tables has been moved to the database level (TEMPORARY).
The USAGE schema privilege allows access to objects in this schema.
The CONNECT database privilege allows connection to this database.
Page 11: Role Categories
Superusers
- full access to all objects, no checks performed
- Owners
- initially, all privileges on the object (can be revoked)
- actions that are not regulated by privileges, such as deleting objects, granting
- and revoking privileges, etc.
- Other roles
- access within the granted privileges
Speaker notes
Generally speaking, a role’s ability to access an object is defined by the role’s privileges. But it makes sense to single out three categories of roles and discuss them separately.
Roles with the SUPERUSER attribute (superusers). These roles can do anything and ignore all access control checks.
Object owner. Initially, this is the role that created the object, although it can be changed later. It’s not just the object creator role that becomes the owner, but also any other role included in it. The object owner gets the full range of privileges on this object.
Technically, these privileges can be revoked, but the owner always retains inherent rights on the actions that are not regulated by any privileges. In particular, the owner can grant and revoke privileges (including to and from themselves), delete the object, etc.
- All other roles have access to the object only as far as the privileges granted to them allow it.
To check if a role has the necessary privilege with respect to some object, you can use the has_*_privilege functions:
https://postgrespro.com/docs/postgresql/16/functions-info
Page 12: Privilege Management
Granting privileges
- alice
- bob
- privileges
- on object
- Revoking privileges
Speaker notes
The right to grant and revoke privileges on an object belongs to the owner of that object (and the superuser).
The syntax of the GRANT and REVOKE commands is quite complex. You can specify both individual and all possible privileges, both individual objects and groups of objects included in certain schemas, etc.
https://postgrespro.com/docs/postgresql/16/sql-grant
https://postgrespro.com/docs/postgresql/16/sql-revoke
Page 13: Privileges
{#source-figure-013 caption=“Source slide page 13: Privileges”}
Alice has connected to the database. Now she wants to create a schema and some objects.
What’s the issue? Alice does not have the privilege to create schemas in the database. Grant it:
Try again:
Now, since Alice is the owner of the schema, she has all the privileges on it and can create any objects in it. This schema will be used by default:
Alice creates two tables.
The superuser creates another role for the user Bob, who will access objects belonging to Alice.
Bob attempts to access the table t1:
What happened? Bob is neither a superuser nor the owner of this schema, so access is denied. This command lists current access rights (Access privileges column):
Page 14: Page 14
{#source-figure-014 caption=“Source slide page 14: Page 14”}
Privileges are displayed in the format: role=privileges/granted_by. Each privilege is encoded with a single character. In particular, for schemas: U = usage; C = create. If the role name is omitted (as in the last line), the pseudorole public is implied. Note that the public pseudorole has only USAGE privilege on the public schema. Here, pg_database_owner is the owner of the database. If the entire field is omitted (as in the first line), then the default privileges are implied: Alice gets both available privileges on her schema, and the other roles do not get any. Grant Bob access to the schema. Alice can do that as the owner.
Bob attempts to access the table t1 again:
Another error. What happened now? This time, Bob has access to the schema, but not to the table itself. This command shows who has access to the table:
The field is empty: only the owner, Alice, has access. Alice grants Bob read and update access:
And read and insert rights for one column in the second table:
The privileges have changed:
Now the empty field has “appeared” and contains a complete list of privileges. Below are the designations, not all of them are quite
Page 15: Page 15
{#source-figure-015 caption=“Source slide page 15: Page 15”}
obvious: a = insert r = select w = update d = delete D = truncate x = reference t = trigger Privileges for columns are displayed separately (under Column privileges). This time Bob is successful. He adds the schema name to his search path, so that he does not have to type it in every time.
Now, the search path will be set in each of Bob’s sessions.
However, all other operations remain restricted.
And the second table:
Reading of the other column is not permitted:
Page 16: Granting Membership
{#source-figure-016 caption=“Source slide page 16: Granting Membership”}
public Granting membership
- alice
- bob
- dba
- public pseudo-role implicitly includes
- all other roles
- Revoking membership
Speaker notes
Any role can include other roles as members. In this case, the role acts as a group. PostgreSQL does not have a separate “group” entity.
A role can be a member of multiple roles; in turn, a member role may include other roles, but circular dependencies are not permitted.
By default, a role inherits the privileges of any group roles it is a member of. This behavior can be changed by using the NOINHERIT attribute for the role. This makes the user have to explicitly switch to the group role using SET ROLE in order to use its privileges. Role attributes are not inherited, but it is possible to switch to a parent role to use its attributes.
Roles that include other roles typically have the NOLOGIN attribute and are referred to as “group roles”. We can think of a group role as a predefined set of privileges that can be granted to a role just like any regular privilege. This simplifies access control and administration.
There is also a pseudorole called public, which implicitly includes all the other roles. Any privilege granted to the public role is automatically granted to all the other roles as well.
https://postgrespro.com/docs/postgresql/16/role-membership
Page 17: Predefined Roles
pg_monitor
- pg_read_all_settings read all server parameters
- pg_read_all_stats access statistics pg_stat_scan_tables monitoring and locking tables
- pg_read_all_data read data in all tables pg_write_all_data change data in all tables
- pg_read_server_files read files on the server pg_write_server_files write to files on the server
- pg_execute_server_programs run programs on the server
- …
Speaker notes
PostgreSQL provides a number of predefined roles that have access to some frequently used, but privileged capabilities and data. Membership in these roles may be granted to users in order to facilitate administrative and maintenance tasks without providing them superuser capabilities.
The list of predefined roles increases with each PostgreSQL version. The full list of all the roles, including predefined ones, cat be seen by \duS command in psql.
https://postgrespro.com/docs/postgresql/16/predefined-roles
You can create your own administrative group roles, e.g. for managing backups.
Page 18: Group Roles
{#source-figure-018 caption=“Source slide page 18: Group Roles”}
Bob could not read the second column:
The superuser includes Bob into the predefined group role pg_read_all_data:
Now Bob can read all tables as if he had been granted SELECT privileges for all tables and USAGE privileges for all schemas:
You can use the command \drg in psql to get role membership information:
The Options column shows attributes for the membership. For Bob as a member of pg_read_all_data, SET means the right to switch to the group role, and INHERIT means that Bob can use the group privileges without explicitly switching to the group. Exclude Bob from the group:
Page 19: Routines
The only privilege for functions and procedures
- EXECUTE
- execution
- Security modes
- SECURITY INVOKER
- executed with the privileges of the caller
- (by default)
- SECURITY DEFINER
- executed with the privileges of the owner
Speaker notes
The privilege EXECUTE, the only privilege for functions and procedures, allows them to execute.
The user on behalf of which the routine is executed is important. If a routine is declared as a SECURITY INVOKER (by default), it is executed with the rights of the user that runs it. In this case, the statements inside the routine can access only those objects that are accessible to the calling user.
On the other hand, if declared with the SECURITY DEFINER, the routine will use the rights of its owner. This is a way to allow certain users perform certain actions on objects they personally have no access to.
https://postgrespro.com/docs/postgresql/16/sql-createfunction
https://postgrespro.com/docs/postgresql/16/sql-createprocedure
Page 20: Default Privileges
Privileges of the public pseudo-role
- connect to any database
- access to the system catalog
- execution of any routines
- privileges are granted automatically on each new object
- Configurable default privileges
- the possibility to additionally grant or revoke privileges on a newly created
- object
Speaker notes
As we have already said, the public pseudo-role includes all other roles, so they inherit all the privileges granted to public.
And public has quite an extensive list of privileges by default. In particular:
● The right to connect to any database (that’s why the role alice could connect to the database although the CONNECT privilege had not been explicitly granted to this role)
● Access to the system catalog
● Execution of any routines
On the one hand, it enables seamless operation without having to deal with privileges; but on the other hand, it brings extra complications if access control is really required.
The public role automatically receives all the privileges listed above for all newly created objects. So it is not enough to simply revoke the EXECUTE privilege from public: once a new routine appears, public immediately gets the right to execute it.
There is a special mechanism of default privileges that enables you to automatically grant and revoke the required privileges on newly created objects. It can be also used to revoke the EXECUTE privilege from the public pseudo-role.
https://postgrespro.com/docs/postgresql/16/sql-alterdefaultprivileges
Page 21: Configurable Default Privileges
{#source-figure-021 caption=“Source slide page 21: Configurable Default Privileges”}
Alice creates a function:
Can Bob execute the function without Alice granting him the EXECUTE privilege?
Bob can call the function, but still cannot access any objects for which he does not have the appropriate privileges. If Bob creates a table t2 in the schema public, the function will work for both users but will actually access different tables, since Alice and Bob have different search paths. In order for Bob to create a table in the schema public, he must have the CREATE privilege for the schema (in PostgreSQL 15+):
Alternatively, the function can be declared as using the privileges of its owner:
In this case, the function will always be executed with the privileges Alice has, regardless of who actually calls it. Bob deletes his table…
…and gets access to the Alice’s table:
In this situation, Alice needs to keep an eye on the granted privileges. For one, she should revoke the EXECUTE privilege from the role public and explicitly grant it only to the roles that should have it.
Page 22: Page 22
{#source-figure-022 caption=“Source slide page 22: Page 22”}
To make matters worse, for each new function, the EXECUTE privilege is always granted to public by default. You can configure default privileges so that specific users would get (or lose) specific privileges on newly created objects:
Now, Bob is immediately granted execute privileges on routines created by Alice, while other users will not have permission to run them.
Page 23: Takeaways
Roles, attributes and privileges together form a flexible mechanism that allows you to set up access control in different ways
- can grant all access widely
- can restrict access heavily if necessary
- When creating a new role, you need to ensure that it can connect to the server
Page 24: Practice
Set up privileges so that some users have full access to the tables, while others can only query, but not modify the data.
- Create a new database and two roles: writer and reader. 2. Revoke all privileges for the public schema from the public role, grant both privileges to the writer role, and only the usage privilege to the reader role. 3. Set up the default privileges so that the reader role gets read access to the tables owned by writer in the public schema. 4. Create user w1 as a member of the writer role, and user r1 as a member of the reader role. 5. As writer, create a table. 6. Verify that r1 has read-only access to the table, and w1 has full access to it, including the ability to remove it.
Page 25: 1. Databases and Roles
2. Privileges
3. Default Privileges
4. Users
Writing role:
The IN ROLE construct immediately includes the new role into the specified one. That is, this command is equivalent to these two:
Reading role:
5. Table
6. Verification
w1 can insert rows:
Page 26: Page 26
r1 can read the table:
But cannot modify it:
w1 can delete the table:
In PostgreSQL 14+, the predefined role pg_read_all_data was added, which automatically provides read access to all data. Only the database owner or a superuser can drop the database:
Page 27: Practice+
- Register the user roles alice and bob. 2. Modify the pg_hba.conf file to allow access without password only for the postgres and student users, ensuring that access for alice and bob remains restricted. 3. Enable peer authentication method for alice and bob. Check that connection attempts fail without OS user mapping. Create such mapping for alice. 4. Check the capability to use the same OS user mapping for different database roles.
Speaker notes
- Using a text editor, insert the new entry before the first uncommented directive in pg_hba.conf. local all postgres,student trust
Reload configuration.
- Modify the inserted directive in pg_hba.conf by replacing it with the following: local all postgres,student trust local all alice,bob peer
Append the following to the end of the pg_ident.conf file: stmap student alice
- For alice and bob roles to share a single mapping, the pg_ident.conf file shall be as follows: stmap student alice stmap student bob
Page 28: 1. Add Roles
Register roles with the permission to log in.
2. Limit the use of trust
Edit the contents of pg_hba.conf, allowing the trust method only for postgres and student.
This is what we get:
Reload the configuration.
Now, neither alice nor bob can connect.
3. Peer Authentication Method
Using a text editor, add another line with the peer authentication method to allow alice and bob to connect.
Contents of pg_hba.conf:
Page 29: Page 29
Reload the configuration.
The users still cannot log in, but the error message is now different.
The peer authentication method requires the user names in the OS and roles in PostgreSQL to match. Map the role alice to the OS user student by adding a line to pg_ident.conf. Do not set up the mapping for bob, though.
Add the parameter map to the line to set the mapping.
Contents of pg_hba.conf:
Reload the configuration.
Now alice can connect to the database and execute commands.
And bob cannot.
Page 30: 4. One Mapping for Multiple Roles
Allow bob to connect under the same conditions as alice.
Lines added to pg_ident.conf:
Reload the configuration.
Now both alice and bob can connect.
14 Lesson 14: Backup / Overview
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_14_backup_overview.html Archive: DBA1-handouts-16-en.zip PDF pages: 36
Page 1: Backup / Overview
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Aleksei Beresnev Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is,” and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- Logical Backup
- Physical Backup
Page 3: Logical Backup
What Is Logical Backup
- Table Backup
- Database Backup
- Cluster Backup
Page 4: Logical Backup
SQL commands to restore data from scratch
- can backup a separate object or database
- can recover on a cluster running different major version
- can recover on a different architecture
- − low speed
- − can restore to the moment of the backup only
Speaker notes
There are two types of backup: logical and physical.
Logical backup is a set of SQL commands that restores a cluster (or database, or a separate object) from scratch.
Such a backup is, in fact, a plain text file, which gives a certain flexibility. For example, you can make a copy of only those objects that are needed; you can edit the file by changing the names or data types, etc.
In addition, SQL commands can be executed on a different version of the DBMS or on a different architecture. Only compatibility at the command level is required, binary compatibility is not necessary.
However, for a large database, this mechanism is inefficient, since executing the commands (in particular, creation of indexes) will take a long time. Moreover, it is possible to restore the system from such a backup only to the moment at which the backup was made.
https://postgrespro.com/docs/postgresql/16/backup-dump
Page 5: COPY: Table Backup
Backup
- output of a table contents or a query results into a file, stream or program
- Restore
- insertion of rows from a file or input stream into an existing table
- Server variant Client variant
- SQL COPY command
- psql \COPY command
- the file must be accessible
- the file must be accessible
- to the postgres user
- to the user who has launched psql
- on the server
- on the client
Speaker notes
If you want to save only the contents of one table, you can use the COPY command.
The command writes a table (or the result of an arbitrary query) either to a file or to an output stream, or sends it as input to another program. You can specify options such as format (plain text, csv or binary), field separator, NULL string representaion, etc.
The opposite variant of the COPY command reads fields from a file or input stream and inserts them into a table. The table isn’t cleared, the new rows are simply appended to the existing ones.
The COPY command is significantly faster than similar INSERT commands, because the client does not need to access the server repeatedly, and the server does not have to analyze the commands multiple times.
https://postgrespro.com/docs/postgresql/16/sql-copy
In psql, there is a client version of the COPY command with a similar syntax. Unlike the server version, which is an SQL command, the client version is a psql command.
The file name in the SQL command corresponds to a file on the database server. The user running PostgreSQL (usually postgres) must have access to this file. In the client version, the file is accessed on the client, and only the content is transmitted to the server.
https://postgrespro.com/docs/postgresql/16/app-psql
Page 6: COPY
Create a database and a table in it.
Here is how the COPY command outputs the table:
Note Note that an empty string and NULL are different things, despite the output not telling us that. You can input the data in a similar way:
Let’s check:
Page 7: pg_dump: Database Backup
Backup
- outputs an SQL script or an archive
- in a special format with a TOC to a stream or file
- supports parallel execution
- can define what objects to backup
- (tables, schemas, only DML or only DDL, etc.)
- Restore
- SQL script via psql
- archive with TOC via pg_restore
- (can define what objects to restore and supports parallel execution)
- the new database must be created from template0
- roles and tablespaces must be created in advance
Speaker notes
The pg_dump utility creates a full-scale database backup. Depending on the options, it provides either an SQL script containing commands that create the required objects, or a file in a special format with a table of contents.
Restoring from an SQL script is as simple as executing it in psql.
https://postgrespro.com/docs/postgresql/16/app-pgdump
Restoring from an archive is done using the pg_restore tool. It reads the file and translates it into regular psql commands. The advantage is that it allows you to specify what objects to restore at the recovery stage, not just at the backup stage. Moreover, this type of backup and restore supports parallel execution.
https://postgrespro.com/docs/postgresql/16/app-pgrestore
The database for restore must be created from the database template0, since all changes made in template1 will also be backed up. In addition, the necessary roles and tablespaces must be created in advance, since these objects belong to the entire cluster. After restore, it is recommended to run the ANALYZE command to collect fresh statistics.
Page 8: pg_dumpall: Cluster Backup
Backup
- makes a backup of the entire cluster, including roles and tablespaces
- outputs an SQL script to the console or to a file
- parallel execution is not supported, but you can dump only the global objects
- and then use pg_dump
- Restore
- via psql
Speaker notes
pg_dumpall creates a backup of the entire cluster, including roles and tablespaces.
Since pg_dumpall requires access to all objects of all databases, it is usually run by the superuser. pg_dumpall connects to each database in the cluster one by one and backups them using pg_dump. In addition, it also stores data related to the cluster as a whole.
The result of pg_dumpall is a script for psql. Other formats are not supported. This means that pg_dumpall does not support parallel execution, which can be a problem for larger clusters. In this case, you can use the –globals-only option to backup only roles and tablespaces, and then backup all the databases using pg_dump.
https://postgrespro.com/docs/postgresql/16/app-pg-dumpall
Page 9: pg_dump Utility
Take a look at pg_dump output in a plain text format. Note the way data from the table is saved. If any changes were made to template1, they will make it into the backup, too. Therefore, when restoring a database, it is best to create one on the target from template0 (the –create option adds the necessary commands automatically).
Page 10: Page 10
As an example, let’s copy the table into another database.
Page 11: Physical Backup
What Is Physical Backup
- Cold and Hot Backups
- Replication Protocol
- Standalone Backup
- Continuous WAL Archiving
Page 12: Physical Backup
Crash recovery mechanism is used: copy of data and WAL
- restore speed
- can restore a cluster to a certain point in time
- − cannot restore a separate database, only the cluster as a whole
- − can restore only on the same architecture and major version
Speaker notes
Physical backup uses the crash recovery mechanism. This requires:
● base backup — a copy of cluster files (data files and system files),
● a set of write-ahead logs needed to restore consistency.
If the file system is already consistent (the backup was made when the server was stopped correctly), then the WALs are not required.
However, with the WAL archive, it is possible to get cluster state at any point in time. This way, the cluster can be restored to the state right before the crash (or at any moment before that, if needed).
High restore speed and the ability to create a backup on the fly without stopping the server make physical backup the main choice for routine backup needs.
https://postgrespro.com/docs/postgresql/16/backup-file
https://postgrespro.com/docs/postgresql/16/continuous-archiving
Page 13: Hot or Cold
- Cold backup
- Hot backup
- the server is off
- the server was shut
- the server is
- Cluster files are
- backed up when…
- down incorrectly
- running
- special means
- required
- WALs are…
- not required
- required since the
- required for the
- duration of the file
- last checkpoint
- copy
- stored in
- the file system
- server
- must not delete WALs
- too early
Speaker notes
Physical backup creates a copy of the database cluster files at some point.
If a backup is created while the server is stopped, it’s called “cold”. A cold backup either contains consistent data (if the server was shut down correctly), or contains all the logs necessary for recovery (for example, if the OS has done a data snapshot). This simplifies restore, but requires that the server is stopped.
If a backup is created while the server is running (which requires certain additional actions since you can’t copy files just like that), it is called “hot”. The procedure is more complicated, but can be performed without stopping the server.
For hot backup, the copy of the cluster files will be inconsistent. However, the crash recovery mechanism can also be successfully applied to restore from backup. This will require the WALs for at least the duration of the file copy.
Page 14: Standalone Backup
Base copy + WAL
- Backup via pg_basebackup
- connects to the server over the replication protocol
- performs a checkpoint
- copies the database cluster files into a specified directory
- saves all WAL segments generated during the copying process
- Restore
- deploy the standalone backup
- start the server
Speaker notes
Hot backups are created with the pg_basebackup tool.
First, it performs a checkpoint. Then, the cluster files are copied.
All WAL files generated by the server during the time from the checkpoint to the end of file copying are also added to the backup. The resulting backup is called standalone because it contains all the data necessary for recovery.
All you need to restore using a standalone backup is to deploy the backup and start the server. It will use the WALs to restore consistency on startup if necessary, and will be ready to go.
https://postgrespro.com/docs/postgresql/16/app-pgbasebackup
Page 15: Replication Protocol
Protocol
- receiving the WAL stream
- backup and replication control commands
- Served by wal_sender process
- wal_level = replica
- Replication slot
- a server object for receiving WAL records
- remembers which record was read last
- WAL segment is not deleted until fully read through the slot
Speaker notes
The replication protocol allows the utility to connect to the server and collect all WAL files generated during file copying. Despite the name, the protocol is used not only for replication (which will be discussed in the next lesson), but also for backup. The protocol can stream WAL records while data files are being copied.
To prevent the server from deleting WAL files too early, the replication slot can be employed, which keeps track of the last WAL record received by the client.
Establishing a connection over the replication protocol requires a certain configuration.
First, the initiating role must have the REPLICATION attribute (or be a superuser). This role must also have the necessary permission in the pg_hba.conf configuration file.
Second, the max_wal_senders parameter must be set sufficiently high. This parameter limits the number of simultaneously running wal_sender processes serving replication protocol connections.
Third, the wal_level parameter, which determines the amount of information in the WAL, must be set to replica.
The default settings already satisfy all these requirements (for a local connection).
https://postgrespro.com/docs/postgresql/16/protocol-replication
Page 16: Standalone Backup
{#source-figure-016 caption=“Source slide page 16: Standalone Backup”}
Speaker notes
The image on the left shows the main server. It processes incoming queries. At the same time, WAL records are formed and the state of the databases changes (first in the buffer cache, then on disk). WAL segments are cyclically overwritten (to be precise, old segments are deleted, since the file names are unique).
At the bottom of the picture is a backup copy (usually located on another server). It contains a base copy of the data and a set of WAL files.
Page 17: Standalone Backup
The default configuration is sufficient for replication protocol:
The local connection permission for the replication protocol is also on in pg_hba.conf by default (not for all package distributions):
Another database cluster, replica, has been initiated on the port 5433. Ubuntu package installs a tool we can use to verify that the cluster is stopped:
Create a backup. Use the default format (plain):
The pg_basebackup utility performs a checkpoint immediately upon connecting to the server. By default, dirty buffers are written gradually to avoid I/O peak loads (the process may take up to 4.5 minutes). When using –checkpoint=fast, buffers are written without delays.
Page 18: Restore
{#source-figure-018 caption=“Source slide page 18: Restore”}
Speaker notes
During restore, the base backup, including the necessary WAL files, is deployed, for example, on another server (shown on the right).
After the startup, it restores consistency and is ready to go. The system is restored to the point in time when the backup was made. Of course, the main server can go far ahead in the meantime.
Page 19: Restore
Move the new backup into replica cluster directory (after making sure the cluster is stopped):
The cluster files must belong to the postgres user.
Verify the contents:
When started, the cluster will begin restore.
Now both servers run concurrently and independently. Main server:
Server restored from the backup:
Page 20: Page 20
Page 21: WAL Archive
File archive
- WAL segments are archived as they are switched
- controlled by the server
- archiving happens with a delay
- Streaming archive
- a stream of WAL records is continuously recorded into the archive
- external tools required
- delays are minimal
Speaker notes
The hot backup concept can be improved upon even further. Since we have a copy of the database cluster files and WALs, then by constantly saving new logs, we will be able to restore the system not only at the time of copying files, but also at any point in time after that.
There are two ways to go about it. The first is to archive old WAL files before deleting them. There are server settings for that. Unfortunately, with this option, a WAL file will not be archived until the server switches to writing to another WAL file.
The second way is to continuously read WAL entries using the replication protocol and write them into the same archive. This way, delays are minimal, but a separate tool has to be set up to receive the stream data and archive it.
Page 22: File WAL Archive
Archiver process
- Parameters
- archive_mode = on
- archive_command
- shell command to copy a WAL segment
- to a separate storage
- Algorithm
- when switching to a new WAL segment, the archive_command is called
- if the command is completed with the status 0, the segment is deleted
- if the command returns anything else (or if the command is not specified),
- the segment remains until the attempt is successful
Speaker notes
The file WAL archive is managed by the archiver background process.
An arbitrary shell command can be defined as the archive_command parameter to be used for copying. The mechanism itself is enabled by the archive_mode = on.
The algorithm goes as follows. When a WAL segment is filled, the copy command is called. If it is completed with a zero status, the segment can be deleted safely. Otherwise, the segment (and the ones following it) will not be deleted, and the server will periodically try to execute the command until it returns 0.
https://postgrespro.com/docs/postgresql/16/continuous-archiving
Page 23: File WAL Archive
{#source-figure-023 caption=“Source slide page 23: File WAL Archive”}
Speaker notes
This figure shows the main server with continuous archiving set up. Filled WAL segments are copied to a separate archive using the command defined by the archive_command parameter. Usually, the archive is located on a separate server, and it also stores the base backup (or several, from multiple points in time).
Page 24: Streamed WAL Archive
pg\_receivewal utility
- connects over the replication protocol (can use a replication slot)
- and stores WAL records stream in segment files
- the starting position is the beginning of the segment following the last filled
- segment in the directory,
- or the start of current segment, if the directory is empty
- unlike the file archive, records are written continuously
- parameters have to be reconfigured when changing servers
Speaker notes
Another solution is to use the pg_receivewal utility, which receives WAL records via the stream replication protocol and writes segments to the archive.
pg_receivewal usually runs on a separate “archive” server and connects to the main server with the parameters specified in the command line options. It can (and should) use a replication slot in order to ensure that records are not lost.
pg_receivewal generates files in the same way as the server does, and writes them to the specified directory. Segments that have not yet been filled in are written with the .partial suffix.
When launched, the utility starts archiving from the beginning of the segment, following the newest completed archive segment. If the archive is empty (first run), archiving starts from the beginning of the current segment.
When switching to a new server, pg_receivewal must be stopped and restarted with new parameters.
PostgreSQL does not include built-in tools to run the utility in the background (daemonization) or for automatic startup (as a service). To achieve this, you should use operating system features.
https://postgrespro.com/docs/postgresql/16/app-pgreceivewal
Page 25: Streamed WAL Archive
{#source-figure-025 caption=“Source slide page 25: Streamed WAL Archive”}
Speaker notes
pg_receivewal connects to the server over the stream replication protocol. The connection is handled by a separate wal sender process (this must be taken into account when setting the max_wal_senders parameter).
pg_receivewal saves data without waiting for the entire segment to be received.
Page 26: Base Backup + Archive
Configured continuous WAL archiving
- Backup via pg_basebackup
- connects to the server over the replication protocol
- performs a checkpoint
- copies the cluster files into a specified directory
- WAL segments
- are not required
- Restore
- deploy the backup
- set configuration parameters
- (command to read WAL from the archive, target recovery point)
- create a recovery.signal file
- start the server
Speaker notes
To create a backup with continuous archiving configured, the same pg_basebackup tool is used, but with a different set of parameters. The difference is that the WAL files are not saved to the backup, since they are already in the archive.
Restore is more complicated in this case. In addition to deploying the base backup, some recovery settings must be specified:
● restore_command (inverse of archive_command, it copies files from the archive to the server);
● target restore point.
In addition, a recovery.signal file is needed. If present at server startup, the file tells the server to enter the managed recovery mode (the contents of the file is ignored).
Page 27: Restore
{#source-figure-027 caption=“Source slide page 27: Restore”}
Speaker notes
The restore procedure (for example, after main server crash) is performed as follows. A base backup is deployed on another (or the same) server and a recovery.signal file is created. The server starts up and starts reading WAL segments from the archive using restore_command and applying them.
Note that during file archiving, the last incomplete WAL segment at the main server will not be archived. However, the segment can be manually added to the pg_wal directory on the backup server, if this option is available. There may be several such segments in case of archiving failure.
Page 28: Restore
{#source-figure-028 caption=“Source slide page 28: Restore”}
Speaker notes
The backup server reads WAL segments from the pg_wal directory and applies them (in the absence of a segment, making an attempt to copy it from the archive), ultimately bringing the state of the databases up to date. The maximum possible loss is the last incomplete WAL segment that has not been archived, and only if it cannot be copied manually for some reason.
By default, all available log entries are applied. If a target restore point is specified, recovery will stop after reaching it.
Page 29: Restore
{#source-figure-029 caption=“Source slide page 29: Restore”}
Speaker notes
After that, the backup server goes into normal operation: processing incoming queries, archiving WAL segments, and so on.
The restored server can act as the primary server from now on, but in this case it should be deployed on sufficiently powerful hardware in the first place to avoid performance degradation.
Page 30: Takeaways
Logical backup creates SQL commands to restore the state of database objects
- copy command, pg_dump and pg_dumpall utilities
- Physical backup creates a copy of the cluster files + a set of WAL files
- pg_basebackup utility
- WAL archive
- file or stream
- can restore the system to an arbitrary point in time
Page 31: Practice
- Create a database and a table in it with several rows. 2. Make a logical backup of the database using pg_dump. Delete the database and restore it from the backup you made. 3. Make a standalone physical backup of the cluster using pg_basebackup. Modify the table. Restore into a new cluster from the backup you made and verify that the restored database does not contain any of the later changes.
Speaker notes
- replica cluster has already been created in the course VM on port 5433. Use this cluster to restore into.
The cluster data directory is /var/lib/postgresql/16/replica.
To connect to it, specify the port number: psql -p 5433
Page 32: 1. Database and Table
2. Logical Backup
Create a backup:
Delete the database and restore it from the backup:
Page 33: 3. Physical Standalone Backup
Creating a backup with a fast checkpoint:
Make sure the second server is stopped and deploy the backup:
Change the table:
Start the server from backup:
Page 34: Practice+
- Set up stream archiving on the main cluster using pg_receivewal. 2. Create a standalone backup of the main cluster (without WAL) using pg_basebackup. 3. In the main cluster, create a database and a table in it. 4. Restore the replica cluster from the base backup using the archive. Verify that the database and the table are also restored.
Speaker notes
The replica cluster catalog is /var/lib/postgresql/16/replica.
The current file being written by the pg_receivewal utility has a .partial suffix. Once writing is complete, the file is renamed. When recovering, use the partial file along with the usual segments.
To connect to the replica cluster, specify the port number: psql -p 5433
Page 35: 1. Streaming Archive
Note Note that some commands are executed as the postgres user, and some as student. Create a directory to store the WAL archive:
Create a slot to avoid gaps in the archive:
Run the pg_receivewal utility in background. To do that, execute the following command in a separate terminal (or add & at the end of the command in the same terminal).
2. Base Backup without WAL
3. New Database and Table
4. Recovery Configuration
Make sure the second server is stopped and push the backup:
Page 36: Page 36
Use the partial segment during recovery as well:
Start the server and see the result:
Archiving is no longer necessary. Stop the utility and delete the slot to prevent it from interfering with WAL cleanup.
15 Lesson 15: Replication / Overview of Physical Replication
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_15_replica_overview_physical.html Archive: DBA1-handouts-16-en.zip PDF pages: 22
Page 1: Replication / Overview of Physical Replication
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Igor Gnatyuk Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is”, and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- Replication Types
- Physical Replication
- Log Levels
- Replication Use Cases
- Switching to Standby
Page 3: Replication Types
Data synchronization between servers
- Purposes
- fault tolerance, high availability
- scalability
- Physical replication
- synchronization at the level of pages and row versions
- Logical replication
- synchronization at the level of table rows
Speaker notes
A single database server may not meet the requirements.
A single server is a potential point of failure. Two (or more) servers allow the system to maintain availability in case of a failure (fault tolerance) or, more broadly, in any scenario, such as during scheduled maintenance (high availability).
One server may not be able to handle the load. Scaling up (upgrading server resources) may be inefficient or even impossible. However, the workload can be distributed across multiple servers (scaling).
Database systems can access shared data.
The solution is to have multiple servers managing the same data. Replication refers to the process of synchronizing this data.
Depending on the level at which synchronization occurs, there are two types of replication: physical replication synchronizes changes at the data page level and transaction statuses and logical replication synchronizes changes at the table row level.
Page 4: Physical Replication
Mechanism
- one server transfers WAL records to another server, and the second server
- replays the received records
- Features
- primary-standby: data flow in one direction only
- binary server compatibility is required
- only the cluster as a whole can be replicated
Speaker notes
The idea of physical replication is that one server transfers WAL records to another server, and the second server replays the received records like in crash recovery.
During physical replication, servers have assigned roles: primary and standby. The primary transfers WAL records to standby (in the form of files or a stream of records). The standby applies these records to its data files. The WAL record application is purely mechanical, without “understanding the meaning” of the changes, so binary compatibility between servers is important (the same platform and major PostgreSQL version). Since the WAL is shared across the entire cluster, only the cluster as a whole can be replicated.
Page 5: Physical Replication
{#source-figure-005 caption=“Source slide page 5: Physical Replication”}
Speaker notes
To set up replication between two servers, we create a replica from a physical backup of the primary server. Normally, restoring such a backup would create a new independent server. However, when replication is enabled, the standby server operates in continuous recovery mode: it constantly applies new WAL records received from the primary server (handled by the startup process). This way, the replica is constantly maintained in an almost up-to-date state.
There are two ways to deliver WALs from the primary to the standby. The one used more commonly in production is streaming replication.
In this case, the replica (walreceiver process) connects to the primary (walsender process) via the replication protocol and receives the WAL record stream. This minimizes the replica lag and can even eliminate it entirely (in synchronous mode).
If the system is set for continuous archiving, file-based replication is possible. In this case, the replica will lag noticeably as the file archive is updated only when a WAL segment is switched.
In practice, file-based replication is used in addition to streaming replication. If the replica cannot receive the next WAL entry via the replication protocol, it will try to read it from the archive.
https://postgrespro.com/docs/postgresql/16/high-availability
Page 6: WAL Levels
wal\_level parameter
- minimal < replica
- crash recovery crash recovery restore from backup, replication
Speaker notes
Since standby only receives the information contained in the WAL, all data necessary for synchronization shall be recorded into the WAL.
The amount of data stored in each WAL record is controlled by the wal_level parameter.
Prior to PostgreSQL 10 the default level was minimal, which guaranteed only crash recovery. Replication cannot function at this level because some changes are directly written to persistent storage for reliability, bypassing WAL.
In PostgreSQL 10+ the default level is replica. At this level all data changes are recorded into the WAL, that enables restoring the system from pg_basebackup hot backups, as well as physical streaming replication.
As backup and replication are highly-demanded features, the default level was switched to replica.
Page 7: Physical Replication Configuration
Setting up streaming replication between two servers. We will focus on the simplest configuration; replication is covered in detail in the DBA3 administrator course. Required configuration parameters to be checked:
Starting with PostgreSQL 10, default parameters already have appropriate values. Ensure pg_hba.conf allows replication protocol connections:
The necessary permissions are already in place. Let’s deploy a standby from a physical backup, for this we use the pg_basebackup tool. The target copy directory must be empty or non-existent:
The –checkpoint=fast option requests the utility to perform an immediate checkpoint (without delays), while -R adds standby configuration settings:
The utility creates a sample configuration file…
… and a signal file that instructs the standby to enter continuous recovery mode:
The cluster where we are deploying the standby has been pre-initialized. If the server is running, it must be stopped first:
The copy was placed in the home directory of student user, and now we transfer it to the cluster data directory and make postgres user the owner of the files:
Now we can start the server:
Page 8: Page 8
Let’s examine the standby processes.
The walreceiver process receives the WAL stream, and the startup process applies changes. Compare these with the primary processes.
The walsender process sends WAL records to the standby. The state of the replication can be checked on the primary server:
*_lsn values indicate which WAL records were sent to the standby, received by it, written to disk and applied. sync_state is synchronous or asynchronous replication (we will explain it in detail later).
Page 9: Standby Usage
Allowed
- read-only queries (SELECT, COPY TO, cursors)
- setting server parameters (SET, RESET)
- transaction management (BEGIN, COMMIT, ROLLBACK…)
- creating a backup (pg_basebackup)
- Not allowed
- any changes (INSERT, UPDATE, DELETE, TRUNCATE, nextval…)
- locks expecting changes (SELECT FOR UPDATE…)
- DDL commands (CREATE, DROP…), including creating temporary tables
- maintenance commands (VACUUM, ANALYZE, REINDEX…)
- access control (GRANT, REVOKE…)
- triggers and advisory locks do not work
Speaker notes
By default, the standby operates in the hot standby mode. In this mode, client connections are allowed but restricted to read-only operations. Setting server parameters and transaction management commands will also work. For example, you can start a (read-only) transaction with a specific isolation level.
In addition, the standby can also be used for making backups (taking into account the possible lag behind the primary).
In hot standby mode, no data changes (including sequences), locks, DDL commands, commands such as VACUUM, ANALYZE and REINDEX or access control commands are allowed on the standby. Basically, anything that changes the data in any way is not accepted.
If required, the standby can be run in warm standby mode by setting the parameter hot_standby = off. In this case, client connections will be completely disabled.
https://postgrespro.com/docs/postgresql/16/hot-standby
Page 10: Standby Usage
Run several commands on the primary server:
Check the standby:
Let’s insert a row into the table on the primary server:
So, replication is working correctly, and queries are executed successfully on the standby. No changes can be made on standby directly:
Page 11: Standby Usage
{#source-figure-011 caption=“Source slide page 11: Standby Usage”}
data storage reliability
Speaker notes
The replication mechanism offers flexible system design options for a variety of applications. Let’s consider several typical cases and possible solutions.
One of the key objectives is ensuring data storage reliability.
As a reminder, transaction commits can operate in synchronous or asynchronous modes. In synchronous mode, the commit is not completed until the data is safely written to persistent storage. In asynchronous mode, there is a risk of losing some committed data, but commits do not wait for disk writes, improving system performance.
A similar principle is applied to replication: in synchronous mode (synchronous_commit = on), when a standby is present, the commit waits not only for the WAL to be written to disk but also for confirmation that the WAL records have been received by the synchronous standby. This further enhances reliability (ensuring data is not lost even if the primary server fails) but also increases latency, slowing down the system.
There are also intermediate configuration options that do not provide absolute reliability guarantees but still reduce the risk of data loss.
Page 12: Standby Usage
{#source-figure-012 caption=“Source slide page 12: Standby Usage”}
long-running analytical queries (reports)
Speaker notes
As mentioned earlier, long-running queries hold back the vacuum horizon, preventing the removal of obsolete row versions. If certain tables are being actively modified during this time, they can grow significantly in size. This is why standbys are often used for long-running analytical queries.
A subtle issue arises when WAL records from the primary server conflict with queries running on the standby. There are two main sources of such records:
The primary server removes row versions that are no longer needed there but are still required by queries on the standby.
Exclusive locks on the primary that are incompatible with queries on the standby.
So, standby for reports is typically configured to accept WAL records from the primary but delay their application if they conflict with running queries. This means the standby’s data may lag behind the primary, but for analytical workloads, this is acceptable.
Page 13: Multiple Stanbys
{#source-figure-013 caption=“Source slide page 13: Multiple Stanbys”}
Speaker notes
Multiple standbys can be connected to the primary server to distribute OLTP read workloads.
OLTP queries should not be long-running. This enables effective use of replication protocol feedback between standbys and the primary server. In this case, the primary server maintains awareness of the transaction horizon required by queries on standbys, preventing vacuum from removing needed row versions. Essentially, this feedback mechanism achieves the same result as if all queries were executing locally on the primary server.
However, replication provides only the basic mechanism. External tools (load balancers) are required for automatic workload distribution. It is important to note that data consistency between the primary and standbys is not guaranteed — even in synchronous replication. Applications reading from a single server will, of course, maintain consistency, but consistency is no longer guaranteed when reading from multiple servers simultaneously. Replicas may return either stale data or changes not yet visible on the primary. These topics are discussed in detail in the DBA3 Backup and Replication course.
Page 14: Cascading Replication
{#source-figure-014 caption=“Source slide page 14: Cascading Replication”}
no additional load on the primary and redistribution of network traffic
Speaker notes
Multiple standbys connected to a single primary server will generate additional load on it. Network load should also be considered when transmitting multiple copies of the WAL stream.
To reduce this load, standbys can be arranged in a cascade configuration, where servers relay WAL records to each other in a chain. The further downstream from the primary, the greater potential lag may accumulate in the replicated data.
Note that cascaded synchronous replication is not supported — the primary can only synchronize with directly connected standbys. However, the primary collects feedback from all standbys in the cascade.
Page 15: Delayed Replication
{#source-figure-015 caption=“Source slide page 15: Delayed Replication”}
“time machine” can recover to a specific point in time without WAL archive
Speaker notes
A useful feature is the ability to view data at and recover to an arbitrary point in time. It is particularly useful for recovering from user errors where incorrect actions need to be rolled back.
The regular archive-based point-in-time recovery mechanism can work here, but it requires a lot of preparation and takes a lot of time. And PostgreSQL itself does not allow to make data snapshots for a given moment in the past.
The solution is to have a standby apply WAL records not immediately, but with a certain delay.
In this course, we do not cover the required configurations for each of the provided options. For detailed information, refer to the DBA3 Backup and Replication course.
Page 16: Switching to Standby
Scheduled switchover
- shutdown of the main server for maintenance without interruption of service
- manual mode
- Emergency switchover
- switch to a standby due to a primary server failure
- manual mode,
- but can be automated with external cluster software
Speaker notes
An existing standby can be used to replace the primary server.
There are different reasons for switchover to a backup server. If it is maintenance time on the primary, the switchover can be performed routinely at a convenient time. If it is the primary failure, on the other hand, the switchover has to be performed as quickly as possible to avoid service downtime.
Even in case of a failure, switchover is performed manually unless specialized cluster software is used to monitor server status and initiate the switchover automatically.
Page 17: Switching to a Standby
{#source-figure-017 caption=“Source slide page 17: Switching to a Standby”}
To switch a standby from recovery to normal mode use the appropriate command.
Starting with PostgreSQL 12, it can be done with the pg_promote SQL function.
We have two completely independent servers running at the same time.
It is extremely important to ensure that an application connects to only one of the servers to avoid split-brain scenarios where data becomes irreconcilably divided between servers.
Page 18: Takeaways
Physical replication mechanism works by delivering WAL records to the standby and applying them there
- streaming WAL records or transferring files
- Physical replication creates an exact copy of the entire cluster
- unidirectional, requires binary compatibility
- core mechanism for solving multiple use cases
Page 19: Practice
- Set up physical streaming replication between the two servers in synchronous mode. Verify that replication works as intended. Make sure that when the standby is stopped, commits on the primary are not completed. 2. By default, conflicting WAL records on the standby are delayed for up to 30 seconds. Disable the delay and verify that long-running queries on the standby are canceled if the primary deletes and vacuums required row versions. Then enable feedback and confirm that it prevents cancellations by delaying primary vacuums.
Speaker notes
- To do this, set the following parameters on the primary using ALTER SYSTEM:
● synchronous_commit = on
● synchronous_standby_names = ‘“16/replica”’
- The max_standby_streaming_delay parameter defines how long the standby will wait for conflicting queries to complete before canceling them. Set it to 0. Enable feedback by setting hot_standby_feedback = on. Apply both settings using ALTER SYSTEM and reload the configuration.
To simulate a long-running query on a small dataset, use pg_sleep() in queries.
Page 20: 1. Synchronous Replication
Deploy the standby as shown in the demonstration:
Starting the standby.
Let’s configure synchronous replication on the primary. By default, synchronous mode is enabled, but transaction commit records are only synchronized with the local file system:
The synchronization remains unconfigured:
There can be several standbys, and the primary must know which one to synchronize with. The standby is represented by the name specified in its cluster_name parameter:
The standby starts successfully.
Page 21: Page 21
Now stop the standby…
…and attempt to execute a transaction:
The operation hangs until the standby restarts and replication is restored:
2. Conflicting Records
We disable delayed conflict resolution:
Insert rows into the table:
Execute a long-running query on the standby…
…meanwhile, delete table rows and perform vacuuming on the primary:
Vacuuming removed all row versions (tuples: 10 removed). The standby query fails with an error:
Repeat the experiment with feedback enabled.
Page 22: Page 22
Now, the vacuum does not delete the row versions because it is aware of a query running on the standby (10 are dead but not yet removable) and the query is completed successfully:
Results: In the first case (max_standby_streaming_delay), application of WAL records on the standby is delayed. In the second case (hot_standby_feedback), the vacuuming on the primary is delayed. Disable synchronous replication.
16 Lesson 16: Replication / Overview of Logical Replication
This page is extracted from the official PDF into native Markdown. Slide text, code and speaker notes are kept in reading order; the original PDF and HTML remain the layout references. HTML source: dba1_16_replica_overview_logical.html Archive: DBA1-handouts-16-en.zip PDF pages: 23
Page 1: Replication / Overview of Logical Replication
Copyright
© Postgres Professional, 2017–2025 Authors: Egor Rogov, Pavel Luzanov, Ilya Bashtanov, Aleksei Beresnev Translated by: Liudmila Mantrova, Alexander Meleshko, Elena Sharafutdinova Photo: Oleg Bartunov (Phu Monastery and Bhrikuti Peak, Nepal)
Use of Course Materials
Non-commercial use of course materials (presentations, demonstrations) is allowed without restrictions. Commercial use is possible only with the written permission of Postgres Professional. It is prohibited to make changes to the course materials.
Feedback
Please send your feedback, comments and suggestions to: edu@postgrespro.ru
Disclaimer
Postgres Professional assumes no responsibility for any damages and losses, including loss of income, caused by direct or indirect, intentional or accidental use of course materials. Postgres Professional company specifically disclaims any warranties on course materials. Course materials are provided “as is”, and Postgres Professional company has no obligations to provide maintenance, support, updates, enhancements, or modifications.
Page 2: Topics
- Logical Replication
- WAL Levels
- Publications and Subscriptions
- Conflict Detection and Resolution
- Replica Usage
Page 3: Logical Replication
Publisher
- reads its own WAL records
- decodes them into table row changes
- sends to subscribers
- Subscriber
- receives WAL records from the replication stream
- applies the changes to its own tables
- Features
- publication-subscription: data flow is possible in both directions
- requires protocol-level compatibility
- can replicate individual tables
Speaker notes
In physical replication only one server (the primary) makes changes and generates WAL records. Other servers (standbys) only read the primary’s WAL records and apply them.
In logical replication all servers operate normally, can modify data, and generate their own WAL records. Any server can publish its changes, while others can subscribe to them. A single server can act as both a publisher and a subscriber, enabling flexible data flows between servers.
The publisher reads its own WAL records, but unlike physical replication, it first decodes them into a logical format that is platform-independent and not tied to specific PostgreSQL version before transmitting to subscribers. Therefore, binary compatibility is not required — only the replication protocol must be understood. It also supports selective replication, allowing only specific tables to be replicated.
https://postgrespro.com/docs/postgresql/16/logical-replication
Page 4: Logical Replication
{#source-figure-004 caption=“Source slide page 4: Logical Replication”}
Speaker notes
On the publisher the wal_sender process generates WAL records reflecting changes to published data. On the subscriber the logical replication worker receives the information from the publisher and applies it.
The subscriber can receive changes either directly from the publisher or from the publisher’s standby. In the latter case, the standby’s wal_sender process sends changes to the subscriber. This architecture helps reduce load on the publisher.
https://postgrespro.com/docs/postgresql/16/logical-replication-publication
https://postgrespro.com/docs/postgresql/16/logical-replication-subscription
Page 5: Publication and Subscription
Publication
- includes one or more database tables
- can specify columns and row filters
- processes INSERT, UPDATE, DELETE, TRUNCATE commands
- sends row-level changes after transaction commit
- uses a logical replication slot
- Subscription
- receives and applies changes
- can do initial synchronization
- no parsing, rewriting and planning, just direct execution
- possible conflicts with local data
Speaker notes
Logical replication uses a publish and subscribe model.
A publication is created on one server and can include several tables in a single database. Starting with version 15, PostgreSQL allows publishing partial table data by specifying column subsets and row filter conditions. Other servers can subscribe to this publication to receive and apply changes to the tables.
Only table row modifications are replicated (not SQL commands). DDL commands are not transmitted, so target tables on the subscriber must be created manually. Initial synchronization of table contents can be performed when a subscription is created.
After transaction commit, information about modified rows is extracted from WAL records on the publisher through logical decoding. The resulting messages are transmitted to subscribers via the replication protocol in a platform-and version-independent format.
Changes are applied without executing SQL commands, avoiding parsing and planning overhead. On the other hand, a single SQL command can result in multiple one-row changes.
https://postgrespro.com/docs/postgresql/16/logical-replication
Page 6: WAL Levels
wal\_level parameter
- minimal < replica < logical
- crash recovery crash recovery crash recovery restore from backup, restore from backup, replication replication logical replication
Speaker notes
To enable the publisher to generate messages about changes at the row level, the WAL must include additional information, such as row identifiers involved in modifications and notifications about changes to table definitions and data types. This allows the subscriber to have up-to-date knowledge of the structure of replicated objects in any moment.
This extended level of logging is called logical. Since the default level is replica, logical replication requires changing the wal_level parameter on the publisher.
https://postgrespro.com/docs/postgresql/16/protocol-logical-replication#PRO TOCOL-LOGICAL-MESSAGES-FLOW
Page 7: Logical Replication
Assume the first server contains a table:
Let’s clone the cluster by creating a standalone backup (as covered in the Physical Replication Overview lesson), but omit the -R option from pg_basebackup call since we need an independent server, not a standby.
If the second server is running, we stop it.
Move the backup to the data directory of the second server, changing the owner of the files:
Start the second server:
We got two independent servers, each of them has an empty test table. Let’s add a couple of lines to the table on the first server:
To establish logical replication between servers, additional information in the WAL of the publishing server is required:
Create a publication on the first server:
On the second server, subscribe to the publication:
Page 8: Page 8
Verify the replication:
The following view shows the state of the subscription:
The logical replication apply worker process has been started (you can see its ID in pg_stat_subscription.pid):
Page 9: Conflicts
Identification modes for modifying and deleting rows
- primary key columns (default)
- columns of a specific unique index with the NOT NULL constraint
- all columns
- no identification (default for the system catalog)
- Conflicts: violation of integrity constraints
- replication is suspended until the conflict is resolved manually
Speaker notes
Inserting new rows is straightforward. Changes and deletions are more complicated. These operations need to somehow identify the old version of the row. By default, primary key columns are used for this, but you can specify other ways (replica identity) when defining a table, i.e. use a unique index or all the table columns. Or you can disable replication for some tables altogether (system catalog tables have it disabled by default).
Since the table on the publisher and the table on the subscriber can be changed independently of each other, conflicts in the form of integrity constraint violations are possible when inserting new row versions. Whenever this happens, the process of applying records is suspended until the conflict is resolved manually.
https://postgrespro.com/docs/postgresql/16/sql-altertable
https://postgrespro.com/docs/postgresql/16/logical-replication-conflicts
https://postgrespro.com/docs/postgresql/16/sql-altersubscription
Page 10: Conflicts
Local modifications on the subscriber are not prohibited. Let’s insert a row into the table on the second server:
If we add a row with the same primary key value on the publisher, the subscription will have a conflict when trying to apply the change.
The subscription is unable to apply the change, replication stops.
To resolve the conflict, remove the row on the second server and wait a moment…
Replication continues.
Dropping a Subscription
If replication is no longer needed, the subscription should be deleted, otherwise the publisher will keep the open replication slot.
Page 11: Page 11
Page 12: Restrictions
Not replicated
- DDL commands
- sequence values
- large objects
- changes to views, materialized views, and foreign tables
- Not supported
- automatic conflict resolution
Speaker notes
Logical replication has some fundamental limitations.
DDL commands are not replicated — all schema changes must be copied manually.
Only regular base tables and partitioned tables can be replicated. Other relation types such as views, materialized views, and foreign tables cannot be replicated.
Sequence values are not replicated. This means that if the subscriber inserts rows into a replicated table with a surrogate primary key, conflicts may occur. These conflicts can be avoided by allocating different sequence ranges to each server or by using UUIDs instead of sequences.
There is no built-in conflict resolution mechanism.
These restrictions reduce the applicability of logical replication.
https://postgrespro.com/docs/postgresql/16/logical-replication-restrictions
Page 13: Features
Referential integrity
- for TRUNCATE operations, the publication must include all tables referenced
- by foreign keys
- Persistent connection is mandatory
- inactive replication slots prevent WAL segment removal and hold back the
- vacuum horizon
- Potential issues
- bulk data changes
- changes made by long-running transactions
Speaker notes
To properly replicate TRUNCATE commands, the publication should include all tables that have foreign key references to the truncated table.
The connection between the publisher and the subscriber must remain stable. If interrupted, the replication slot becomes inactive, forcing the server to retain WAL segments until reconnection. Inactive slots also hold back the vacuum horizon.
Changes are replicated row-by-row, so SQL commands affecting many rows on the publisher create significant subscriber load.
The default configuration handles long-running transactions poorly — they increase publisher load since changes are only sent after transaction commit. The subscription’s streaming parameter mitigates this by getting row change without delay, either buffering changes in temporary files or applying changes immediately when background process is available.
Page 14: 1. Consolidation
{#source-figure-014 caption=“Source slide page 14: 1. Consolidation”}
receipt and consolidation of data from regional servers
Speaker notes
Let’s discuss some logical replication use cases.
Suppose there are several regional branches, each of which runs on its own PostgreSQL server. The goal is to consolidate some data on a central server.
First, publications of the necessary data are created on regional servers. The central server subscribes to these publications. The received data can be processed using triggers on the central server (for example, unifying the data format).
Inverted, the setup allows, for example, to transfer reference information from the central server to regional ones.
The business logic may apply additional constraints on the system. In some use cases, scheduled batch data transfers may be preferable.
The image shows two WAL receiving processes running on the central server, one for each subscription.
Page 15: 2. Server Update
{#source-figure-015 caption=“Source slide page 15: 2. Server Update”}
updating the major version without interruption of service
Speaker notes
Case: update the major version on the server without interrupting the service.
Two major versions are not binary compatible, so physical replication will not work. However, logical replication can solve the problem.
As usual, external tools are required to switch users between servers.
First, a new server is created with the desired PostgreSQL version.
Page 16: 2. Server Update
{#source-figure-016 caption=“Source slide page 16: 2. Server Update”}
updating the major version without interruption of service
Speaker notes
Then, logical replication of all required databases is set up between the servers, and the data is synchronized. This is possible because logical replication does not require binary compatibility between servers.
Page 17: 2. Server Update
{#source-figure-017 caption=“Source slide page 17: 2. Server Update”}
updating the major version without interruption of service
Speaker notes
Clients are then switched to the new server while the old one is terminated.
In practice, the process of updating major server version using logical replication is much more complicated and difficult. It is discussed in more detail in the Server Update lesson of the DBA2 course.
Page 18: 3. Primary-Primary
{#source-figure-018 caption=“Source slide page 18: 3. Primary-Primary”}
a cluster where multiple servers can modify data
Speaker notes
Case: provide reliable data storage on multiple servers with the ability to write the data on any server (particularly useful for geo-distributed systems).
This can be achieved through bidirectional logical replication, synchronizing changes for the same tables between servers in both directions.
PostgreSQL 16 introduced bidirectional replication, where tables can be both published and subscribed to on different servers simultaneously.
This requires that the applications working with the cluster are built with certain considerations in mind in order to avoid conflicts when modifying data in the same table. For example, to use globally unique identifiers or to ensure that different servers work with different ranges of keys.
Keep in mind that the primary-primary setup with logical replication will not support global distributed transactions and thus cannot guarantee full data consistency between servers. In addition, PostgreSQL does not offer any tools for automatic failure processing, adding nodes to the cluster or removing nodes from it, etc. These tasks must be solved by external means.
Page 19: Takeaways
Logical replication streams individual row changes
- multidirectional
- requires protocol-level compatibility
- Publish and subscribe model
- All current restrictions must be considered
Page 20: Practice
- Set up logical replication of an arbitrary table to another table on the same server. 2. Set up bidirectional logical replication of the same table between two different servers.
Speaker notes
- If you try to perform standard operations, the CREATE SUBSCRIPTION command will hang. Read carefully the documentation:
https://postgrespro.com/docs/postgresql/16/sql-createsubscription
- Clone the second server from a backup, as shown in demonstration earlier.
When creating subscriptions on both servers, specify the following parameters: copy_data = false, origin = none.
Page 21: 1. Replication on a Single Server
Test table:
A copy of the database with the table:
Set the required WAL level:
Connect to the databases:
Create a publication in the first database:
By default, the CREATE SUBSCRIPTION command creates a replication slot, which waits for all active transactions (at the time of slot creation) to complete before proceeding. One such transaction is the CREATE SUBSCRIPTION command itself, causing it to block indefinitely. This issue can be resolved by manually creating a replication slot and specifying its name when creating the subscription:
Verification:
Page 22: Page 22
Replication is working. Delete the publication, subscription, and the second database.
2. Bidirectional Replication
We clone the server using a backup:
Make sure the second server is stopped and push the backup:
Start the second server:
The database with the table and the log level setting were also cloned:
We publish the table on both servers:
Subscribe:
Page 23: Page 23
Modify table rows:
For changes and deletions to be replicated properly, row identification must be configured. By default, rows are identified by their primary key.
Retry the operation:
Check the Result: