Showing posts with label Queries. Show all posts
Showing posts with label Queries. Show all posts

Wednesday, 14 August 2024

How to determine the replication lag - Query

 
How to determine the replication lag

I post a new PostgreSQL "howto" article there every day. Join me in this journey – subscribe here or on X, provide feedback, share!

******************************
On primary / standby leader
******************************

When connected to a primary (or standby leader in case of cascaded replication), one can use pg_stat_replication:

nik=# \d pg_stat_replication
   View "pg_catalog.pg_stat_replication"
      Column      |           Type
------------------+--------------------------
 pid              | integer
 usesysid         | oid 
 usename          | name
 application_name | text
 client_addr      | inet
 client_hostname  | text
 client_port      | integer
 backend_start    | timestamp with time zone
 backend_xmin     | xid
 state            | text
 sent_lsn         | pg_lsn
 write_lsn        | pg_lsn
 flush_lsn        | pg_lsn
 replay_lsn       | pg_lsn
 write_lag        | interval
 flush_lag        | interval
 replay_lag       | interval
 sync_priority    | integer
 sync_state       | text
 reply_time       | timestamp with time zone


This view contains information for both physical (only those using streaming, not WAL shipping) and logical replicas. The lag values here are measured in bytes (columns ***_lsn) or in time intervals (columns ***_lag), and multiple steps can be observed for each replication stream.
To analyze the LSN values in this system view, we need to subtract them from current LSN values on the server we're connected to:
if it's primary (pg_is_in_recovery() returns false), then use pg_current_wal_lsn()
otherwise (standby leader), use pg_last_wal_replay_lsn()
To subtract, we can use the function pg_wal_lsn_diff() or the - operator.
Docs: Monitoring pg_stat_replication view.
Examples of queries to view lags:
Netdata
Postgres exporter for Prometheus

On a physical replica
When connected to a physical replica (standby), to get its lag as a time interval:
select now() - pg_last_xact_replay_timestamp();
In some cases, pg_last_xact_replay_timestamp() may return NULL:
if the standby server has just started and hasn't replayed any transactions,
if there are no recent transactions on the primary.
This behavior of pg_last_xact_replay_timestamp() might lead to wrong conclusions that standby is lagging and that replication is not healthy – this isn't uncommon in low-activity setups (e.g., non-production environments).
Docs: pg_last_xact_replay_timestamp.
Logical replication
The lag can be observed from the primary (in the contest of logical replication, it's usually called "publisher).


In addition to pg_stat_replication already discussed above, pg_replication_slots can also be used:

select
    slot_name,
    pg_current_wal_lsn() - confirmed_flush_lsn as lag_bytes
from pg_replication_slots;


Hybrid case: logical & physical
*************************************
In some cases, you might need to deal with a combination of logical and physical replication. For example, consider the case:
Cluster A: a regular cluster of 3 nodes (primary + 2 physical standbys).
Cluster B: also primary + 2 physical nodes, and the primary connected to the cluster A's primary via logical replication
This is a typical situation when a complex change (e.g., a major upgrade) is performed involving logical replication. At some point, you might want to redirect a portion of the read-only traffic from cluster A's standbys to cluster B's standbys.
In this case, if we need to understand how each of the nodes is lagging, and we want the code to work well with all nodes, it can be tricky.
Here is how it could be solved (credits: Dylan Griffith from GitLab), assuming we can get information from both the node we're analyzing and the cluster A's primary (keeping all the comments above in mind):

1) First, get LSN on the primary:
select pg_current_wal_lsn() as primary_lsn;

2) Then obtain the LSN location of the observed node, and use it to calculate the lag value in bytes:
with current_node as (
  select case
    when exists (select from pg_replication_origin_status) then (
      select remote_lsn
      from pg_replication_origin_status
    )
    when pg_is_in_recovery() then pg_last_wal_replay_lsn()
    else pg_current_wal_lsn()
  end as lsn
)
select lsn – {{primary_lsn}} as lag_bytes
from current_node;
That's it for today. ~0 lags to everyone!

