Top key metrics for monitoring MySQL
Published: 2021-11-05 08:39:49

Monitoring MySQL with Prometheus is easy to do thanks to the MySQL Prometheus Exporter. MySQL doesn’t need an introduction – it’s one of the most used relational databases in the world, and it’s also open-source!
​
Being such a popular database means that the community behind it is also huge. So don’t worry: you won’t be alone.

​
![illustration showing 5 cute little monitors with metrics in a podium](https://sysdig.com/wp-content/uploads/Blog-How-to-monitor-MySQL-Featured-image.jpg)


In this article, you’ll learn how to start monitoring a MySQL Server with Prometheus – from configuring the exporter to what are the top metrics, with alert examples. If you’re running MySQL with RDS, don’t miss our ​​[top 5 key metrics for monitoring Amazon RDS](https://sysdig.com/blog/monitoring-amazon-rds/).


Setting things up
-----------------
Before deploying the exporter in the cluster, you have to create the user and password for the exporter in the database. Enter your MySQL database and execute the following queries:
​
```sql
CREATE USER 'exporter'@'%' IDENTIFIED BY 'YOUR-PASSWORD' WITH MAX_USER_CONNECTIONS 3;

GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'%';
```

Substitute the user name and the password in the SQL sentence for your custom ones.
​
Once you have the user and password for the exporter in the database, you have to create a `mysql-exporter.cnf` file with the credentials of the exporter. Also, in this file you will include the url to connect your MySQL database in the field `host`:
```ini​
[client]
user = exporter
password = "YOUR-PASSWORD"
host=YOUR-DB-IP
```

In your Kubernetes cluster, create the secret with the `mysql-exporter.cnf` file. This file will be mounted in the exporter to authenticate with the database:
```bash
kubectl create secret generic mysql-exporter \
  --from-file=.my.cnf=./mysql-exporter.cnf
```

How to deploy the MySQL Prometheus Exporter
-------------------------------------------
​
To start monitoring MySQL with Prometheus, you’ll need to deploy [the MySQL Exporter](https://github.com/prometheus/mysqld_exporter) in your cluster.
​
To deploy the exporter, you can use the example files provided below.
​
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-exporter-exporter
spec:
  selector:
    matchLabels:
      app: mysql-exporter
  replicas: 1
  template:
    metadata:
      labels:
        app: mysql-exporter
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9104"
    spec:
      containers:
        - name: mysql-exporter
          image: prom/mysqld-exporter:latest
          args:
            - --config.my-cnf=/tmp/.my.cnf
          ports:
            - containerPort: 9104
          volumeMounts:
            - name: my-cnf
              mountPath: /tmp/.my.cnf
              subPath: .my.cnf
          resources:
            limits:
              memory: "256Mi"
              cpu: "256m"
      volumes:
        - name: my-cnf
          secret:
            defaultMode: 420
            secretName: mysql-exporter
            items:
              - key: .my.cnf
                path: .my.cnf
```
After applying the Deployment for the exporter, Prometheus will automatically start scraping the MySQL metrics as it already has the standard annotations.


Top 5 metrics to monitor MySQL with Prometheus
----------------------------------------------
​
### #1 Availability
There are two metrics that monitor the availability of your MySQL instance. The first one is `mysql_up`. If this metric equals zero, the exporter cannot access the database, which can be a symptom of an unhealthy or failed database.
​
You can create an alert to notify you in case of a database down with the following query:
```r
mysql_up == 0
```

Also, the metric `mysql_global_status_uptime` can give you an idea of quick restarts that can pass under the radar of the previous metric. You can create an alert with the following Prometheus query to detect instances that have an uptime of less than half an hour:
```r
mysql_global_status_uptime < 1800
```
​
### #2 Connections
One of the main sources of errors in databases are connection errors. The metric `mysql_global_status_connection_errors_total` allows you to detect when the database is generating these errors:
​```r
rate(mysql_global_status_connection_errors_total[5m])
```

One common cause of connection error is the lack of available connections. You can check the percent of available connections used with this Prometheus query:
```r​
100 * mysql_global_status_threads_connected / mysql_global_variables_max_connections
```

If this value is near 100, the database can start refusing new connections. A solution for this can be increasing the number of maximum connections. Before doing that, be sure to check the available number of open files in the system. You can check it with the following Prometheus query:
```r​
mysql_global_variables_open_files_limit - mysql_global_variables_innodb_open_files
```

_Want to dig deeper into PromQL? Read our [getting started with PromQL](https://sysdig.com/blog/getting-started-with-promql-cheatsheet/) guide to learn how Prometheus stores data, and how to use PromQL functions and operators._
​
### #3 Slow queries
Like many databases, MySQL keeps a log for slow queries. The number of entries in this log can be consulted with the metric `mysql_global_status_slow_queries`. You can create an alert with the following Prometheus query to notify when there are new entries in the slow queries log, which can mean that there is a performance issue:
```r​
rate(mysql_global_status_slow_queries[5m])
```

### #4 Cache Hit Rate
MySQL uses in-memory cache to optimize the disk read and write operations. A low hit rate in the cache will impact the performance of the database. This Prometheus query will give you the value of the [open tables cache](https://dev.mysql.com/doc/refman/8.0/en/table-cache.html) hit rate:
```r​
rate(mysql_global_status_table_open_cache_hits[5m]) /
(rate(mysql_global_status_table_open_cache_hits[5m]) + rate(mysql_global_status_table_open_cache_misses[5m]))
```

Also, you can monitor the [buffer pool](https://dev.mysql.com/doc/refman/5.6/en/innodb-buffer-pool.html) cache with the following promQL query:
```r
(rate(mysql_global_status_innodb_buffer_pool_read_requests[5m]) -
rate(mysql_global_status_innodb_buffer_pool_reads[5m])) / rate(mysql_global_status_innodb_buffer_pool_read_requests[5m]))
```
​
You can tune the buffer size for open tables and pool caches in the MySQL configuration, but keep in mind that doing so will affect the memory usage of your instance. Be sure that there is enough memory. You can check the available memory of the container using cAdvisor metrics:
```r
sum by (namespace,pod,container)((container_memory_usage_bytes{container!="POD",container!=""} -  on (namespace,pod,container) avg by (namespace,pod,container)(kube_pod_container_resource_limits{resource="memory"})) \* -1 >0 ) / (1024\*1024\*1024)
```

If your MySQL instance is in AWS RDS, check our blog post on [how to monitor your RDS instance](https://sysdig.com/blog/monitoring-amazon-rds/)
​
### #5 Query types
You can get statistics of the usage of each kind of SELECT query in your MySQL database, using the metric that gives information on each of them:
​
*   [Full join](https://www.w3schools.com/sql/sql_join_full.asp): mysql_global_status_select_full_join
*   [Full range join](https://dev.mysql.com/doc/internals/en/optimizer-range-join-type.html): mysql_global_status_select_full_range_join
*   Select range check (joins without keys that check for key usage after each row): mysql_global_status_select_range_check
*   [Select scan](https://dev.mysql.com/doc/refman/8.0/en/table-scan-avoidance.html): mysql_global_status_select_scan
​
All of them are counters, so remember to use the function rate in Prometheus queries, like this:
```r
rate(mysql_global_status_select_full_join[5m])
```

Similarly, you can also have statistics on the use of sorting queries in MySQL:
*   [Sort rows](https://dev.mysql.com/doc/mysql-tutorial-excerpt/8.0/en/sorting-rows.html): mysql_global_status_sort_rows
*   The number of sorts that were done using ranges: mysql_global_status_sort_range
*   [Sort merge](https://dev.mysql.com/doc/internals/en/optimizer-index-merge-join-type.html) (The number of merge passes that the sort algorithm has had to do): mysql_global_status_sort_merge_passes
*   The number of sorts that were done by scanning the table: mysql_global_status_sort_scan
​

Next steps
----------
In this article, you learned how easy it is to start monitoring MySQL with Prometheus. You just need [the right exporter and configuration](https://promcat.io/).
You also learned the top Prometheus queries you should have in mind when monitoring MySQL with Prometheus.
​
If you want to get some inspiration for writing Prometheus queries, you can take a look at these [examples for monitoring Kubernetes](https://sysdig.com/blog/prometheus-query-examples/), or download our [PromQL getting started cheatsheet](https://sysdig.com/blog/getting-started-with-promql-cheatsheet/).
​