| name | mysql |
| description | Administer MySQL/MariaDB databases. Configure replication and optimize performance. Use when managing MySQL deployments. |
| license | MIT |
| metadata | {"author":"devops-skills","version":"1.0"} |
MySQL / MariaDB
Administer, optimize, and secure MySQL and MariaDB databases in development and production environments.
When to Use
- You need a mature, widely supported relational database.
- Your stack depends on MySQL-specific features or compatibility (WordPress, Magento, many PHP frameworks).
- You are setting up source-replica replication for read scaling.
- You want to tune InnoDB for high-throughput transactional workloads.
Prerequisites
- Linux server (Debian/Ubuntu or RHEL-based) or Docker.
- Root or sudo access for package installation.
- Familiarity with SQL fundamentals.
Installation and Setup
sudo apt update
sudo apt install -y mysql-server
sudo dnf install -y mysql-server
sudo systemctl enable --now mysqld
sudo mysql_secure_installation
mysql --version
sudo systemctl status mysql
Initial User and Database Setup
sudo mysql -u root -p
CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'myapp'@'%' IDENTIFIED BY 'strong_password_here';
GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO 'myapp'@'%';
FLUSH PRIVILEGES;
SHOW GRANTS FOR 'myapp'@'%';
mysql CLI Reference
mysql -u myapp -p -h 127.0.0.1 mydb
mysql -u myapp -p -e "SELECT COUNT(*) FROM orders;" mydb
mysql -u myapp -p mydb < schema.sql
mysql -u myapp -p -e "SELECT * FROM users" mydb \
| tr '\t' ',' > users.csv
-- Inside the mysql shell
SHOW DATABASES;
USE mydb;
SHOW TABLES;
DESCRIBE users;
SHOW CREATE TABLE users\G
SHOW PROCESSLIST;
SHOW ENGINE INNODB STATUS\G
Configuration Tuning
Edit /etc/mysql/mysql.conf.d/mysqld.cnf (or /etc/my.cnf on RHEL).
[mysqld]
bind-address = 0.0.0.0
max_connections = 300
wait_timeout = 600
interactive_timeout = 600
innodb_buffer_pool_size = 4G
innodb_buffer_pool_instances = 4
innodb_log_file_size = 1G
innodb_flush_log_at_trx_commit = 1
innodb_flush_method = O_DIRECT
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_error = /var/log/mysql/error.log
server-id = 1
log_bin = /var/log/mysql/mysql-bin
binlog_expire_logs_seconds = 604800
=
= utf8mb4
= utf8mb4_unicode_ci
sudo systemctl restart mysql
mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
Backup and Restore
Logical Backups with mysqldump
mysqldump -u root -p --single-transaction --routines --triggers \
mydb > /backups/mydb_$(date +%F).sql
mysqldump -u root -p --all-databases --single-transaction \
> /backups/all_$(date +%F).sql
mysqldump -u root -p --single-transaction mydb \
| gzip > /backups/mydb_$(date +%F).sql.gz
mysql -u root -p mydb < /backups/mydb_2025-01-15.sql
gunzip < /backups/mydb_2025-01-15.sql.gz | mysql -u root -p mydb
Physical Backups with Percona XtraBackup
sudo apt install -y percona-xtrabackup-80
xtrabackup --backup --user=root --password=secret \
--target-dir=/backups/full_$(date +%F)
xtrabackup --prepare --target-dir=/backups/full_2025-01-15
sudo systemctl stop mysql
sudo rm -rf /var/lib/mysql/*
xtrabackup --move-back --target-dir=/backups/full_2025-01-15
sudo chown -R mysql:mysql /var/lib/mysql
sudo systemctl start mysql
Incremental Backup with XtraBackup
xtrabackup --backup --user=root --password=secret \
--target-dir=/backups/inc_$(date +%F) \
--incremental-basedir=/backups/full_2025-01-15
xtrabackup --prepare --apply-log-only --target-dir=/backups/full_2025-01-15
xtrabackup --prepare --target-dir=/backups/full_2025-01-15 \
--incremental-dir=/backups/inc_2025-01-16
Source-Replica Replication
Source (Primary)
[mysqld]
server-id = 1
log_bin = /var/log/mysql/mysql-bin
binlog_format = ROW
CREATE USER 'replicator'@'10.0.0.%' IDENTIFIED BY 'repl_secret';
GRANT REPLICATION SLAVE ON *.* TO 'replicator'@'10.0.0.%';
FLUSH PRIVILEGES;
SHOW MASTER STATUS\G
Replica
[mysqld]
server-id = 2
relay_log = /var/log/mysql/relay-bin
read_only = ON
CHANGE REPLICATION SOURCE TO
SOURCE_HOST = '10.0.0.1',
SOURCE_USER = 'replicator',
SOURCE_PASSWORD = 'repl_secret',
SOURCE_LOG_FILE = 'mysql-bin.000003',
SOURCE_LOG_POS = 154;
START REPLICA;
SHOW REPLICA STATUS\G
Monitoring Queries
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Max_used_connections';
SELECT
ROUND(100 - (
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') /
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests')
) * 100, 2) AS buffer_pool_hit_pct;
SELECT DIGEST_TEXT, COUNT_STAR, AVG_TIMER_WAIT / 1e12 AS avg_sec
FROM performance_schema.events_statements_summary_by_digest
ORDER BY AVG_TIMER_WAIT DESC
LIMIT 10;
SELECT table_name,
ROUND(data_length / 1024 / 1024, 2) AS data_mb,
ROUND(index_length / 1024 / 1024, 2) index_mb,
table_rows
information_schema.tables
table_schema
data_length ;
REPLICA STATUS\G
Docker Compose Setup
version: "3.9"
services:
mysql:
image: mysql:8.0
restart: unless-stopped
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: mydb
MYSQL_USER: myapp
MYSQL_PASSWORD: secret
volumes:
- mysql_data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
command: >
--innodb-buffer-pool-size=512M
--max-connections=200
--slow-query-log=ON
--long-query-time=1
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-prootpass"]
interval: 10s
timeout: 5s
retries: 5
phpmyadmin:
image: phpmyadmin:latest
restart: unless-stopped
ports:
-
docker compose up -d
mysql -h 127.0.0.1 -u myapp -psecret mydb
Maintenance Tasks
mysql -u root -p -e "OPTIMIZE TABLE mydb.orders;"
mysql -u root -p -e "ANALYZE TABLE mydb.orders;"
mysql -u root -p -e "CHECK TABLE mydb.orders;"
mysql -u root -p -e "REPAIR TABLE mydb.orders;"
sudo mv /var/log/mysql/slow.log /var/log/mysql/slow.log.old
mysqladmin -u root -p flush-logs
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|
Too many connections | Connection limit exceeded | Increase max_connections; use connection pooling (ProxySQL) |
| Slow queries across the board | innodb_buffer_pool_size too small | Set to ~70% of available RAM and restart |
Replication stopped (SQL_Running: No) | Duplicate key or schema mismatch on replica | Check SHOW REPLICA STATUS\G error; skip or fix the row |
Table is full | Disk space exhausted or table limit hit | Free disk space; check innodb_data_file_path autoextend |
Lock wait timeout exceeded | Long-running transaction holding row locks | Identify with SHOW ENGINE INNODB STATUS; kill the blocking query |
| High IOPS / disk usage | Redo log too small causing frequent flushes | Increase innodb_log_file_size (requires restart) |
Related Skills