How to create instance in PostgreSQL - Query

 
How to create instance in PostgreSQL
-bash-4.2$ pwd
/u02/PostgreSQL/11/data
-bash-4.2$
-bash-4.2$ cd ..
-bash-4.2$ ls -lrt
total 6956
drwx------  3 postgres postgres    4096 Mar 30  2020 rollbackBackupDirectory
drwx------  3 postgres postgres    4096 Mar 30  2020 OmniDB
drwx------  4 postgres postgres    4096 Mar 30  2020 pl-languages
-rwx------  1 postgres postgres 6946528 Mar 30  2020 Uninstall11
-rwx------  1 postgres postgres  138094 Mar 30  2020 Uninstall11.dat
drwx------  6 postgres postgres    4096 Mar 30  2020 11
-rw-------  1 postgres postgres     358 Mar 30  2020 pg_upgrade_utility.log
-rw-------  1 postgres postgres    1315 Mar 30  2020 pg_upgrade_internal.log
-rw-------  1 postgres postgres    3459 Mar 30  2020 pg_upgrade_server.log
drwx------ 19 postgres postgres    4096 Oct 23 14:33 data
drwx------  3 postgres postgres    4096 Oct 23 21:11 base2
-bash-4.2$
-bash-4.2$ mkdir pgdata
-bash-4.2$
-bash-4.2$
-bash-4.2$ ls -lrt
total 6960
drwx------  3 postgres postgres    4096 Mar 30  2020 rollbackBackupDirectory
drwx------  3 postgres postgres    4096 Mar 30  2020 OmniDB
drwx------  4 postgres postgres    4096 Mar 30  2020 pl-languages
-rwx------  1 postgres postgres 6946528 Mar 30  2020 Uninstall11
-rwx------  1 postgres postgres  138094 Mar 30  2020 Uninstall11.dat
drwx------  6 postgres postgres    4096 Mar 30  2020 11
-rw-------  1 postgres postgres     358 Mar 30  2020 pg_upgrade_utility.log
-rw-------  1 postgres postgres    1315 Mar 30  2020 pg_upgrade_internal.log
-rw-------  1 postgres postgres    3459 Mar 30  2020 pg_upgrade_server.log
drwx------ 19 postgres postgres    4096 Oct 23 14:33 data
drwx------  3 postgres postgres    4096 Oct 23 21:11 base2
drwxrwxr-x  2 postgres postgres    4096 Oct 24 00:00 pgdata
-bash-4.2$ cd pgdata/
-bash-4.2$ ls -lrt
total 0
-bash-4.2$
-bash-4.2$ initdb -D pgdata
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.
The database cluster will be initialized with locale "en_US.UTF-8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".
Data page checksums are disabled.
creating directory pgdata ... ok
creating subdirectories ... ok
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting default timezone ... Asia/Kolkata
selecting dynamic shared memory implementation ... posix
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok
WARNING: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the option -A, or
--auth-local and --auth-host, the next time you run initdb.
Success. You can now start the database server using:
    pg_ctl -D pgdata -l logfile start
-bash-4.2$ ls -lrt
total 4
drwx------ 19 postgres postgres 4096 Oct 24 00:00 pgdata
-bash-4.2$
-bash-4.2$ pwd
/u02/PostgreSQL/11/pgdata
-bash-4.2$ cd pgdata/
-bash-4.2$ ls -lrt
total 112
-rw------- 1 postgres postgres     3 Oct 24 00:00 PG_VERSION
drwx------ 2 postgres postgres  4096 Oct 24 00:00 pg_twophase
drwx------ 2 postgres postgres  4096 Oct 24 00:00 pg_tblspc
drwx------ 2 postgres postgres  4096 Oct 24 00:00 pg_stat_tmp
drwx------ 2 postgres postgres  4096 Oct 24 00:00 pg_stat
drwx------ 2 postgres postgres  4096 Oct 24 00:00 pg_snapshots
drwx------ 2 postgres postgres  4096 Oct 24 00:00 pg_serial
drwx------ 2 postgres postgres  4096 Oct 24 00:00 pg_replslot
drwx------ 4 postgres postgres  4096 Oct 24 00:00 pg_multixact
drwx------ 2 postgres postgres  4096 Oct 24 00:00 pg_dynshmem
drwx------ 2 postgres postgres  4096 Oct 24 00:00 pg_commit_ts
-rw------- 1 postgres postgres 22873 Oct 24 00:00 postgresql.conf
-rw------- 1 postgres postgres    88 Oct 24 00:00 postgresql.auto.conf
-rw------- 1 postgres postgres  1636 Oct 24 00:00 pg_ident.conf
-rw------- 1 postgres postgres  4513 Oct 24 00:00 pg_hba.conf
drwx------ 2 postgres postgres  4096 Oct 24 00:00 pg_xact
drwx------ 3 postgres postgres  4096 Oct 24 00:00 pg_wal
drwx------ 2 postgres postgres  4096 Oct 24 00:00 pg_subtrans
drwx------ 2 postgres postgres  4096 Oct 24 00:00 pg_notify
drwx------ 2 postgres postgres  4096 Oct 24 00:00 global
drwx------ 5 postgres postgres  4096 Oct 24 00:00 base
drwx------ 4 postgres postgres  4096 Oct 24 00:00 pg_logical
-bash-4.2$
-bash-4.2$ pg_ctl start -D /u02/PostgreSQL/11/pgdata/pgdata
waiting for server to start....2021-10-24 00:10:18.711 IST [47827] LOG:  could not bind IPv4 address "127.0.0.1": Address already in use
2021-10-24 00:10:18.711 IST [47827] HINT:  Is another postmaster already running on port 5433? If not, wait a few seconds and retry.
2021-10-24 00:10:18.711 IST [47827] WARNING:  could not create listen socket for "localhost"
2021-10-24 00:10:18.711 IST [47827] FATAL:  could not create any TCP/IP sockets
2021-10-24 00:10:18.711 IST [47827] LOG:  database system is shut down
 stopped waiting
pg_ctl: could not start server
Examine the log output.
-bash-4.2$
-bash-4.2$



vi postgresq.conf

port=5001
log_destination = 'stderr'      
logging_collector = on      
log_rotation_age = 0            
client_min_messages = notice
log_min_messages = warning
log_min_error_statement = error
log_min_duration_statement = 0
log_checkpoints = on
log_connections = on
log_disconnections = on
log_duration = on
log_error_verbosity = verbose       
log_hostname = on
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d '          
log_lock_waits = on         
log_statement = 'all'       
log_temp_files = 0      

:wq


# Add settings for extensions here
-bash-4.2$
-bash-4.2$ pg_ctl start -D /u02/PostgreSQL/11/pgdata/pgdata
waiting for server to start....2021-10-24 00:14:11 IST [48085]: [1-1] user=,db= LOG:  00000: listening on IPv4 address "127.0.0.1", port 5001
2021-10-24 00:14:11 IST [48085]: [2-1] user=,db= LOCATION:  StreamServerPort, pqcomm.c:593
2021-10-24 00:14:11 IST [48085]: [3-1] user=,db= LOG:  00000: listening on Unix socket "/tmp/.s.PGSQL.5001"
2021-10-24 00:14:11 IST [48085]: [4-1] user=,db= LOCATION:  StreamServerPort, pqcomm.c:587
2021-10-24 00:14:11 IST [48085]: [5-1] user=,db= LOG:  00000: redirecting log output to logging collector process
2021-10-24 00:14:11 IST [48085]: [6-1] user=,db= HINT:  Future log output will appear in directory "log".
2021-10-24 00:14:11 IST [48085]: [7-1] user=,db= LOCATION:  SysLogger_Start, syslogger.c:666
 done
server started
-bash-4.2$

Generate the drop sql command - Query

 

/*Generate the drop sql command*/
———————————————————————————————————–

select case when pgc.relname like ‘%_index’
then ‘drop index ‘ || pgns.nspname || ‘.’ || pgc.relname || ‘;’
else ‘drop table ‘ || pgns.nspname || ‘.’ || pgc.relname || ‘;’ end as drop_query
from pg_class pgc
join pg_namespace pgns on pgc.relnamespace = pgns.oid
where pg_is_other_temp_schema(pgc.relnamespace)
and pgc.relname not like ‘%toast%’;

Finding the database replication delay - Query

 
/*Finding the database replication delay*/
———————————————————————————————————

a) Running the following query on the slave:

SELECT EXTRACT(EPOCH FROM (now() – pg_last_xact_replay_timestamp()))::INT;

SELECT extract(epoch from now() – pg_last_xact_replay_timestamp()) AS replica_lag

This query gives you the lag in seconds.
Note: The issue with this query is that while your replica(s) may be 100% caught up, the time interval being returned is always increasing until new write activity occurs on the primary that the replica can replay. This can cause monitoring to give false positives.

b) This can be achieved by comparing pg_last_xlog_receive_location() and pg_last_xlog_replay_location()
on the slave, and if they are the same itreturns 0, otherwise it runs the above query again:

SELECT
CASE
WHEN pg_last_xlog_receive_location() = pg_last_xlog_replay_location() THEN 0
ELSE EXTRACT (EPOCH FROM now() – pg_last_xact_replay_timestamp())::INTEGER
END
AS replication_lag;

c) Compare master and slave xlog

Master:

SELECT pg_current_xlog_location();

Slave:

SELECT pg_last_xlog_receive_location()

d) Execute on Source :

SELECT now()::timestamp(0), slot_name, pg_current_wal_lsn(), restart_lsn, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(),restart_lsn)) as replicationSlotLag, active from pg_replication_slots;

database sessions query

 

Database sessions query
Query

select pid as process_id, 
       usename as username, 
       datname as database_name, 
       client_addr as client_address, 
       application_name,
       backend_start,
       state,
       state_change
from pg_stat_activity;


Columns
process_id - process ID of this backend
username - name of the user logged into this backend
database_name - name of the database this backend is connected to
client_address - IP address of the client connected to this backend
application_name - name of the application that is connected to this backend
backend_start - time when this process was started. For client backends, this is the time the client connected to the server.
state - current overall state of this backend. Possible values are:
active
idle
idle in transaction
idle in transaction (aborted)
fastpath function call
disabled
state_change - time when the state was last changed

Rows
One row: represents one active connection
Scope of rows: all active connections

Daily used SQL query statements for PostgreSQL DBA - Query

 

Daily used SQL query statements for PostgreSQL DBA


PostgreSQL has been rated as the database of the year for two consecutive years, and it has been favored by many DBAs.

In this article, let’s understand what query statements are commonly used to learn PostgreSQL?

View help commands

DB=# help — total help
DB=# \h — SQL commands level help
DB=# \? — psql commands level help
Show by column, similar to MySQL G
DB=# \x
Expanded display is on.

View the DB installation directory (preferably root user execution)

find / -name initdb
See how many DB instances are running (preferably root user execution)

find / -name postgresql.conf
View DB version

cat $PGDATA/PG_VERSION
psql — version

DB=# show server_version;
DB=# select version();
View the running status of the DB instance

pg_ctl status

View all databases

1. psql — l — check how many DBs are under port 5432
psql — p XX — l — check how many DBs are under XX port
DB=# \l
DB=# select * from pg_database;
Create a database

createdb database_name
DB=# \h create database — Help command to create database
DB=# create database database_name
Enter a database

psql –d dbname
DB=# \c dbname
View the current database

DB=# \c
DB=# select current_database();
View database file directory

DB=# show data_directory;
cat $PGDATA/postgresql.conf |grep data_directory
cat /etc/init.d/postgresql|grep PGDATA=
lsof |grep 5432 gets the PID number in the second column and then ps –ef|grep PID
View table space

select * from pg_tablespace;
View language

select * from pg_language;
Query all schemas, must be executed under the specified database
select * from information_schema.schemata;
SELECT nspname FROM pg_namespace;
\dnS
View table name

