Initial commit
This commit is contained in:
594
skills/databases/references/postgresql-administration.md
Normal file
594
skills/databases/references/postgresql-administration.md
Normal file
@@ -0,0 +1,594 @@
|
||||
# PostgreSQL Administration
|
||||
|
||||
User management, backups, replication, maintenance, and production database administration.
|
||||
|
||||
## User and Role Management
|
||||
|
||||
### Create Users
|
||||
```sql
|
||||
-- Create user with password
|
||||
CREATE USER appuser WITH PASSWORD 'secure_password';
|
||||
|
||||
-- Create superuser
|
||||
CREATE USER admin WITH SUPERUSER PASSWORD 'admin_password';
|
||||
|
||||
-- Create role without login
|
||||
CREATE ROLE readonly;
|
||||
|
||||
-- Create user with attributes
|
||||
CREATE USER developer WITH
|
||||
PASSWORD 'dev_pass'
|
||||
CREATEDB
|
||||
VALID UNTIL '2025-12-31';
|
||||
```
|
||||
|
||||
### Alter Users
|
||||
```sql
|
||||
-- Change password
|
||||
ALTER USER appuser WITH PASSWORD 'new_password';
|
||||
|
||||
-- Add attributes
|
||||
ALTER USER appuser WITH CREATEDB CREATEROLE;
|
||||
|
||||
-- Remove attributes
|
||||
ALTER USER appuser WITH NOSUPERUSER;
|
||||
|
||||
-- Rename user
|
||||
ALTER USER oldname RENAME TO newname;
|
||||
|
||||
-- Set connection limit
|
||||
ALTER USER appuser CONNECTION LIMIT 10;
|
||||
```
|
||||
|
||||
### Roles and Inheritance
|
||||
```sql
|
||||
-- Create role hierarchy
|
||||
CREATE ROLE readonly;
|
||||
CREATE ROLE readwrite;
|
||||
|
||||
-- Grant role to user
|
||||
GRANT readonly TO appuser;
|
||||
GRANT readwrite TO developer;
|
||||
|
||||
-- Revoke role
|
||||
REVOKE readonly FROM appuser;
|
||||
|
||||
-- Role membership
|
||||
\du
|
||||
```
|
||||
|
||||
### Permissions
|
||||
|
||||
#### Database Level
|
||||
```sql
|
||||
-- Grant database access
|
||||
GRANT CONNECT ON DATABASE mydb TO appuser;
|
||||
|
||||
-- Grant schema usage
|
||||
GRANT USAGE ON SCHEMA public TO appuser;
|
||||
|
||||
-- Revoke access
|
||||
REVOKE CONNECT ON DATABASE mydb FROM appuser;
|
||||
```
|
||||
|
||||
#### Table Level
|
||||
```sql
|
||||
-- Grant table permissions
|
||||
GRANT SELECT ON users TO appuser;
|
||||
GRANT SELECT, INSERT, UPDATE ON orders TO appuser;
|
||||
GRANT ALL PRIVILEGES ON products TO appuser;
|
||||
|
||||
-- Grant on all tables
|
||||
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
|
||||
|
||||
-- Revoke permissions
|
||||
REVOKE INSERT ON users FROM appuser;
|
||||
```
|
||||
|
||||
#### Column Level
|
||||
```sql
|
||||
-- Grant specific columns
|
||||
GRANT SELECT (id, name, email) ON users TO appuser;
|
||||
GRANT UPDATE (status) ON orders TO appuser;
|
||||
```
|
||||
|
||||
#### Sequence Permissions
|
||||
```sql
|
||||
-- Grant sequence usage (for SERIAL/auto-increment)
|
||||
GRANT USAGE, SELECT ON SEQUENCE users_id_seq TO appuser;
|
||||
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO appuser;
|
||||
```
|
||||
|
||||
#### Function Permissions
|
||||
```sql
|
||||
-- Grant execute on function
|
||||
GRANT EXECUTE ON FUNCTION get_user(integer) TO appuser;
|
||||
```
|
||||
|
||||
### Default Privileges
|
||||
```sql
|
||||
-- Set default privileges for future objects
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||||
GRANT SELECT ON TABLES TO readonly;
|
||||
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO readwrite;
|
||||
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||||
GRANT USAGE ON SEQUENCES TO readwrite;
|
||||
```
|
||||
|
||||
### View Permissions
|
||||
```sql
|
||||
-- Show table permissions
|
||||
\dp users
|
||||
|
||||
-- Show role memberships
|
||||
\du
|
||||
|
||||
-- Query permissions
|
||||
SELECT grantee, privilege_type
|
||||
FROM information_schema.role_table_grants
|
||||
WHERE table_name = 'users';
|
||||
```
|
||||
|
||||
## Backup and Restore
|
||||
|
||||
### pg_dump (Logical Backup)
|
||||
```bash
|
||||
# Dump database to SQL file
|
||||
pg_dump mydb > mydb.sql
|
||||
|
||||
# Custom format (compressed, allows selective restore)
|
||||
pg_dump -Fc mydb > mydb.dump
|
||||
|
||||
# Directory format (parallel dump)
|
||||
pg_dump -Fd mydb -j 4 -f mydb_dir
|
||||
|
||||
# Specific table
|
||||
pg_dump -t users mydb > users.sql
|
||||
|
||||
# Multiple tables
|
||||
pg_dump -t users -t orders mydb > tables.sql
|
||||
|
||||
# Schema only
|
||||
pg_dump -s mydb > schema.sql
|
||||
|
||||
# Data only
|
||||
pg_dump -a mydb > data.sql
|
||||
|
||||
# Exclude table
|
||||
pg_dump --exclude-table=logs mydb > mydb.sql
|
||||
|
||||
# With compression
|
||||
pg_dump -Fc -Z 9 mydb > mydb.dump
|
||||
```
|
||||
|
||||
### pg_dumpall (All Databases)
|
||||
```bash
|
||||
# Dump all databases
|
||||
pg_dumpall > all_databases.sql
|
||||
|
||||
# Only globals (roles, tablespaces)
|
||||
pg_dumpall --globals-only > globals.sql
|
||||
```
|
||||
|
||||
### pg_restore
|
||||
```bash
|
||||
# Restore from custom format
|
||||
pg_restore -d mydb mydb.dump
|
||||
|
||||
# Restore specific table
|
||||
pg_restore -d mydb -t users mydb.dump
|
||||
|
||||
# List contents
|
||||
pg_restore -l mydb.dump
|
||||
|
||||
# Parallel restore
|
||||
pg_restore -d mydb -j 4 mydb.dump
|
||||
|
||||
# Clean database first
|
||||
pg_restore -d mydb --clean mydb.dump
|
||||
|
||||
# Create database if not exists
|
||||
pg_restore -C -d postgres mydb.dump
|
||||
```
|
||||
|
||||
### Restore from SQL
|
||||
```bash
|
||||
# Restore SQL dump
|
||||
psql mydb < mydb.sql
|
||||
|
||||
# Create database and restore
|
||||
createdb mydb
|
||||
psql mydb < mydb.sql
|
||||
|
||||
# Single transaction
|
||||
psql -1 mydb < mydb.sql
|
||||
|
||||
# Stop on error
|
||||
psql --set ON_ERROR_STOP=on mydb < mydb.sql
|
||||
```
|
||||
|
||||
### Automated Backup Script
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# backup.sh
|
||||
|
||||
# Configuration
|
||||
DB_NAME="mydb"
|
||||
BACKUP_DIR="/backups"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
RETENTION_DAYS=7
|
||||
|
||||
# Create backup
|
||||
pg_dump -Fc "$DB_NAME" > "$BACKUP_DIR/${DB_NAME}_${DATE}.dump"
|
||||
|
||||
# Remove old backups
|
||||
find "$BACKUP_DIR" -name "${DB_NAME}_*.dump" -mtime +$RETENTION_DAYS -delete
|
||||
|
||||
# Log
|
||||
echo "Backup completed: ${DB_NAME}_${DATE}.dump"
|
||||
```
|
||||
|
||||
### Point-in-Time Recovery (PITR)
|
||||
```bash
|
||||
# Enable WAL archiving (postgresql.conf)
|
||||
wal_level = replica
|
||||
archive_mode = on
|
||||
archive_command = 'cp %p /archive/%f'
|
||||
max_wal_senders = 3
|
||||
|
||||
# Base backup
|
||||
pg_basebackup -D /backup/base -Ft -z -P
|
||||
|
||||
# Restore to point in time
|
||||
# 1. Stop PostgreSQL
|
||||
# 2. Restore base backup
|
||||
# 3. Create recovery.conf with recovery_target_time
|
||||
# 4. Start PostgreSQL
|
||||
```
|
||||
|
||||
## Replication
|
||||
|
||||
### Streaming Replication (Primary-Replica)
|
||||
|
||||
#### Primary Setup
|
||||
```sql
|
||||
-- Create replication user
|
||||
CREATE USER replicator WITH REPLICATION PASSWORD 'replica_pass';
|
||||
|
||||
-- Configure postgresql.conf
|
||||
wal_level = replica
|
||||
max_wal_senders = 3
|
||||
wal_keep_size = 64MB
|
||||
|
||||
-- Configure pg_hba.conf
|
||||
host replication replicator replica_ip/32 md5
|
||||
```
|
||||
|
||||
#### Replica Setup
|
||||
```bash
|
||||
# Stop replica PostgreSQL
|
||||
systemctl stop postgresql
|
||||
|
||||
# Remove data directory
|
||||
rm -rf /var/lib/postgresql/data/*
|
||||
|
||||
# Clone from primary
|
||||
pg_basebackup -h primary_host -D /var/lib/postgresql/data -U replicator -P -R
|
||||
|
||||
# Start replica
|
||||
systemctl start postgresql
|
||||
|
||||
# Check replication status
|
||||
SELECT * FROM pg_stat_replication; -- On primary
|
||||
```
|
||||
|
||||
### Logical Replication
|
||||
|
||||
#### Publisher (Primary)
|
||||
```sql
|
||||
-- Create publication
|
||||
CREATE PUBLICATION my_publication FOR ALL TABLES;
|
||||
|
||||
-- Or specific tables
|
||||
CREATE PUBLICATION my_publication FOR TABLE users, orders;
|
||||
|
||||
-- Check publications
|
||||
\dRp
|
||||
SELECT * FROM pg_publication;
|
||||
```
|
||||
|
||||
#### Subscriber (Replica)
|
||||
```sql
|
||||
-- Create subscription
|
||||
CREATE SUBSCRIPTION my_subscription
|
||||
CONNECTION 'host=primary_host dbname=mydb user=replicator password=replica_pass'
|
||||
PUBLICATION my_publication;
|
||||
|
||||
-- Check subscriptions
|
||||
\dRs
|
||||
SELECT * FROM pg_subscription;
|
||||
|
||||
-- Monitor replication
|
||||
SELECT * FROM pg_stat_subscription;
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Database Size
|
||||
```sql
|
||||
-- Database size
|
||||
SELECT pg_size_pretty(pg_database_size('mydb'));
|
||||
|
||||
-- Table sizes
|
||||
SELECT schemaname, tablename,
|
||||
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
|
||||
FROM pg_tables
|
||||
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
|
||||
|
||||
-- Index sizes
|
||||
SELECT schemaname, tablename, indexname,
|
||||
pg_size_pretty(pg_relation_size(indexrelid)) AS size
|
||||
FROM pg_stat_user_indexes
|
||||
ORDER BY pg_relation_size(indexrelid) DESC;
|
||||
```
|
||||
|
||||
### Connections
|
||||
```sql
|
||||
-- Current connections
|
||||
SELECT count(*) FROM pg_stat_activity;
|
||||
|
||||
-- Connections by database
|
||||
SELECT datname, count(*) FROM pg_stat_activity GROUP BY datname;
|
||||
|
||||
-- Connection limit
|
||||
SHOW max_connections;
|
||||
|
||||
-- Kill connection
|
||||
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE pid = 12345;
|
||||
```
|
||||
|
||||
### Activity
|
||||
```sql
|
||||
-- Active queries
|
||||
SELECT pid, usename, state, query, query_start
|
||||
FROM pg_stat_activity
|
||||
WHERE state != 'idle';
|
||||
|
||||
-- Long-running queries
|
||||
SELECT pid, now() - query_start AS duration, query
|
||||
FROM pg_stat_activity
|
||||
WHERE state != 'idle'
|
||||
ORDER BY duration DESC;
|
||||
|
||||
-- Blocking queries
|
||||
SELECT blocked.pid AS blocked_pid,
|
||||
blocked.query AS blocked_query,
|
||||
blocking.pid AS blocking_pid,
|
||||
blocking.query AS blocking_query
|
||||
FROM pg_stat_activity blocked
|
||||
JOIN pg_stat_activity blocking
|
||||
ON blocking.pid = ANY(pg_blocking_pids(blocked.pid));
|
||||
```
|
||||
|
||||
### Cache Hit Ratio
|
||||
```sql
|
||||
-- Should be > 0.99 for good performance
|
||||
SELECT
|
||||
sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) AS cache_hit_ratio
|
||||
FROM pg_statio_user_tables;
|
||||
```
|
||||
|
||||
### Table Bloat
|
||||
```sql
|
||||
-- Check for table bloat (requires pgstattuple extension)
|
||||
CREATE EXTENSION pgstattuple;
|
||||
|
||||
SELECT schemaname, tablename,
|
||||
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size,
|
||||
pgstattuple(schemaname||'.'||tablename) AS stats
|
||||
FROM pg_tables
|
||||
WHERE schemaname = 'public';
|
||||
```
|
||||
|
||||
## Maintenance
|
||||
|
||||
### VACUUM
|
||||
```sql
|
||||
-- Reclaim storage
|
||||
VACUUM users;
|
||||
|
||||
-- Verbose
|
||||
VACUUM VERBOSE users;
|
||||
|
||||
-- Full (locks table, rewrites)
|
||||
VACUUM FULL users;
|
||||
|
||||
-- With analyze
|
||||
VACUUM ANALYZE users;
|
||||
|
||||
-- All tables
|
||||
VACUUM;
|
||||
```
|
||||
|
||||
### Auto-Vacuum
|
||||
```sql
|
||||
-- Check last vacuum
|
||||
SELECT schemaname, tablename, last_vacuum, last_autovacuum
|
||||
FROM pg_stat_user_tables;
|
||||
|
||||
-- Configure postgresql.conf
|
||||
autovacuum = on
|
||||
autovacuum_vacuum_threshold = 50
|
||||
autovacuum_vacuum_scale_factor = 0.2
|
||||
autovacuum_analyze_threshold = 50
|
||||
autovacuum_analyze_scale_factor = 0.1
|
||||
```
|
||||
|
||||
### REINDEX
|
||||
```sql
|
||||
-- Rebuild index
|
||||
REINDEX INDEX idx_users_email;
|
||||
|
||||
-- Rebuild all indexes on table
|
||||
REINDEX TABLE users;
|
||||
|
||||
-- Rebuild database indexes
|
||||
REINDEX DATABASE mydb;
|
||||
|
||||
-- Concurrently (doesn't lock)
|
||||
REINDEX INDEX CONCURRENTLY idx_users_email;
|
||||
```
|
||||
|
||||
### ANALYZE
|
||||
```sql
|
||||
-- Update statistics
|
||||
ANALYZE users;
|
||||
|
||||
-- Specific columns
|
||||
ANALYZE users(email, status);
|
||||
|
||||
-- All tables
|
||||
ANALYZE;
|
||||
|
||||
-- Verbose
|
||||
ANALYZE VERBOSE users;
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### postgresql.conf Location
|
||||
```sql
|
||||
SHOW config_file;
|
||||
```
|
||||
|
||||
### Key Settings
|
||||
```conf
|
||||
# Memory
|
||||
shared_buffers = 4GB # 25% of RAM
|
||||
work_mem = 64MB # Per operation
|
||||
maintenance_work_mem = 512MB # VACUUM, CREATE INDEX
|
||||
effective_cache_size = 12GB # OS cache estimate
|
||||
|
||||
# Query Planner
|
||||
random_page_cost = 1.1 # Lower for SSD
|
||||
effective_io_concurrency = 200 # Concurrent disk ops
|
||||
|
||||
# Connections
|
||||
max_connections = 100
|
||||
superuser_reserved_connections = 3
|
||||
|
||||
# Logging
|
||||
log_destination = 'stderr'
|
||||
logging_collector = on
|
||||
log_directory = 'log'
|
||||
log_filename = 'postgresql-%Y-%m-%d.log'
|
||||
log_rotation_age = 1d
|
||||
log_min_duration_statement = 100 # Log slow queries
|
||||
|
||||
# Replication
|
||||
wal_level = replica
|
||||
max_wal_senders = 3
|
||||
wal_keep_size = 64MB
|
||||
|
||||
# Autovacuum
|
||||
autovacuum = on
|
||||
autovacuum_vacuum_scale_factor = 0.2
|
||||
autovacuum_analyze_scale_factor = 0.1
|
||||
```
|
||||
|
||||
### Reload Configuration
|
||||
```sql
|
||||
-- Reload config without restart
|
||||
SELECT pg_reload_conf();
|
||||
|
||||
-- Or from shell
|
||||
pg_ctl reload
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
### SSL/TLS
|
||||
```conf
|
||||
# postgresql.conf
|
||||
ssl = on
|
||||
ssl_cert_file = '/path/to/server.crt'
|
||||
ssl_key_file = '/path/to/server.key'
|
||||
ssl_ca_file = '/path/to/ca.crt'
|
||||
```
|
||||
|
||||
### pg_hba.conf (Host-Based Authentication)
|
||||
```conf
|
||||
# TYPE DATABASE USER ADDRESS METHOD
|
||||
|
||||
# Local connections
|
||||
local all postgres peer
|
||||
local all all md5
|
||||
|
||||
# Remote connections
|
||||
host all all 0.0.0.0/0 md5
|
||||
host all all ::0/0 md5
|
||||
|
||||
# Replication
|
||||
host replication replicator replica_ip/32 md5
|
||||
|
||||
# SSL required
|
||||
hostssl all all 0.0.0.0/0 md5
|
||||
```
|
||||
|
||||
### Row Level Security
|
||||
```sql
|
||||
-- Enable RLS
|
||||
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Create policy
|
||||
CREATE POLICY user_policy ON users
|
||||
USING (user_id = current_user_id());
|
||||
|
||||
-- Drop policy
|
||||
DROP POLICY user_policy ON users;
|
||||
|
||||
-- View policies
|
||||
\d+ users
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Backups**
|
||||
- Daily automated backups
|
||||
- Test restores regularly
|
||||
- Store backups off-site
|
||||
- Use pg_dump custom format for flexibility
|
||||
|
||||
2. **Monitoring**
|
||||
- Monitor connections, queries, cache hit ratio
|
||||
- Set up alerts for critical metrics
|
||||
- Log slow queries
|
||||
- Use pg_stat_statements
|
||||
|
||||
3. **Security**
|
||||
- Use strong passwords
|
||||
- Restrict network access (pg_hba.conf)
|
||||
- Enable SSL/TLS
|
||||
- Regular security updates
|
||||
- Principle of least privilege
|
||||
|
||||
4. **Maintenance**
|
||||
- Regular VACUUM and ANALYZE
|
||||
- Monitor autovacuum
|
||||
- REINDEX periodically
|
||||
- Check for table bloat
|
||||
|
||||
5. **Configuration**
|
||||
- Tune for workload
|
||||
- Use connection pooling (pgBouncer)
|
||||
- Monitor and adjust memory settings
|
||||
- Keep PostgreSQL updated
|
||||
|
||||
6. **Replication**
|
||||
- At least one replica for HA
|
||||
- Monitor replication lag
|
||||
- Test failover procedures
|
||||
- Use logical replication for selective replication
|
||||
Reference in New Issue
Block a user