tutorial

Monitoring Percona Server: MySQL, MongoDB, PMM Integration, and Vigilmon for Database SLAs

"A practical guide to monitoring Percona Server for MySQL and MongoDB — slow queries, replication lag, InnoDB buffer pool, PMM integration, and Vigilmon external checks for production database SLAs."

Monitoring Percona Server: MySQL, MongoDB, PMM Integration, and Vigilmon for Database SLAs

Percona Server for MySQL and Percona Server for MongoDB are production-hardened forks of their upstream counterparts, adding observability hooks, improved diagnostics, and performance instrumentation. This guide covers what to monitor in a Percona deployment, how to use Percona Monitoring and Management (PMM), and how to wire up Vigilmon for external database SLA enforcement.

Monitoring Percona Server for MySQL

Slow Query Detection

The most actionable signal in any MySQL deployment is the slow query log. Percona Server extends it with microsecond timestamps and extra fields:

-- Enable slow query log
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;  -- log queries > 1 second
SET GLOBAL log_slow_extra = 'ON';  -- Percona-specific: extra fields

-- Find the log location
SHOW VARIABLES LIKE 'slow_query_log_file';

Parse the slow query log with pt-query-digest from Percona Toolkit:

pt-query-digest /var/log/mysql/slow.log \
  --limit 10 \
  --output json > slow_query_report.json

For real-time visibility, set log_output = TABLE and query mysql.slow_log:

SELECT 
  sql_text,
  query_time,
  lock_time,
  rows_examined,
  rows_sent,
  start_time
FROM mysql.slow_log
ORDER BY query_time DESC
LIMIT 20;

InnoDB Buffer Pool Health

The InnoDB buffer pool is your primary performance lever. Monitor hit ratio and dirty page ratio:

SELECT 
  (1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests)) * 100 
    AS buffer_pool_hit_ratio_pct,
  (Innodb_buffer_pool_pages_dirty / Innodb_buffer_pool_pages_total) * 100
    AS dirty_pages_pct,
  Innodb_buffer_pool_pages_free,
  Innodb_buffer_pool_pages_total
FROM (
  SELECT 
    VARIABLE_VALUE AS Innodb_buffer_pool_reads
  FROM performance_schema.global_status 
  WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads'
) r
CROSS JOIN (
  SELECT VARIABLE_VALUE AS Innodb_buffer_pool_read_requests 
  FROM performance_schema.global_status 
  WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests'
) rr
-- ... (simplify by querying global_status directly)
;

-- Simpler version
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool%';

Target metrics:

  • Hit ratio > 99% (below 95% means the buffer pool is undersized)
  • Dirty pages < 75% (high dirty ratio causes checkpoint stalls)

Replication Lag

For MySQL replication, the standard check:

SHOW SLAVE STATUS\G
-- or on MySQL 8+
SHOW REPLICA STATUS\G

Key fields:

  • Seconds_Behind_Master — lag in seconds; alert > 30s
  • Slave_IO_Running: Yes — IO thread must be running
  • Slave_SQL_Running: Yes — SQL thread must be running
  • Last_Error — non-empty means replication has broken

Automate this check:

mysql -u monitor -ppassword -e "SHOW SLAVE STATUS\G" \
  | grep -E 'Seconds_Behind_Master|Slave_IO_Running|Slave_SQL_Running|Last_Error'

Connection and Thread Health

-- Current connections by state
SELECT STATE, COUNT(*) AS cnt 
FROM information_schema.PROCESSLIST 
GROUP BY STATE 
ORDER BY cnt DESC;

-- Max connections utilization
SELECT 
  VARIABLE_VALUE AS max_connections 
FROM performance_schema.global_variables 
WHERE VARIABLE_NAME = 'max_connections';

SELECT 
  VARIABLE_VALUE AS threads_connected 
FROM performance_schema.global_status 
WHERE VARIABLE_NAME = 'Threads_connected';

Alert when Threads_connected / max_connections > 0.8.

Monitoring Percona Server for MongoDB

Replication Lag

In a MongoDB replica set, replication lag is the delta between the primary's oplog and a secondary's last applied optime:

// In mongosh
rs.printSecondaryReplicationInfo()

// Programmatic
const status = rs.status();
status.members.forEach(m => {
  if (m.state === 2) {  // SECONDARY
    const lagSeconds = (status.members[0].optimeDate - m.optimeDate) / 1000;
    print(`${m.name}: lag=${lagSeconds}s`);
  }
});

Alert when lag exceeds your maxStalenessSeconds read preference setting.

WiredTiger Cache Health

db.serverStatus().wiredTiger.cache
// Key fields:
// "bytes currently in the cache"
// "maximum bytes configured"
// "pages read into cache"
// "pages written from cache"
// "tracked dirty bytes in the cache"
# Via mongostat
mongostat --host localhost:27017 \
  -u admin -p password \
  --authenticationDatabase admin \
  -n 60 \
  --humanReadable

Watch faults (page faults indicate cache pressure) and qrw (queued reads/writes indicate lock contention).

