Database Administration: MySQL and PostgreSQL Essentials
Most web applications rely on a relational database. This guide covers installation, user management, backups, basic tuning, and replication concepts for the two most popular open-source databases.
MySQL / MariaDB
Installation
# Debian/Ubuntu
sudo apt update
sudo apt install mysql-server
# RHEL/CentOS/Fedora (MariaDB)
sudo dnf install mariadb-server
sudo systemctl enable --now mariadb
Secure the Installation
sudo mysql_secure_installation
This interactive script will: - Set the root password - Remove anonymous users - Disallow remote root login - Remove the test database
Database and User Management
-- Connect as root
-- sudo mysql -u root -p
-- Create a database
CREATE DATABASE webapp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- Create a user with a password
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'StrongPassword123!';
-- Grant permissions
GRANT ALL PRIVILEGES ON webapp.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
-- Grant read-only access to a reporting user
CREATE USER 'reporter'@'10.0.1.%' IDENTIFIED BY 'ReadOnlyPass!';
GRANT SELECT ON webapp.* TO 'reporter'@'10.0.1.%';
FLUSH PRIVILEGES;
-- Show grants for a user
SHOW GRANTS FOR 'appuser'@'localhost';
Backup with mysqldump
# Dump a single database
mysqldump -u root -p webapp > webapp_backup.sql
# Dump all databases
mysqldump -u root -p --all-databases > all_databases.sql
# Dump with compression
mysqldump -u root -p webapp | gzip > webapp_backup.sql.gz
# Restore
mysql -u root -p webapp < webapp_backup.sql
For large databases, consider mysqldump --single-transaction (InnoDB) to
avoid locking tables during the backup.
Basic Tuning
Edit /etc/mysql/mysql.conf.d/mysqld.cnf (or /etc/my.cnf):
[mysqld]
# InnoDB buffer pool -- set to ~70% of available RAM on a dedicated DB server
innodb_buffer_pool_size = 2G
# Log slow queries for performance analysis
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
# Connection limits
max_connections = 200
# Temp tables
tmp_table_size = 64M
max_heap_table_size = 64M
sudo systemctl restart mysql
PostgreSQL
Installation
# Debian/Ubuntu
sudo apt update
sudo apt install postgresql postgresql-client
sudo systemctl enable --now postgresql
# RHEL/CentOS/Fedora
sudo dnf install postgresql-server postgresql
sudo postgresql-setup --initdb
sudo systemctl enable --now postgresql
Database and User Management
PostgreSQL uses its own authentication system. By default, the postgres
OS user maps to the postgres superuser:
# Create a user (role)
sudo -u postgres createuser --interactive appuser
# Create a database owned by that user
sudo -u postgres createdb -O appuser webapp
# Or from the psql shell:
sudo -u postgres psql
-- Inside psql
CREATE USER appuser WITH PASSWORD 'StrongPassword123!';
CREATE DATABASE webapp OWNER appuser;
-- Grant specific privileges
GRANT CONNECT ON DATABASE webapp TO reporter;
GRANT USAGE ON SCHEMA public TO reporter;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO reporter;
-- List databases
\l
-- List users
\du
-- Connect to a database
\c webapp
Backup with pg_dump
# Dump a single database (custom format -- compressed, supports parallel restore)
sudo -u postgres pg_dump -Fc webapp > webapp.dump
# Dump in plain SQL
sudo -u postgres pg_dump webapp > webapp.sql
# Dump all databases
sudo -u postgres pg_dumpall > all_databases.sql
# Restore from custom format
sudo -u postgres pg_restore -d webapp webapp.dump
# Restore from plain SQL
sudo -u postgres psql webapp < webapp.sql
Basic Tuning
Edit /etc/postgresql/16/main/postgresql.conf (adjust version number):
# Shared buffers -- set to ~25% of available RAM
shared_buffers = 1GB
# Work memory for sorts and joins (per operation)
work_mem = 64MB
# Maintenance tasks (VACUUM, CREATE INDEX)
maintenance_work_mem = 256MB
# WAL settings for write-heavy workloads
wal_buffers = 64MB
checkpoint_completion_target = 0.9
# Connection limits
max_connections = 200
# Effective cache size (estimate of OS file cache -- ~75% of RAM)
effective_cache_size = 3GB
# Logging
log_min_duration_statement = 1000 # log queries taking > 1 second
sudo systemctl restart postgresql
Use PGTune for automated tuning recommendations based on your hardware.
Replication Concepts
Both MySQL and PostgreSQL support replication for high availability and read scaling:
| Concept | MySQL | PostgreSQL |
|---|---|---|
| Streaming replication | Binary log replication (source/replica) | WAL streaming (primary/standby) |
| Read replicas | read_only = ON on replica |
Hot standby |
| Failover | MySQL Group Replication, ProxySQL | Patroni, pg_auto_failover |
| Logical replication | binlog_format = ROW + GTID |
Logical replication slots |
Setting up replication is a significant topic. The key principle: the primary writes data, replicas receive and replay those writes, and your application routes reads to replicas and writes to the primary.
Monitoring
# MySQL -- check running queries
mysqladmin -u root -p processlist
# MySQL -- show engine status
mysql -u root -p -e "SHOW ENGINE INNODB STATUS\G"
# PostgreSQL -- check active queries
sudo -u postgres psql -c "SELECT pid, state, query FROM pg_stat_activity WHERE state != 'idle';"
# PostgreSQL -- check database sizes
sudo -u postgres psql -c "SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database ORDER BY pg_database_size(datname) DESC;"
Return to the Web Servers hub or continue to Performance Tuning and Nginx Guide.