PostgreSQL - Advanced Relational Database
Install, configure, and use PostgreSQL, the world's most advanced open-source relational database — covering installation, advanced features, performance tuning, and production deployments.
- Step 1
Overview
PostgreSQL (also known as Postgres) is a powerful, open-source object-relational database system that uses and extends the SQL language. It has earned a reputation for reliability, feature robustness, and performance.
Key capabilities:
- ACID Compliance: Full transactional integrity with MVCC
- Extensibility: User-defined types, functions, operators, and indexing methods
- Standards Compliance: Implements most SQL:2016 features
- JSON Support: Native JSON/JSONB for NoSQL use cases
- Full-Text Search: Built-in PostgreSQL search engine
- Geospatial: PostGIS extension for GIS applications
- Advanced Indexing: B-tree, Hash, GiST, SP-GiST, BRIN, GIN
- Concurrency: MVCC allows readers never to block writers
Why PostgreSQL:
- Open Source: Free with no vendor lock-in
- Reliability: Battle-tested at massive scale
- Features: Most feature-rich open-source database
- Performance: Excellent for complex queries and analytics
- Community: Active development and large community
Official site: https://www.postgresql.org GitHub: https://github.com/postgres/postgres (14K+ stars) Documentation: https://www.postgresql.org/docs/ Key features: - ACID transactions with MVCC - Extensible (user-defined types, functions, operators) - Advanced indexing (B-tree, GIN, GiST, BRIN, etc.) - Full JSON/JSONB support - Foreign data wrappers for distributed queries - Point-in-time recovery (PITR) - Tablespaces for storage management - Step 2
Quick Installation Options
Multiple installation methods available depending on your platform and use case:
# Option 1: Docker (recommended for development) docker run -d \ --name postgres \ -e POSTGRES_PASSWORD=mysecretpassword \ -e POSTGRES_DB=mydatabase \ -p 5432:5432 \ postgres:16 # Option 2: Homebrew (macOS) brew install postgresql@16 brew services start postgresql@16 # Option 3: APT (Debian/Ubuntu) sudo apt update sudo apt install postgresql postgresql-contrib sudo systemctl start postgresql # Option 4: YUM (RHEL/CentOS/Fedora) sudo yum install postgresql-server postgresql-contrib sudo postgresql-setup --initdb sudo systemctl start postgresql # Option 5: Windows (Chocolatey) choco install postgresql # Verify installation psql --version psql -U postgres -c "SELECT version();" # Connect to database psql -U postgres # or with password: psql -U postgres -W - Step 3
Build from Source (Ubuntu 22.04/24.04)
Building PostgreSQL from source gives you the latest features and custom compilation options. This is recommended for production deployments or when you need specific features.
# Install build dependencies sudo apt update sudo apt install -y \ build-essential \ libreadline-dev \ zlib1g-dev \ libssl-dev \ libxml2-dev \ libjson-c-dev \ libkrb5-dev \ wget \ curl # Download PostgreSQL source (version 16 latest stable) wed https://ftp.postgresql.org/pub/source/v16.3/postgresql-16.3.tar.gz tar xzf postgresql-16.3.tar.gz cd postgresql-16.3 # Configure build # Default install location: /usr/local/pgsql ./configure --prefix=/usr/local/pgsql \ --with-openssl \ --with-libxml \ --with-libjson \ --with-python \ --with-perl \ --with-tcl # Build (use -j for parallel compilation) make -j$(nproc) # Run regression tests make install-world # Install sudo make install # Create directories for data and configuration sudo mkdir -p /usr/local/pgsql/data sudo chown $USER /usr/local/pgsql/data # Initialize database cluster /usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data # Start PostgreSQL /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data start # Add to PATH export PATH=/usr/local/pgsql/bin:$PATH # Verify installation psql --version psql -c "SELECT version();" - Step 4
Initial Configuration
PostgreSQL uses two main configuration files:
postgresql.conf - Server runtime configuration
- Memory settings (shared_buffers, work_mem)
- Connection settings (max_connections, listen_addresses)
- Logging (log_destination, logging_collector)
- WAL settings for replication
pg_hba.conf - Client authentication configuration
- Defines which clients can connect
- Authentication methods (trust, password, md5, scram-sha-256, cert)
Key settings to customize:
listen_addresses: Where to accept connectionsport: TCP port (default: 5432)shared_buffers: Memory for caching (25% of RAM recommended)max_connections: Maximum simultaneous connectionslog_statement: Query logging for debugging
# postgresql.conf - Essential settings # Connections listen_addresses = '*' # Accept connections from anywhere (use 'localhost' for local only) port = 5432 # Default port max_connections = 100 # Increase for production # Memory Tuning (adjust based on available RAM) shared_buffers = 256MB # 25% of RAM, minimum 128MB effective_cache_size = 2GB # 50-75% of RAM work_mem = 16MB # Per-operation memory maintenance_work_mem = 256MB # For VACUUM, CREATE INDEX # WAL Settings wal_level = replica # 'minimal', 'replica', or 'logical' max_wal_size = 2GB # Before autovacuum triggers min_wal_size = 1GB # Logging (enable for production) logging_collector = on log_directory = 'log' log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' log_statement = 'ddl' # Log all DDL or 'mod' for DML too log_min_duration_statement = 1000 # Log queries taking > 1s # Autovacuum (keep enabled!) autovacuum = on autovacuum_max_workers = 3 autovacuum_naptime = 1min # pg_hba.conf - Client authentication (simplified) # TYPE DATABASE USER ADDRESS METHOD local all all peer host all all 127.0.0.1/32 scram-sha-256 host all all ::1/128 scram-sha-256 host all all 0.0.0.0/0 scram-sha-256 - Step 5
Basic Usage & SQL
PostgreSQL supports standard SQL with many extensions. Use
psqlas the command-line client:Basic operations:
- CREATE, INSERT, UPDATE, DELETE
- Complex queries with JOINs, subqueries, CTEs
- Stored procedures and functions (PL/pgSQL)
- Triggers and constraints
- Window functions and aggregates
psql meta-commands:
\l- List databases\c dbname- Connect to database\dt- List tables\d table- Describe table\dx- List extensions\q- Quit
-- Start psql psql -U postgres -- Create a database CREATE DATABASE myapp; -- Connect to it \c myapp -- Create a table CREATE TABLE users ( id SERIAL PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL, email VARCHAR(100) UNIQUE NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, is_active BOOLEAN DEFAULT true ); -- Insert data INSERT INTO users (username, email) VALUES ('alice', 'alice@example.com'), ('bob', 'bob@example.com'), ('charlie', 'charlie@example.com'); -- Query data SELECT * FROM users; SELECT username, email FROM users WHERE is_active = true; -- Update data UPDATE users SET is_active = false WHERE username = 'bob'; -- Delete data DELETE FROM users WHERE username = 'charlie'; -- Complex query with aggregation SELECT COUNT(*) as total_users, SUM(CASE WHEN is_active THEN 1 ELSE 0 END) as active_users FROM users; -- Use CTEs (Common Table Expressions) WITH active_users AS ( SELECT * FROM users WHERE is_active = true ) SELECT * FROM active_users; -- Window functions SELECT username, email, ROW_NUMBER() OVER (ORDER BY created_at) as row_num, RANK() OVER (ORDER BY created_at DESC) as rank FROM users; -- Exit psql \q - Step 6
JSON & JSONB Support
PostgreSQL has excellent JSON support with two types:
JSON: Stores exact text representation, preserves formatting JSONB: Binary JSON, optimized for storage and querying, recommended for most use cases
JSONB supports indexing and operators for efficient querying, making PostgreSQL a viable NoSQL alternative.
-- Create table with JSONB column CREATE TABLE products ( id SERIAL PRIMARY KEY, name VARCHAR(100), attributes JSONB, metadata JSONB ); -- Insert JSON data INSERT INTO products (name, attributes, metadata) VALUES ('Laptop', '{"cpu": "i7", "ram": 16, "storage": "512GB SSD"}', '{"color": "silver", "warranty": "2 years", "tags": ["electronics", "portable"]}'); -- Query JSONB data SELECT name, attributes->>'cpu' as cpu FROM products; SELECT name, attributes->'ram' as ram FROM products; -- Query nested JSON SELECT * FROM products WHERE attributes @> '{"cpu": "i7"}'; -- Check if JSON contains key SELECT * FROM products WHERE attributes ? 'cpu'; -- Array operations SELECT metadata->'tags'->>0 as first_tag FROM products; -- Update JSON field UPDATE products SET attributes = attributes || '{"battery": "8 hours"}' WHERE id = 1; -- Create index on JSON field for faster queries CREATE INDEX idx_products_cpu ON products ((attributes->>'cpu')); CREATE INDEX idx_products_tags ON products USING GIN (metadata->'tags'); -- Query with JSON aggregation SELECT json_agg(row_to_json(p.*)) FROM ( SELECT id, name, attributes->>'cpu' as cpu FROM products ) p; - Step 7
Full-Text Search
PostgreSQL has built-in full-text search capabilities. Create search indexes using tsvector and tsquery:
Key components:
tsvector- Document representation for searchingtsquery- Search query representationto_tsvector()- Convert text to tsvectorto_tsquery()- Convert query to tsquery@@- Match operator
Features:
- Stemming and normalization
- Ranking by relevance
- Multiple language support
-- Create table with full-text search CREATE TABLE articles ( id SERIAL PRIMARY KEY, title VARCHAR(200), content TEXT, search_vector tsvector ); -- Create search vector from text UPDATE articles SET search_vector = to_tsvector('english', title || ' ' || content); -- Create GIN index for fast search CREATE INDEX idx_articles_search ON articles USING GIN(search_vector); -- Search for documents SELECT id, title FROM articles WHERE search_vector @@ to_tsquery('english', 'database & postgresql'); -- Ranked search (most relevant first) SELECT id, title, ts_rank(search_vector, to_tsquery('english', 'database optimization')) as rank FROM articles WHERE search_vector @@ to_tsquery('english', 'database optimization') ORDER BY rank DESC; -- Phrase search SELECT * FROM articles WHERE search_vector @@ to_tsquery('english', ''"database optimization"''); -- Search with stemming (finds "optimize", "optimized", etc.) SELECT * FROM articles WHERE search_vector @@ to_tsquery('english', 'optimization'); -- Update trigger for automatic search vector maintenance CREATE FUNCTION update_search_vector() RETURNS trigger AS $$ BEGIN NEW.search_vector := to_tsvector('english', COALESCE(NEW.title, '') || ' ' || COALESCE(NEW.content, '')); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER articles_search_vector_update BEFORE INSERT OR UPDATE ON articles FOR EACH ROW EXECUTE FUNCTION update_search_vector(); -- Fuzzy search with plainto_tsquery (more user-friendly) SELECT * FROM articles WHERE search_vector @@ plainto_tsquery('english', 'database AND postgresql'); -- Highlighting search results SELECT id, title, highlight(search_vector, to_tsquery('english', 'database')) as snippet FROM articles WHERE search_vector @@ to_tsquery('english', 'database'); - Step 8
Advanced Indexing
PostgreSQL supports multiple indexing methods for different use cases:
B-tree: Default, for equality and range queries GIN: For JSONB, arrays, full-text search GiST: For geometric data, full-text, and custom operators BRIN: Block Range Index, excellent for very large tables Hash: For equality-only lookups
Choose the right index type based on your query patterns.
-- B-tree index (default, most common) CREATE INDEX idx_users_email ON users(email); CREATE INDEX idx_users_active ON users(is_active); -- Composite index for multi-column queries CREATE INDEX idx_users_active_email ON users(is_active, email); -- Partial index (only for subset of data) CREATE INDEX idx_users_active_only ON users(email) WHERE is_active = true; -- Expression index CREATE INDEX idx_users_lower_email ON users(LOWER(email)); -- GIN index for JSONB columns CREATE INDEX idx_products_attributes ON products USING GIN(attributes); -- GIN index for arrays CREATE TABLE tags ( id SERIAL PRIMARY KEY, name TEXT[] ); CREATE INDEX idx_tags_name ON tags USING GIN(name); -- BRIN index for large tables with natural ordering CREATE TABLE sensor_readings ( id SERIAL, timestamp TIMESTAMP, value DECIMAL ); CREATE INDEX idx_sensor_timestamp ON sensor_readings USING BRIN (timestamp); -- Concurrent index creation (doesn't lock table) CREATE INDEX CONCURRENTLY idx_large_table ON large_table(column); -- Check index usage EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com'; -- View indexes on table \d users -- Index statistics SELECT schemaname, tablename, indexname, pg_size_pretty(pg_relation_size(indexname::regclass)) as index_size FROM pg_stat_user_indexes WHERE schemaname = 'public' ORDER BY pg_relation_size(indexname::regclass) DESC; - Step 9
Performance Tuning
Optimize PostgreSQL for your workload:
Memory allocation:
shared_buffers: 25% of RAM (min 128MB)effective_cache_size: 50-75% of RAMwork_mem: Per-operation (sorts, hashes) - be conservativemaintenance_work_mem: For VACUUM, CREATE INDEX
Connection management:
- Use connection pooling (pgbouncer)
- Limit max_connections appropriately
- Configure idle timeouts
Query optimization:
- Use EXPLAIN ANALYZE to find slow queries
- Add indexes for common query patterns
- Avoid SELECT * in production
- Use proper JOIN types
# Check current configuration psql -c "SHOW shared_buffers;" psql -c "SHOW work_mem;" psql -c "SHOW effective_cache_size;" # Analyze slow queries psql -c "SELECT query, calls, total_time, mean_time FROM pg_stat_statements ORDER BY mean_time DESC LIMIT 10;" # Check table statistics psql -c "SELECT relname, n_live_tup, n_dead_tup, last_vacuum, last_analyze FROM pg_stat_user_tables ORDER BY n_dead_tup DESC;" # Run VACUUM to clean up dead tuples VACUUM ANALYZE; # Check index usage psql -c "SELECT indexrelname, idx_scan, idx_tup_read, idx_tup_fetch FROM pg_stat_user_indexes ORDER BY idx_scan ASC;" # Analyze specific query EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com'; # Autovacuum configuration (postgresql.conf) # autovacuum = on # autovacuum_max_workers = 3 # autovacuum_naptime = 1min # autovacuum_vacuum_threshold = 50 # autovacuum_analyze_threshold = 50 # Connection pooling with pgbouncer # sudo apt install pgbouncer # Edit /etc/pgbouncer/pgbouncer.ini # [databases] # postgres = host=127.0.0.1 port=5432 # [pgbouncer] # listen_port = 6432 # auth_type = md5 - Step 10
Backup & Recovery
Protect your data with regular backups:
Backup methods:
pg_dump: Logical backup of single databasepg_dumpall: Backup all databasespg_basebackup: Physical backup for replication- PITR: Point-in-time recovery using WAL
Best practices:
- Regular scheduled backups
- Test restoration procedures
- Offsite backup storage
- WAL archiving for continuous backup
# Backup single database (custom format, compressed) pg_dump -h localhost -U postgres -Fc -f backup.dump mydatabase # Backup all databases pg_dumpall -h localhost -U postgres -f all_databases.sql # Backup specific table pg_dump -h localhost -U postgres -t users -f users.sql mydatabase # Restore from custom format pg_restore -h localhost -U postgres -d mydatabase backup.dump # Restore from SQL dump psql -h localhost -U postgres mydatabase < backup.sql # Create backup with WAL for point-in-time recovery # Enable in postgresql.conf: archive_mode = on archive_command = 'cp %p /var/lib/postgresql/wal_archive/%f' # Schedule daily backups with cron crontab -e # 0 2 * * * pg_dump -U postgres mydatabase | gzip > /backups/mydatabase-$(date +\%Y\%m\%d).sql.gz # Verify backup pg_restore -l backup.dump # Backup size check ls -lh /backups/*.sql.gz # Point-in-time recovery # 1. Stop PostgreSQL # 2. Remove data directory contents # 3. Restore from base backup # 4. Configure recovery target in postgresql.conf # recovery_target_time = '2024-01-15 10:00:00' # 5. Start PostgreSQL - Step 11
Extensions
PostgreSQL can be extended with powerful modules:
Popular extensions:
- PostGIS: Geographic information system
- pg_stat_statements: Query performance statistics
- uuid-ossp: UUID generation
- pg_trgm: Text similarity and trigram indexing
- pgcrypto: Cryptographic functions
- hstore: Key-value pairs
Enable extensions to add functionality without application changes.
-- List available extensions \dx -- Install pg_stat_statements for query analysis CREATE EXTENSION pg_stat_statements; -- View query statistics SELECT query, calls, total_time, mean_time, rows FROM pg_stat_statements ORDER BY mean_time DESC LIMIT 10; -- Install UUID support CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- Create table with UUID primary key CREATE TABLE sessions ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, user_id INTEGER, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Insert with UUID INSERT INTO sessions (user_id) VALUES (1); SELECT * FROM sessions; -- Install PostGIS for GIS (requires system package) -- sudo apt install postgresql-16-postgis-3 CREATE EXTENSION postgis; -- Create table with geometry CREATE TABLE locations ( id SERIAL PRIMARY KEY, name VARCHAR(100), geom GEOMETRY(Point, 4326) ); -- Insert geographic data INSERT INTO locations (name, geom) VALUES ('New York', ST_GeomFromText('POINT(-74.0060 40.7128)', 4326)); -- Query distance between points SELECT name, ST_Distance( geom, ST_GeomFromText('POINT(-73.9352 40.7306)', 4326) ) as distance_meters FROM locations; -- Install pg_trgm for text similarity CREATE EXTENSION pg_trgm; -- Similar string search SELECT 'postgresql' || '' as similarity, similarity('postgresql', 'postgres') as similarity_score; -- Create trigram index CREATE INDEX idx_users_username_trgm ON users USING GIN (username gin_trgm_ops); -- Fuzzy search SELECT * FROM users WHERE username ILIKE '%john%'; -- Install pgcrypto for encryption CREATE EXTENSION pgcrypto; -- Encrypt/decrypt data SELECT pgp_sym_encrypt('secret data', 'secret key'); SELECT pgp_sym_decrypt(encrypted_column, 'secret key') FROM table; -- Install hstore for key-value CREATE EXTENSION hstore; -- Use hstore column CREATE TABLE config ( id SERIAL PRIMARY KEY, settings hstore ); INSERT INTO config (settings) VALUES ('key1=>value1, key2=>value2'); SELECT settings->'key1' FROM config; - Step 12
Replication & High Availability
Set up replication for high availability and read scaling:
Streaming replication:
- One primary (master) server
- Multiple replica (standby) servers
- Synchronous or asynchronous replication
- Automatic failover with Patroni
Benefits:
- Read scalability (query replicas)
- High availability (failover)
- Disaster recovery
- Reporting offload
# Configure primary server (postgresql.conf) wal_level = replica max_wal_senders = 3 wal_keep_size = 64MB hot_standby = on # Configure pg_hba.conf for replica connections host replication replicator 10.0.0.0/24 scram-sha-256 # Create replication user CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'secure_password'; # Take base backup on primary pg_basebackup -h primary_host -U replicator -D /var/lib/postgresql/backup -Fp -Xs -P # Configure standby server (postgresql.conf) primary_conninfo = 'host=primary_host port=5432 user=replicator password=secure_password' hot_standby = on # Recovery configuration (standby.signal file) # Create empty file to indicate this is a standby touch /var/lib/postgresql/data/standby.signal # Start standby server pg_ctl -D /var/lib/postgresql/data start # Verify replication status (on primary) SELECT * FROM pg_stat_replication; # Check replica lag SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes FROM pg_stat_replication; # Query on standby (read-only) psql -h standby_host -U postgres -c "SELECT * FROM users;" # Synchronous replication (stronger consistency) # In postgresql.conf: synchronous_standby_names = 'standby1' # Patroni for automatic failover (recommended for production) # Install Patroni on all nodes # pip install patroni # Configure with etcd or Consul for consensus - Step 13
Client Libraries & ORMs
Connect to PostgreSQL from your applications:
Official drivers:
- Python: psycopg2, psycopg3
- Node.js: node-postgres (pg)
- Java: PostgreSQL JDBC Driver
- Go: lib/pq, pgx
- PHP: PDO, pgsql
- Ruby: pg
ORMs with PostgreSQL support:
- Django ORM
- SQLAlchemy (Python)
- Prisma (TypeScript)
- Hibernate (Java)
- Rails ActiveRecord (Ruby)
# Python with psycopg3 (latest) import psycopg conn = psycopg.connect( host="localhost", port=5432, database="mydb", user="postgres", password="mypassword" ) with conn.cursor() as cur: cur.execute("INSERT INTO users (username, email) VALUES (%s, %s)", ("alice", "alice@example.com")) cur.execute("SELECT * FROM users WHERE is_active = %s", (True,)) rows = cur.fetchall() for row in rows: print(row) conn.commit() conn.close() # Python with SQLAlchemy ORM from sqlalchemy import create_engine, Column, Integer, String, Boolean from sqlalchemy.orm import declarative_base, sessionmaker engine = create_engine('postgresql://postgres:mypassword@localhost/mydb') Base = declarative_base() SessionLocal = sessionmaker(bind=engine) class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) username = Column(String(50), unique=True) email = Column(String(100), unique=True) is_active = Column(Boolean, default=True) # Create tables Base.metadata.create_all(engine) # Use session session = SessionLocal() user = User(username="bob", email="bob@example.com") session.add(user) session.commit() # Query users = session.query(User).filter(User.is_active == True).all() for u in users: print(u.username) # Node.js with pg const { Pool } = require('pg'); const pool = new Pool({ host: 'localhost', port: 5432, database: 'mydb', user: 'postgres', password: 'mypassword' }); async function query() { const result = await pool.query('SELECT * FROM users WHERE is_active = $1', [true]); console.log(result.rows); } query().then(() => pool.end()); # Go with pgx import "github.com/jackc/pgx/v5" conn, err := pgx.Connect(ctx, "postgres://postgres:mypassword@localhost:5432/mydb") if err != nil { log.Fatal("Failed to connect:", err) } defer conn.Close(ctx) var username string err = conn.QueryRow(ctx, "SELECT username FROM users WHERE id = $1", 1).Scan(&username) if err != nil { log.Fatal("Query failed:", err) } fmt.Println(username) # Java with JDBC Connection conn = DriverManager.getConnection( "jdbc:postgresql://localhost:5432/mydb", "postgres", "mypassword" ); PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?"); stmt.setInt(1, 1); ResultSet rs = stmt.executeQuery(); if (rs.next()) { String username = rs.getString("username"); System.out.println(username); } - Step 14
Monitoring & Administration
Monitor PostgreSQL health and performance:
Key metrics:
- Connection count and wait events
- Query performance (slow queries)
- Table/index sizes and bloat
- WAL generation and replication lag
- Cache hit ratio
- Deadlock detection
Built-in monitoring:
- pg_stat_database: Database statistics
- pg_stat_user_tables: Table statistics
- pg_stat_user_indexes: Index statistics
- pg_stat_activity: Current queries
Tools:
- pgAdmin (GUI)
- pg_stat_statements (extension)
- Prometheus + Grafana
- Datadog, New Relic
-- View current queries SELECT pid, usename, application_name, state, query, state_change::text as duration FROM pg_stat_activity WHERE state != 'idle'; -- Database size SELECT pg_size_pretty(pg_database_size('mydatabase')) as size; -- Table sizes SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as total_size FROM pg_tables WHERE schemaname = 'public' ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC; -- Index sizes SELECT indexname, pg_size_pretty(pg_relation_size(indexrelid)) as size FROM pg_stat_user_indexes ORDER BY pg_relation_size(indexrelid) DESC; -- Cache hit ratio (should be >95%) SELECT datname, round(100.0 * sum(blks_hit) / nullif(sum(blks_hit + blks_read), 0), 2) as cache_hit_ratio FROM pg_statio_user_tables GROUP BY datname; -- Locks and blocking queries SELECT blocked_locks.pid as blocked_pid, blocked_activity.usename as blocked_user, blocked_activity.query as blocked_query, blocking_locks.pid as blocking_pid, blocking_activity.usename as blocking_user, blocking_activity.query as blocking_query 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; -- Slow queries (requires pg_stat_statements) SELECT query, calls, total_time, mean_time, rows FROM pg_stat_statements WHERE mean_time > 100 -- queries taking >100ms ORDER BY mean_time DESC LIMIT 20; -- Connection count by database SELECT datname, numbackends as connections FROM pg_stat_database WHERE datname != 'template0' AND datname != 'template1'; -- Terminate problematic query SELECT pg_terminate_backend(12345); -- Check for table bloat SELECT schemaname, tablename, n_live_tup as live_rows, n_dead_tup as dead_rows, round(100.0 * n_dead_tup / nullif(n_live_tup + n_dead_tup, 0), 2) as dead_percent FROM pg_stat_user_tables WHERE n_dead_tup > 1000 ORDER BY dead_percent DESC; - Step 15
Resources & Next Steps
Documentation:
- Official PostgreSQL Docs
- PostgreSQL Tutorial
- Use The Index, Luke - Indexing guide
- Exploring PostgreSQL - Blog
Community:
Tools:
- pgAdmin - GUI management
- DBeaver - Universal database tool
- pgBadger - Log analyzer
- explain.depesz.com - EXPLAIN analyzer
Learning:
- PostgreSQL: Up and Running - O'Reilly book
- Mastering PostgreSQL - Advanced guide
Next guides:
- PostgreSQL performance tuning for production
- PostGIS for geospatial applications
- Continuous archiving and point-in-time recovery
- High availability with Patroni and etcd
Official site: https://www.postgresql.org GitHub: https://github.com/postgres/postgres Documentation: https://www.postgresql.org/docs/ Tutorial: https://www.postgresqltutorial.com/ Downloads: https://www.postgresql.org/download/ Extensions catalog: https://pgextensions.org/ Status (versions): https://www.postgresql.org/support/versioning/
Feature requests
Sign in to suggest features or vote on existing ones.
No feature requests yet.
Discussion
Sign in to join the discussion.
No comments yet.