DB=# \dt — You can only view the public table name under the current database
DB=# SELECT tablename FROM pg_tables WHERE tablename NOT LIKE’pg%’ AND tablename NOT LIKE’sql_%’ ORDER BY tablename;
DB=# SELECT * FROM information_schema.tables WHERE table_name=’ff_v3_ff_basic_af’;
View table structure

DB=# \d tablename
DB=# select * from information_schema.columns where table_schema=’public’ and table_name=’XX’;
View index

DB=# \di
DB=# select * from pg_index;
View view

DB=# \dv
DB=# select * from pg_views where schemaname =’public’;
DB=# select * from information_schema.views where table_schema =’public’;
View trigger

DB=# select * from information_schema.triggers;
View sequence

DB=# select * from information_schema.sequences where sequence_schema =’public’;
View constraints

DB=# select * from pg_constraint where contype =’p’
DB=# select a.relname as table_name,b.conname as constraint_name,b.contype as constraint_type from pg_class a,pg_constraint b where a.oid = b.conrelid and a.relname =’cc’;
View the size of the XX database

SELECT pg_size_pretty(pg_database_size(‘XX’)) As fulldbsize;
View the size of all databases

select pg_database.datname, pg_size_pretty (pg_database_size(pg_database.datname)) AS size from pg_database;
View the data creation time of each database:

select datname,(pg_stat_file(format(‘%s/%s/PG_VERSION’,case when spcname=’pg_default’ then’base’ else’pg_tblspc/’||t2.oid||’/PG_11_201804061/’ end, t1. oid))).* from pg_database t1,pg_tablespace t2 where t1.dattablespace=t2.oid;
View the size of all tables in order according to the space occupied

select relname, pg_size_pretty(pg_relation_size(relid)) from pg_stat_user_tables where schemaname=’public’ order by pg_relation_size(relid) desc;
According to the size of the space, view the index size in order

select indexrelname, pg_size_pretty(pg_relation_size(relid)) from pg_stat_user_indexes where schemaname=’public’ order by pg_relation_size(relid) desc;
View parameter file

DB=# show config_file;
DB=# show hba_file;
DB=# show ident_file;
View the parameter values ​​​​of the current session

DB=# show all;
View parameter values

select * from pg_file_settings
View a parameter value, such as the parameter work_mem

DB=# show work_mem
Modify a parameter value, such as the parameter work_mem

DB=# alter system set work_mem=’8MB’
Note: Using the alter system command will modify the postgresql.auto.conf file instead of postgresql.conf, which can protect the postgresql.conf file very well, adding the mess you made after using many alter system commands, then you only need to delete postgresql .auto.conf, and then execute pg_ctl reload to load the postgresql.conf file to reload the parameters.

See if archive

DB=# show archive_mode;
Check the configuration of the operation log. The operation log includes Error information, slow location query SQL, database startup and shutdown information, and checkpoint too frequent alarm information.

show logging_collector; — start log collection
show log_directory; — Log output path
show log_filename; — log file name
show log_truncate_on_rotation; — when generating a new file, if the file name already exists, whether to overwrite the old file name with the same name

show log_statement; — Set the log record content
show log_min_duration_statement;-statements running for XX milliseconds will be recorded in the log, -1 means disable this function, 0 means record all statements, similar to the slow query configuration of mysql

View the configuration of the wal log, which is the redo redo log

Store in the data_directory/pg_wal directory

View current user

DB=# \c
DB=# select current_user;
View all users
DB=# select * from pg_user;
DB=# select * from pg_shadow;
View all roles

DB=# \du
DB=# select * from pg_roles;
Query user XX authority must be executed under the specified database
select * from information_schema.table_privileges where grantee=’XX’;
Create user XX a
POSTGRESQL database export SQL statement
pg_dump — host hostname — port 5432 — username username -t testtable> /var/www/mytest/1.sql testdb
Command explanation:
pg_dump — host hostname — port 5432 — username username -t testtable > /var/www/mytest/1.sql testdb
Among them: the bold part means:
hostname : the name of the host;
5432 : The database uses the port, the default is 5432
username : the username to log in to the database;
testtable : the table whose data will be exported;
testdb: the database used
Usage:

pg_dump [options]… [database name]

general options:

-f, — file=FILENAME output file or directory name
-F, — format =c|d|t|p output file format (custom, directory, tar)
clear text (default))
-v, — verbose verbose mode
-V, — version output version information, then exit
-Z, — compress =0–9 Compression level of compressed format
 — lock-wait-timeout=TIMEOUT Operation failed after waiting for table lock timeout
-?, — help Display this help, and then exit the
control output options:
-a, — data -only Dump only data, excluding mode
-b, — blobs include large objects in dump
-c, — clean Before re-creating, first clear (delete) database objects
-C, — create in dump Include commands in order to create the database
-E, — encoding=ENCODING turn Store data encoded in ENCODING format
-n, — schema=SCHEMA only dump patterns with specified names
-N, — exclude-schema=SCHEMA do not dump named patterns
-o, — oids include OID in dump
-O, — no -owner Ignore the owner of the recovery object in the plain text format
-s, — schema-only only dump the mode, excluding data
-S, — superuser=NAME use the specified superuser name in the plain text format
-t,- -table=TABLE only dump the table with the specified name
-T, — exclude-table=TABLE does not dump the table with the specified name
-x, — no-privileges do not dump permissions (grant/revoke)
 — binary-upgrade Can only be used by the upgrade tool
 — column-inserts to dump data in the form of an INSERT command with column names
 — disable-dollar-quoting cancel dollar (symbol) quotes, use SQL standard quotes — exclude-table-data=TABLE Do not dump the data in the table with the specified name — inserts dump data in the form of INSERT command instead of COPY command
 — disable-triggers to disable triggers in the process of restoring data only
 — no-security-labels are not assigned a security tag dump
- -no-tablespaces Do not dump table space allocation information
 — no-unlogged-table-data Do not dump table data without logs
 — quote-all-identifiers All identifiers are quoted, even if they are not keywords
 — section=SECTION Back up named sections (before data, data, and after data)
 — serializable-deferrable wait until the backup can run without exception
 — use-set-session-authorization
use SESSION AUTHORIZATION command instead of
ALTER OWNER command to set ownership
connection option:
-h , — host=hostname database server hostname or socket directory
-p, — port=port number database server port number
-U, — username=name connect with the specified database user
-w, — no-password Never prompt for password
-W, — password Force password prompt (automatic)
 — role=ROLENAME Run SET ROLE before dumping
If no database name is provided, then use

the value of the PGDATABASE environment variable.

Thanks for reading

Check the progress of running VACUUM - Query

 
PostgreSQL: Check the progress of running VACUUM

In this post, I am sharing a system view which we can use to check the progress of running vacuum process of PostgreSQL.

PostgreSQL based on MVCC, and in this architecture VACUUM is a routine task of DBA for removing dead tuples.



Now the question is, How to monitor the progress of VACUUM?

Simple, we can use pg_stat_progress_vacuum view for this purpose. In this view, we can check phase column which indicates the different stages of running the vacuum.


select * from pg_stat_progress_vacuum;


Type of phases:

initializing: VACUUM is preparing to begin scanning the heap

scanning heap: VACUUM is currently scanning the heap

vacuuming indexes: VACUUM is currently vacuuming the indexes

vacuuming heap: VACUUM is currently vacuuming the heap

cleaning up indexes: VACUUM is currently cleaning up indexes

truncating heap: VACUUM is currently truncating the heap to return empty pages at the end of the relation to the operating system

performing final cleanup: VACUUM is performing final cleanup. During this phase, VACUUM will vacuum the free space map, update statistics in pg_class, and report statistics to the statistics collector

Sunday, 11 August 2024

PostgreSQL - Queries

 

List of handful PostgreSQL commands to improve productivity

If you are a PostgreSQL DBA working a lot on PostgreSQL. You may want to know some of the useful commands that can be very useful while performing day to day task.



/*Session Monitoring*/
———————————————————————————————————–

SELECT now()::timestamp(0) as time,datname,pid,(now() - query_start)::time(0) AS runtime,EXTRACT(EPOCH FROM (now() - query_start))*1000::INT AS runtime_millisecs,query_start::timestamp(0),usename,client_addr,state, query FROM pg_stat_activity Where pid <> pg_backend_pid() AND now() - query_start > '15 milliseconds'::INTERVAL ORDER BY EXTRACT(EPOCH FROM (now() - query_start))::INT DESC;

SELECT pid,query::varchar(100),datname, now() - pg_stat_activity.query_start AS duration,usename,client_addr,state,wait_event_type,wait_event
FROM pg_stat_activity
WHERE state!='idle' and pid <> pg_backend_pid() -- and now() - query_start > '1 minutes'::interval -- and query not like '%VACUUM%'
order by duration desc;

SELECT pid,query::varchar(100),datname, now() - pg_stat_activity.query_start AS duration,usename,client_addr,state,wait_event_type,wait_event
FROM pg_stat_activity
WHERE pid <> pg_backend_pid()
order by duration desc;




/* Query to check Blocking Session */
———————————————————————————————————–

SELECT blocked_locks.pid AS blocked_pid,
blocked_activity.usename AS blocked_user,
blocking_locks.pid AS blocking_pid,
blocking_activity.usename AS blocking_user,
blocked_activity.query AS blocked_statement,
blocking_activity.query AS current_statement_in_blocking_process
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks
ON blocking_locks.locktype = blocked_locks.locktype
AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE
AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.GRANTED;


/* Query to check tables list with Primary Key*/
———————————————————————————————————–

select kcu.table_schema,
kcu.table_name,
tco.constraint_name,
kcu.ordinal_position as position,
kcu.column_name as key_column
from information_schema.table_constraints tco
join information_schema.key_column_usage kcu
on kcu.constraint_name = tco.constraint_name
and kcu.constraint_schema = tco.constraint_schema
and kcu.constraint_name = tco.constraint_name
where tco.constraint_type <> 'PRIMARY KEY' and kcu.table_schema<>'sys'
order by kcu.table_schema,
kcu.table_name,
position;




/* Query to check tables list without Primary Key*/
———————————————————————————————————–

select tab.table_schema,
tab.table_name
from information_schema.tables tab
left join information_schema.table_constraints tco
on tab.table_schema = tco.table_schema
and tab.table_name = tco.table_name
and tco.constraint_type = 'PRIMARY KEY'
where tab.table_type = 'BASE TABLE'
and tab.table_schema not in ('pg_catalog','sys','information_schema')
and tco.constraint_name is null
order by table_schema,
table_name;