Oplog Window

The oplog window determines how long a secondary can be offline before it needs a full resync:

// Check oplog window size in hours
const oplog = db.getSiblingDB('local').oplog.rs;
const firstTs = oplog.find().sort({$natural: 1}).limit(1).next().ts;
const lastTs = oplog.find().sort({$natural: -1}).limit(1).next().ts;
const windowHours = (lastTs.t - firstTs.t) / 3600;
print(`Oplog window: ${windowHours.toFixed(1)} hours`);

Alert when the oplog window drops below 24 hours.

PMM Integration

Percona Monitoring and Management (PMM) is the recommended observability stack for Percona deployments. It ships Grafana dashboards, Prometheus exporters, and query analytics out of the box.

Install PMM Server

docker run -d \
  -p 80:80 -p 443:443 \
  --name pmm-server \
  percona/pmm-server:2

Register a MySQL Client

# Install pmm-client on the database host
apt-get install pmm2-client

# Configure server connection
pmm-admin config \
  --server-insecure-tls \
  --server-url=https://admin:admin@pmm-server-host

# Add MySQL service
pmm-admin add mysql \
  --username=pmm \
  --password=pmm_password \
  --host=localhost \
  --port=3306 \
  --service-name=prod-mysql

Register a MongoDB Client

pmm-admin add mongodb \
  --username=pmm \
  --password=pmm_password \
  --host=localhost \
  --port=27017 \
  --service-name=prod-mongodb

PMM will now scrape metrics every 5 seconds and populate the built-in dashboards for query analytics, InnoDB metrics, replication lag, and more.

Vigilmon Integration

PMM is excellent for internal observability, but Vigilmon provides an external SLA enforcement layer — confirming your databases are reachable from outside your network and alerting your on-call team via independent channels.

Step 1 — create a health check endpoint

from flask import Flask, jsonify
import pymysql
import pymongo

app = Flask(__name__)

MYSQL_CONFIG = {
    "host": "localhost", "port": 3306,
    "user": "monitor", "password": "monitor_pass",
    "database": "mysql", "connect_timeout": 3
}

MONGO_URI = "mongodb://monitor:monitor_pass@localhost:27017/admin"

@app.route("/health/percona/mysql")
def mysql_health():
    try:
        conn = pymysql.connect(**MYSQL_CONFIG)
        cur = conn.cursor()

        # Check replication
        cur.execute("SHOW SLAVE STATUS")
        row = cur.fetchone()
        if row:
            lag = row[32]  # Seconds_Behind_Master
            io_running = row[10]   # Slave_IO_Running
            sql_running = row[11]  # Slave_SQL_Running
            if io_running != "Yes" or sql_running != "Yes":
                conn.close()
                return jsonify({"error": "replication stopped"}), 503
            if lag is not None and lag > 30:
                conn.close()
                return jsonify({"error": f"replication lag {lag}s"}), 503

        conn.close()
        return jsonify({"status": "ok"}), 200
    except Exception as e:
        return jsonify({"error": str(e)}), 503

@app.route("/health/percona/mongodb")
def mongo_health():
    try:
        client = pymongo.MongoClient(MONGO_URI, serverSelectionTimeoutMS=3000)
        client.admin.command("ping")

        # Check replica set health
        status = client.admin.command("replSetGetStatus")
        primary_count = sum(1 for m in status["members"] if m["stateStr"] == "PRIMARY")
        if primary_count == 0:
            return jsonify({"error": "no primary in replica set"}), 503

        return jsonify({"status": "ok", "primary_count": primary_count}), 200
    except Exception as e:
        return jsonify({"error": str(e)}), 503

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=9090)

Step 2 — add Vigilmon monitors

In Vigilmon, configure:

  1. HTTP monitorhttps://your-host:9090/health/percona/mysql — interval 60s
  2. HTTP monitorhttps://your-host:9090/health/percona/mongodb — interval 60s
  3. TCP monitormysql-host:3306 — basic connectivity
  4. TCP monitormongo-host:27017 — basic connectivity

Set up alert policies pointing to PagerDuty, Slack, or email for each monitor.

Alert Thresholds

| Metric | Warning | Critical | |--------|---------|----------| | MySQL slow query rate | > 5/min | > 50/min | | InnoDB hit ratio | < 98% | < 95% | | Replication lag (MySQL) | > 10s | > 60s | | Threads_connected / max | > 70% | > 90% | | MongoDB replication lag | > 30s | > 120s | | WiredTiger cache dirty % | > 20% | > 40% | | Oplog window | < 48h | < 12h |

Conclusion

Percona Server provides richer diagnostics than upstream MySQL and MongoDB, and PMM gives you a purpose-built monitoring stack for Percona deployments. Pair PMM's internal dashboards with Vigilmon's external HTTP and TCP monitors to cover both in-cluster observability and external SLA verification. With slow query alerts, replication lag checks, and buffer pool monitoring in place, your Percona databases can run in production with confidence.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →