# MySQL

The deployment target pages cover *where* the collector runs. This page covers what **MySQL itself**
needs before the collector can read it, because that part does not carry over from PostgreSQL. The
grants are different, the statistics prerequisite is different, and the way you enable it is
different.

Run the collector on whichever target suits you: [Docker](/docs/getting-started/collector-installation/local-docker/),
[Kubernetes](/docs/getting-started/collector-installation/kubernetes/), or alongside
[AWS RDS and Aurora](/docs/getting-started/collector-installation/aws-rds-aurora/). Then come back
here for the database-side setup.

## Supported versions

**MySQL 8.0 and above.** Anything older is refused at discovery with an explicit error rather than
degrading quietly. The reason is `performance_schema`: its tables are unindexed before 8.0, so the
activity queries this collector runs would be pathologically slow.

| Product | Supported today | Notes |
|---|---|---|
| MySQL 8.0 and above | **Yes** | Full feature set. |
| Aurora MySQL 3 | **Yes** | 8.0-based. |
| MySQL 5.7 and older | Not yet | `performance_schema` tables are unindexed before 8.0. |
| Aurora MySQL 2 | Not yet | 5.7-based. Aurora MySQL 3 works today. |
| MariaDB | Not yet | Shares MySQL's wire protocol, not its `performance_schema` layout. |

Self-managed, AWS RDS for MySQL and Aurora MySQL 3 are all supported.

Anything not on the supported list is refused at connection time with a named error, rather than
connecting and reporting partial data. An unparseable version string is refused for the same
reason: not knowing what the server is does not get treated as "probably fine".

:::tip[Want an engine we do not cover yet?]
We add databases regularly, and what customers ask for is what we build next.
**[Tell us which engine you want](https://www.dbgorilla.com/contact-us)** and we will let you know
when it lands.
:::

## Database grants

Create a dedicated monitoring login. As with PostgreSQL, **"read-only" is several grants, not one**.
They are not the same several:

```sql
CREATE USER 'dbg_monitor'@'%' IDENTIFIED BY 'replace-me';

GRANT SELECT ON performance_schema.* TO 'dbg_monitor'@'%';
GRANT SELECT ON information_schema.* TO 'dbg_monitor'@'%';
GRANT PROCESS ON *.* TO 'dbg_monitor'@'%';             -- see other sessions in the processlist
GRANT REPLICATION CLIENT ON *.* TO 'dbg_monitor'@'%';  -- replica lag metrics

-- plus SELECT on each database you want schema captured from:
GRANT SELECT ON app.* TO 'dbg_monitor'@'%';

FLUSH PRIVILEGES;
```

`PROCESS` is what lets the collector see sessions other than its own. Without it the collector still
connects, and activity monitoring shows you only the collector.

**A missing `REPLICATION CLIENT` is not fatal.** It is logged and skipped, and you lose replica lag
metrics only. That is the one grant here you can defer.

Schema capture shells out to `mysqldump`, which must be able to `SELECT` every table it reads, so
repeat the per-database `GRANT SELECT` for every database you list in `databases`.

## performance_schema

This is the MySQL counterpart to PostgreSQL's `pg_stat_statements`, and it is where most MySQL
onboardings stall.

**`performance_schema = ON` is required.** It is on by default on RDS, Aurora and most installs.
Without it there are no statement digests and no wait attribution.

**Turning it on is not enough.** `performance_schema = ON` does not enable all of its *consumers*,
and three of them carry features you will otherwise be missing without being told:

| Consumer | What you lose without it |
|---|---|
| `statements_digest` | The workload catalog: query text and per-digest statistics |
| `events_statements_current` | The link from an active session to its digest, and picosecond query durations |
| `events_waits_current` | Real wait events. Attribution falls back to a thread-state heuristic. |

Check what is actually on:

```sql
SELECT NAME, ENABLED FROM performance_schema.setup_consumers;
```

:::caution[`events_waits_current` is the one that catches people]

It is **off by default on RDS**, and enabling it with a runtime `UPDATE` **does not survive a restart
or a failover**. Set it in the parameter group on RDS and Aurora, or in `my.cnf` for self-managed,
so it persists. A runtime-only change works right up until the next failover, then silently stops.
:::

### Replicas on self-managed MySQL

For self-managed replica discovery, each replica must set `report_host` in its own `my.cnf`.
`SHOW REPLICAS` reports a replica's host only when the replica sets it. Without it the row comes
back with an empty host and that replica cannot be monitored.

This does not apply to Aurora, which has no binlog replication. Its members come from the RDS API
instead.

## Configuration

The `[[component]]` block is the same shape as PostgreSQL's, with `engine = "mysql"`, port 3306, and
MySQL's own SSL vocabulary.

```toml
[dbgorilla]
agent_id  = "a8c1cde3-3e91-4ecf-b615-26bae0c35f02"
tenant_id = "fd4c6676-c1a1-4d9a-babb-54958c26369d"
secret    = "${DBG_SERVER_SECRET}"

[[component]]
name   = "staging-mysql"
engine = "mysql"

[component.provider]
type = "self_hosted"

[component.auth]
method   = "password"
user     = "dbg_monitor"
password = "${MYSQL_PASSWORD}"

[component.connect]
host      = "mysql.internal"
port      = 3306
databases = ["app"]
ssl_mode  = "verify_identity"   # disabled | required | verify_ca | verify_identity (default)
```

:::danger[`ssl_mode` does not use the PostgreSQL spellings, and the key name matters too]

MySQL uses its own `--ssl-mode` vocabulary: `disabled`, `required`, `verify_ca`, `verify_identity`.
Note the underscores rather than hyphens. The default is `verify_identity`, the strictest of the
four.

**A wrong value fails loudly. A wrong key name fails silently.** They are not the same mistake:

| What you write | What happens |
|---|---|
| `ssl_mode = "verify-full"` | **The collector refuses to start**, naming the bad value and listing the four valid ones. |
| `sslmode = "verify-full"` | Unknown key. Logged as a warning, then **ignored**, and you silently get the `verify_identity` default. |

So copying `ssl_mode` across from a PostgreSQL component stops the collector, which is what you
want. Copying `sslmode`, libpq's spelling of the key itself, leaves a setting that looks applied
and is not. Check the startup log for an "ignoring unrecognised key" warning.
:::

### AWS RDS with IAM authentication

```toml
[[component]]
name   = "prod-rds-mysql"
engine = "mysql"

[component.provider]
type        = "aws_rds"
region      = "us-east-1"
instance_id = "prod-mysql"

[component.auth]
method = "iam"
user   = "dbg_iam_user"

[component.connect]
host      = "prod-mysql.abc.us-east-1.rds.amazonaws.com"
port      = 3306
databases = ["app"]
```

Everything else works exactly as described in
[Collector Installation](/docs/getting-started/collector-installation/overview/): the `[dbgorilla]`
block, endpoints, the optional `[commands]` block, and outbound network requirements.