MetricHarborDocs

Monitoring MySQL and MariaDB

MetricHarbor reads server counters through SHOW GLOBAL STATUS and SHOW GLOBAL VARIABLES, replication state through SHOW REPLICA STATUS, and — optionally — per-schema sizes through information_schema.TABLES.

That last one forces a choice MySQL gives you no way to avoid. Decide it deliberately rather than discovering it.

The access trade-off

information_schema.TABLES only lists tables the user holds some privilege on. So a monitoring user with no SELECT gets zero rows and no error — the check reports a clean success with no schema sizes at all. Verified on MariaDB 11.8.6: a user with only the replication grant sees exactly 0 user tables there, and the query succeeds. That silent emptiness reads like a bug in MetricHarbor, which is why it is the first thing on this page.

PostgreSQL has a role that grants statistics without data access. MySQL does not. So:

MySQL:

CREATE USER 'metricharbor'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT REPLICATION CLIENT ON *.* TO 'metricharbor'@'localhost';
FLUSH PRIVILEGES;

MariaDB — the grant is different, and REPLICATION CLIENT is not enough:

DROP USER IF EXISTS 'metricharbor'@'localhost';
CREATE USER 'metricharbor'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT SLAVE MONITOR ON *.* TO 'metricharbor'@'localhost';
FLUSH PRIVILEGES;

MariaDB 10.5 split the old REPLICATION CLIENT privilege in two — BINLOG MONITOR for binary log status, and SLAVE MONITOR for SHOW REPLICA STATUS — and kept REPLICATION CLIENT as an alias for BINLOG MONITOR only. So granting REPLICATION CLIENT on MariaDB is accepted, shows up in SHOW GRANTS under a different name, and still leaves SHOW REPLICA STATUS refused with:

ERROR 1227 (42000): Access denied; you need (at least one of) the SLAVE MONITOR privilege(s)

Verified on MariaDB 11.8.6. The collector reports that refusal as a warning on every poll rather than failing the check, so the symptom is replication metrics that never appear while everything else looks healthy.

Then turn off "Per-schema sizes" on the check.

This user can read server counters and replication state, and nothing else: not a row of data, not a schema name. You lose per-schema size metrics.

Option B — per-schema sizes, at the cost of read access

DROP USER IF EXISTS 'metricharbor'@'localhost';
CREATE USER 'metricharbor'@'localhost' IDENTIFIED BY 'a-long-random-password';
-- MySQL:
GRANT SELECT, REPLICATION CLIENT ON *.* TO 'metricharbor'@'localhost';
-- MariaDB:
GRANT SELECT, SLAVE MONITOR ON *.* TO 'metricharbor'@'localhost';
FLUSH PRIVILEGES;

DROP USER IF EXISTS first, on purpose. Running CREATE USER for an account that already exists fails with

ERROR 1396 (HY000): Operation CREATE USER failed for 'metricharbor'@'localhost'

which says nothing about why. Dropping first makes the whole block safe to re-run — on a second attempt, after a typo, or on a host somebody else already set up.

Not CREATE USER IF NOT EXISTS, which looks tidier and hides a worse problem: if the account exists with a different password, that form succeeds, changes nothing, and leaves you with a password that is not the one you just typed. The failure then arrives later as an authentication error with no connection to this step.

SELECT ON *.* makes information_schema.TABLES complete. It also lets that user read every row in the server. If you take this option, the argument for keeping the credential off MetricHarbor's servers gets stronger, not weaker — and it already is off them.

The replication grant — REPLICATION CLIENT on MySQL, SLAVE MONITOR on MariaDB — is needed only for SHOW REPLICA STATUS. If you turn the replication option off on the check as well, Option A needs no grant at all beyond the account existing.

Let the agent reach it

If the agent runs on the database host:

# /etc/mysql/mysql.conf.d/mysqld.cnf
bind-address = 127.0.0.1

Prefer 'metricharbor'@'localhost' over 'metricharbor'@'%' and point the target at 127.0.0.1. A monitoring account that can log in from anywhere is a wider surface than it needs to be.

Supply the password and add the check

runuser -u metricharbor -- metricharbor-agent cred add mysql-monitor --type database_password

Then Targets → Add target → MySQL / MariaDB database (host 127.0.0.1, port 3306, the credential, and the agent on that host), and Add a check → MySQL / MariaDB on the target page.

The database name is optional here, unlike PostgreSQL: everything this collector reads is server-wide.

What it reads

Source For
SHOW GLOBAL STATUS connections, queries, slow queries, buffer pool, table locks, aborted clients
SHOW GLOBAL VARIABLES max_connections and friends, for usage percentages
SHOW REPLICA STATUS / SHOW SLAVE STATUS replication running, seconds behind source
information_schema.TABLES per-schema size and table count — optional, see above

MariaDB and older MySQL use SHOW SLAVE STATUS; the collector tries the modern name first and falls back, so you do not configure anything for this.

Options on the check

Option Default Notes
Per-schema sizes on Queries information_schema.TABLES. Slow on a server with tens of thousands of tables, and needs SELECT — see the trade-off above.
Replication status on Turn off on a standalone server to avoid needing REPLICATION CLIENT.
Statement timeout 10s So a pathological query cannot make collection hang.

What you should see

success with a sample count, and counters under Latest readings. Most MySQL metrics are single-series; the per-label-set shape earns its keep on schemas, and on disks and pods elsewhere.

Error Cause
authentication naming your credential No secret under that name on the agent, or a name mismatch. If you created the database user more than once, the password may not be the one you think — see DROP USER IF EXISTS above.
connection naming the host bind-address, a firewall, the wrong port, or the account is @localhost while the target points at a LAN address.
permission_denied on SHOW REPLICA STATUS The replication grant is missing. On MariaDB this is SLAVE MONITOR, not REPLICATION CLIENT — see Option A. Or turn the replication option off.
success, but no per-schema sizes The trade-off at the top of this page. The user has no SELECT, so information_schema.TABLES returned nothing.

More in When nothing is collecting.