SELECT t.table_schema || '.' || t.table_name SchemaName_TableName
FROM information_schema.tables t
WHERE
(table_catalog, table_schema, table_name) NOT IN (
SELECT tc.table_catalog, tc.table_schema, tc.table_name
FROM information_schema.table_constraints tc
WHERE constraint_type = 'PRIMARY KEY') AND t.table_type = 'BASE TABLE' AND
t.table_schema NOT IN ('information_schema', 'pg_catalog', 'pgq', 'londiste','sys');



/*Query to check User privileges*/
———————————————————————————————————–
SELECT table_catalog, table_schema, table_name, privilege_type FROM information_schema.table_privileges WHERE grantee = 'Username';



/*Query to check Table Size*/
———————————————————————————————————–
SELECT relnamespace::regclass, relname,pg_size_pretty(pg_relation_size(pg_class.oid, 'main')) as main, pg_size_pretty(pg_relation_size(pg_class.oid,'fsm')) as fsm, pg_size_pretty(pg_relation_size(pg_class.oid, 'vm')) as vm, pg_size_pretty(pg_relation_size(pg_class.oid, 'init')) as init, pg_size_pretty(pg_table_size(pg_class.oid)) as table, pg_size_pretty(pg_indexes_size(pg_class.oid)) as indexes, pg_size_pretty(pg_total_relation_size(pg_class.oid)) as total FROM pg_class WHERE relkind='r' ORDER BY pg_total_relation_size(pg_class.oid) DESC LIMIT 10;



/*Query to Check the Active and Inactive users in PostgreSQL*/
———————————————————————————————————–

select count(*),usename,state from pg_stat_activity group by usename, state;

select usename,state,state_change,pid,backend_start,client_addr from pg_stat_activity where usename like 'sc_%' order by 5,2,1;





/*PostgreSQL: Script to kill all idle sessions and connections of a Database*/
———————————————————————————————————–

SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = ''
AND pid <> pg_backend_pid()
AND state in ('idle', 'idle in transaction', 'idle in transaction (aborted)', 'disabled')
AND state_change < current_timestamp - INTERVAL '15' MINUTE;




/*Query to Check Unused Index */
———————————————————————————————————–

SELECT now(), schemaname || '.' || relname AS table, indexrelname AS index, pg_size_pretty(pg_relation_size(i.indexrelid)) AS index_size, idx_scan as index_scans,pg_get_indexdef(i.indexrelid) FROM pg_stat_user_indexes ui JOIN pg_index i ON ui.indexrelid = i.indexrelid WHERE NOT indisunique AND idx_scan < 50 AND pg_relation_size(relid) > 5 * 8192 ORDER BY pg_relation_size(i.indexrelid) DESC LIMIT 20 ;





/*Query to identify the orphan tables*/
———————————————————————————————————–

select pgns.nspname as schema_name
, pgc.relname as object_name
from pg_class pgc
join pg_namespace pgns on pgc.relnamespace = pgns.oid
where pg_is_other_temp_schema(pgc.relnamespace);




/*Generate the drop sql command*/
———————————————————————————————————–

select case when pgc.relname like '%_index'
then 'drop index ' || pgns.nspname || '.' || pgc.relname || ';'
else 'drop table ' || pgns.nspname || '.' || pgc.relname || ';' end as drop_query
from pg_class pgc
join pg_namespace pgns on pgc.relnamespace = pgns.oid
where pg_is_other_temp_schema(pgc.relnamespace)
and pgc.relname not like '%toast%';




/*Query to check Bloated Index*/
———————————————————————————————————–

WITH btree_index_atts AS (
SELECT nspname, relname, reltuples, relpages, indrelid, relam,
regexp_split_to_table(indkey::text, ' ')::smallint AS attnum,
indexrelid as index_oid
FROM pg_index
JOIN pg_class ON pg_class.oid=pg_index.indexrelid
JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
JOIN pg_am ON pg_class.relam = pg_am.oid
WHERE pg_am.amname = 'btree'
-- AND pg_namespace.nspname IN ('')
AND pg_class.relname NOT LIKE 'pk_%'
),
index_item_sizes AS (
SELECT
i.nspname, i.relname, i.reltuples, i.relpages, i.relam,
s.starelid, a.attrelid AS table_oid, index_oid,
current_setting('block_size')::numeric AS bs,
/* MAXALIGN: 4 on 32bits, 8 on 64bits (and mingw32 ?) / CASE WHEN version() ~ 'mingw32' OR version() ~ '64-bit' THEN 8 ELSE 4 END AS maxalign, 24 AS pagehdr, / per tuple header: add index_attribute_bm if some cols are null-able / CASE WHEN max(coalesce(s.stanullfrac,0)) = 0 THEN 2 ELSE 6 END AS index_tuple_hdr, / data len: we remove null values save space using it fractionnal part from stats */
sum( (1-coalesce(s.stanullfrac, 0)) * coalesce(s.stawidth, 2048) ) AS nulldatawidth
FROM pg_attribute AS a
JOIN pg_statistic AS s ON s.starelid=a.attrelid AND s.staattnum = a.attnum
JOIN btree_index_atts AS i ON i.indrelid = a.attrelid AND a.attnum = i.attnum
WHERE a.attnum > 0
GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9
),
index_aligned AS (
SELECT maxalign, bs, nspname, relname AS index_name, reltuples,
relpages, relam, table_oid, index_oid,
( 2 +
maxalign - CASE /* Add padding to the index tuple header to align on MAXALIGN / WHEN index_tuple_hdr%maxalign = 0 THEN maxalign ELSE index_tuple_hdr%maxalign END + nulldatawidth + maxalign - CASE / Add padding to the data to align on MAXALIGN / WHEN nulldatawidth::integer%maxalign = 0 THEN maxalign ELSE nulldatawidth::integer%maxalign END )::numeric AS nulldatahdrwidth, pagehdr FROM index_item_sizes AS s1 ), otta_calc AS ( SELECT bs, nspname, table_oid, index_oid, index_name, relpages, coalesce( ceil((reltuples(4+nulldatahdrwidth))/(bs-pagehdr::float)) +
CASE WHEN am.amname IN ('hash','btree') THEN 1 ELSE 0 END , 0 -- btree and hash have a metadata reserved block
) AS otta
FROM index_aligned AS s2
LEFT JOIN pg_am am ON s2.relam = am.oid
),
raw_bloat AS (
SELECT current_database() as dbname, nspname, c.relname AS table_name, index_name,
bs(sub.relpages)::bigint AS totalbytes, CASE WHEN sub.relpages <= otta THEN 0 ELSE bs(sub.relpages-otta)::bigint END
AS wastedbytes,
CASE
WHEN sub.relpages <= otta THEN 0 ELSE bs*(sub.relpages-otta)::bigint * 100 / (bs*(sub.relpages)::bigint) END AS realbloat, pg_relation_size(sub.table_oid) as table_bytes, stat.idx_scan as index_scans FROM otta_calc AS sub JOIN pg_class AS c ON c.oid=sub.table_oid JOIN pg_stat_user_indexes AS stat ON sub.index_oid = stat.indexrelid ) SELECT dbname as database_name, nspname as schema_name, table_name, index_name, round(realbloat, 1) as bloat_pct, wastedbytes as bloat_bytes, replace(pg_size_pretty(wastedbytes::bigint),' ','') as bloat_size, totalbytes as index_bytes, replace(pg_size_pretty(totalbytes::bigint),' ','') as index_size, table_bytes, replace(pg_size_pretty(table_bytes),' ','') as table_size, index_scans FROM raw_bloat WHERE realbloat > 40
-- AND wastedbytes > 50000000
ORDER BY wastedbytes DESC;



/*Query to Check Bloated Table */
———————————————————————————————————–

WITH table_bloat AS (
SELECT current_database(),
schemaname,
tblid,
tblname,
bstblpages AS real_size, fillfactor, (tblpages-est_tblpages_ff)bs AS bloat_size,
CASE
WHEN tblpages - est_tblpages_ff > 0
THEN 100 * (tblpages - est_tblpages_ff)/tblpages::float
ELSE 0
END AS bloat_ratio,
is_na
-- , (pst).free_percent + (pst).dead_tuple_percent AS real_frag
FROM (
SELECT ceil( reltuples / ( (bs-page_hdr)/tpl_size ) ) + ceil( toasttuples / 4 ) AS est_tblpages,
ceil( reltuples / ( (bs-page_hdr)fillfactor/(tpl_size100) ) ) + ceil( toasttuples / 4 ) AS est_tblpages_ff,
tblpages,
fillfactor,
bs,
tblid,
schemaname,
tblname,
heappages,
toastpages,
is_na
-- , stattuple.pgstattuple(tblid) AS pst
FROM (
SELECT
( 4 + tpl_hdr_size + tpl_data_size + (2ma) - CASE WHEN tpl_hdr_size%ma = 0 THEN ma ELSE tpl_hdr_size%ma END - CASE WHEN ceil(tpl_data_size)::int%ma = 0 THEN ma ELSE ceil(tpl_data_size)::int%ma END ) AS tpl_size, bs - page_hdr AS size_per_block, (heappages + toastpages) AS tblpages, heappages, toastpages, reltuples, toasttuples, bs, page_hdr, tblid, schemaname, tblname, fillfactor, is_na FROM ( SELECT tbl.oid AS tblid, ns.nspname AS schemaname, tbl.relname AS tblname, tbl.reltuples, tbl.relpages AS heappages, coalesce(toast.relpages, 0) AS toastpages, coalesce(toast.reltuples, 0) AS toasttuples, coalesce(substring(array_to_string(tbl.reloptions, ' ') FROM '%fillfactor=#"__#"%' FOR '#')::smallint, 100) AS fillfactor, current_setting('block_size')::numeric AS bs, CASE WHEN version()~'mingw32' OR version()~'64-bit|x86_64|ppc64|ia64|amd64' THEN 8 ELSE 4 END AS ma, 24 AS page_hdr, 23 + CASE WHEN MAX(coalesce(null_frac,0)) > 0 THEN ( 7 + count() ) / 8 ELSE 0::int END + CASE WHEN tbl.relhasoids THEN 4 ELSE 0 END AS tpl_hdr_size,
sum( (1-coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 1024) ) AS tpl_data_size,
bool_or(att.atttypid = 'pg_catalog.name'::regtype) AS is_na
FROM pg_attribute AS att
JOIN pg_class AS tbl ON att.attrelid = tbl.oid
JOIN pg_namespace AS ns ON ns.oid = tbl.relnamespace
JOIN pg_stats AS s ON s.schemaname=ns.nspname
AND s.tablename = tbl.relname
AND s.inherited=false AND s.attname=att.attname
LEFT JOIN pg_class AS toast ON tbl.reltoastrelid = toast.oid
WHERE att.attnum > 0 AND NOT att.attisdropped
-- enable below filter for schema specific bloat detection
-- AND ns.nspname = '${schema}'
AND tbl.relkind = 'r'
GROUP BY 1,2,3,4,5,6,7,8,9,10, tbl.relhasoids
ORDER BY 2,3
) AS s
) AS s2
) AS s3
ORDER BY bloat_size DESC
)
SELECT schemaname,
tblid,
tblname,
REPLACE(pg_size_pretty(real_size::numeric), ' ','') as real_size,
fillfactor,
REPLACE(pg_size_pretty(bloat_size::numeric),' ','') as bloat_size,
round(bloat_ratio::numeric,2) as bloat_ratio,
is_na
FROM table_bloat
-- modify the ratio based on your use case
WHERE bloat_ratio > 40
--WHERE tblname=''
ORDER BY bloat_ratio DESC
;



/*Cache Hit*/
———————————————————————————————————–

with
all_tables as
(
SELECT *
FROM (
SELECT 'all'::text as table_name,
sum( (coalesce(heap_blks_read,0) + coalesce(idx_blks_read,0) + coalesce(toast_blks_read,0) + coalesce(tidx_blks_read,0)) ) as from_disk,
sum( (coalesce(heap_blks_hit,0) + coalesce(idx_blks_hit,0) + coalesce(toast_blks_hit,0) + coalesce(tidx_blks_hit,0)) ) as from_cache
FROM pg_statio_all_tables -- change to pg_statio_USER_tables if you want to check only user tables (excluding postgres's own tables)
) a
WHERE (from_disk + from_cache) > 0 -- discard tables without hits
),
tables as
(
SELECT *
FROM (
SELECT relname as table_name,
( (coalesce(heap_blks_read,0) + coalesce(idx_blks_read,0) + coalesce(toast_blks_read,0) + coalesce(tidx_blks_read,0)) ) as from_disk,
( (coalesce(heap_blks_hit,0) + coalesce(idx_blks_hit,0) + coalesce(toast_blks_hit,0) + coalesce(tidx_blks_hit,0)) ) as from_cache
FROM pg_statio_all_tables -- change to pg_statio_USER_tables if you want to check only user tables (excluding postgres's own tables)
) a
WHERE (from_disk + from_cache) > 0 -- discard tables without hits
)
SELECT table_name as "table name",
from_disk as "disk hits",
round((from_disk::numeric / (from_disk + from_cache)::numeric)100.0,2) as "% disk hits", round((from_cache::numeric / (from_disk + from_cache)::numeric)100.0,2) as "% cache hits",
(from_disk + from_cache) as "total hits"
FROM (SELECT * FROM all_tables UNION ALL SELECT * FROM tables) a
ORDER BY (case when table_name = 'all' then 0 else 1 end), from_disk desc




/*OS process and DB Process check*/
———————————————————————————————————–

Grep for top 10 postgres memory process:-

ps axu | awk '{print $2, $3, $4, $11, $12 }' | sort -k3 -nr |head -10| grep -i postgres




/*Finding the database replication delay*/
———————————————————————————————————–

a) Running the following query on the slave:

SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::INT;

SELECT extract(epoch from now() - pg_last_xact_replay_timestamp()) AS replica_lag

This query gives you the lag in seconds.
Note: The issue with this query is that while your replica(s) may be 100% caught up, the time interval being returned is always increasing until new write activity occurs on the primary that the replica can replay. This can cause monitoring to give false positives.

b) This can be achieved by comparing pg_last_xlog_receive_location() and pg_last_xlog_replay_location()
on the slave, and if they are the same itreturns 0, otherwise it runs the above query again:

SELECT
CASE
WHEN pg_last_xlog_receive_location() = pg_last_xlog_replay_location() THEN 0
ELSE EXTRACT (EPOCH FROM now() - pg_last_xact_replay_timestamp())::INTEGER
END
AS replication_lag;

c) Compare master and slave xlog

Master:

SELECT pg_current_xlog_location();

Slave:

SELECT pg_last_xlog_receive_location()

d) Execute on Source :

SELECT now()::timestamp(0), slot_name, pg_current_wal_lsn(), restart_lsn, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(),restart_lsn)) as replicationSlotLag, active from pg_replication_slots;





Wednesday, 28 February 2024

Query Tuning - PostgreSQL

 







Below is the example of INNER JOIN 






Ending cost = 303.67

Starting cost = 176.34





Above output will give you timestamp and above values is in milliseconds 



12 seconds 

Query is taking 12seconds to complete so it is Bad performance of the INNER join query.

Because both the tables don't have indexes 




Now Let's create indexes on emp and dept tables and check the Explain plan :-





You can compare the previous explain plan with current explain plan.


1) Merge Join converted into Hash Join

2) Cost changed

3) number of rows selection reduced 


Let's calculate final cost :-





























Enables or disables the query planner's use of sequential scan plan types. It is impossible to suppress sequential scans entirely, but turning this variable off discourages the planner from using one if there are other methods available. The default is on.











Temporary table is session specific,

Below is an example of temporary table emp_temp










https://pgtune.leopard.in.ua/





























postgres=# \timing 

check the table creation time





















































Master and Slave - Sync check - PostgreSQL

  1) Run the below Query on Primary:- SELECT     pid,     usename,     application_name,     client_addr,     state,     sync_state,     sen...