In this blog post, we’ll look at how to use Prometheus to check for auto_increment value exhaustion.
One of the proactive tasks DBAs perform is checking if a field defined as auto_increment is about to reach the maximum allowed value of the int definition. For example, if a field is defined as smallint unsigned and your next auto_increment value is 65535 (the maximum possible value for smallint), you are in trouble. You should modify the column definition to at least mediumint unsigned, (which has a maximum value is 16777215) before you start to silently lose data or are not able to insert more rows.
Prometheus and the mysqld exporter
When you deploy the Percona Monitoring and Management (PMM) platform, you’re not only getting a state-of-the-art visualization tool, but you also get the Prometheus time-series database with it as part of the package. Prometheus connects to exporters running on a PMM Client and aggregates metrics collected by those exporters. For this case, the exporter that we care about is for MySQL (https://github.com/percona/mysqld_exporter), and specifically the metrics gathered by the collect.auto_increment.columns collector.
Tablestats
To know if your PMM installation is collecting this data, you should check with pmm-admin. If your output comes with tablestats=OFF, you are not collecting this metric:
1 2 | [root@localhost]# pmm-admin list | grep tablestats mysql:metrics ip-10-0-0-207 42002 YES percona:***@unix(/var/lib/mysql/mysql.sock) tablestats=OFF |
By default, the tablestats feature is enabled (unless your MySQL server has more than 1000 tables). To enable it beyond that limit, re-add the metrics using the --disable-tablestats-limit parameter with a larger value than the number of your tables (described in https://www.percona.com/doc/percona-monitoring-and-management/pmm-admin.html):
1 | pmm-admin add mysql:metrics --disable-tablestats-limit 10000 |
Prometheus query
Prometheus not only stores the current value of the auto_increment column (mysql_info_schema_auto_increment_column), but also has the maximum allowed value per int type on it (mysql_info_schema_auto_increment_column_max). We can get the percentage of values used for the column with a single query, like:
mysql_info_schema_auto_increment_column*100/mysql_info_schema_auto_increment_column_max (available thanks to Max Bubenick)
Now, where do I execute the query? There are three options: the Prometheus GUI, using the API or the MySQL Table Statistics dashboard in PMM.
GUI
Every PMM installation allows you to access Prometheus by going to the URL http://<serverIP>/prometheus. Put the query in the text field, and hit Execute. The results look like this:
You can see a row per existent table. In this case we can see that the table “sbtest20” is using the 95% of the available values. This makes it a candidate for a column definition modification. The remaining tables are using around 0.0002% of the values, so there’s plenty room to grow.
API
This is probably the best approach for checking this regularly. You can use the REST API to query metrics (as explained in https://prometheus.io/docs/querying/api/), like this:
1 | curl -g 'http://127.0.0.1:80/prometheus/api/v1/query?query=(mysql_info_schema_auto_increment_column{schema="percona",table="sbtest20"}*100/mysql_info_schema_auto_increment_column_max{schema="percona",table="sbtest20"})' |
See the new data? If you want to query the metrics for a specific table like I do, then just add the schema and table name: {schema=”percona”,table=”sbtest20″}:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | [root@localhost ~]# curl -g 'http://127.0.0.1:80/prometheus/api/v1/query?query=(mysql_info_schema_auto_increment_column{schema="percona",table="sbtest20"}*100/mysql_info_schema_auto_increment_column_max{schema="percona",table="sbtest20"})' | python -m json.tool % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 220 100 220 0 0 1646 0 --:--:-- --:--:-- --:--:-- 1666 { "data": { "result": [ { "metric": { "column": "id", "instance": "localhost.localdomain", "job": "mysql", "schema": "percona", "table": "sbtest20" }, "value": [ 1509395634.736, "95.2755905511811" ] } ], "resultType": "vector" }, "status": "success" } |
The part of the output that matters here is:
1 | "value": [ 1509395634.736, "95.2755905511811" ] |
As we saw in the GUI output, the table sbtest20 already used 95% of the available values for the column int definition. In this case, it is tinyint signed and the current maximum value is 120 (out of 127):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | mysql> show create table sbtest20G *************************** 1. row *************************** Table: sbtest20 Create Table: CREATE TABLE `sbtest20` ( `id` tinyint(10) NOT NULL AUTO_INCREMENT, `k` int(10) unsigned NOT NULL DEFAULT '0', `c` char(120) NOT NULL DEFAULT '', `pad` char(60) NOT NULL DEFAULT '', PRIMARY KEY (`id`), KEY `k_1` (`k`) ) ENGINE=InnoDB AUTO_INCREMENT=121 DEFAULT CHARSET=latin1 1 row in set (0.02 sec) mysql> select max(id) from sbtest20; +---------+ | max(id) | +---------+ | 120 | +---------+ 1 row in set (0.09 sec) |
PMM’s MySQL Table Statistics dashboard
This information is also available on PMM, just go to the dashboard called MySQL Table Statistics and look for the section “Top Tables by Auto Increment Usage”:
The above graph is from Percona’s PMM demo setup, which is available publicly to everyone. You can access that particular chart by going to https://pmmdemo.percona.com/graph/dashboard/db/mysql-table-statistics?refresh=1m&panelId=53&fullscreen&orgId=1.
Conclusion
There are several ways to check for auto_increment capacity; this is just one of them. But what really matters is how PMM collects a rich set of information, and that’s is available for just one API request of distance. Note that each additional metric series collected by Prometheus adds additional work on the PMM Server, so watch that your server doesn’t enter what is known as Rushed Mode. This is a condition where the PMM Server is experiencing a high volume of writes and is struggling to keep up. You can identify this condition (since version 1.4) by examining the Prometheus dashboard, and in particular the graph Prometheus Problems. Check for the metric series called “Has been in Rushed Mode” and see if is greater than 0.
Enjoy PMM!
Awesomesauce!
Indeed, It’s very much helpful, But I still tend to use the tweaked version of common_schema query to quickly check through the server where we don’t have PMM.
select tab as ‘Table’, col as ‘Column’, cv as ‘Current Value’, ud as ‘Appox. Usage Period (Days)’, round(usg,2) as ‘Usage %’ from (select concat(table_schema,’.’,table_name) as tab, concat(column_name,’ ‘,column_type,’ ‘,if(locate(‘unsigned’, column_type)=0,’signed’,”)) as col, auto_increment as cv, datediff(now(),create_time) as ud, ((auto_increment/(case data_type when ‘tinyint’ then 255 when ‘smallint’ then 65535 when ‘mediumint’ then 16777215 when ‘int’ then 4294967295 when ‘bigint’ then 18446744073709551615 end >> if(locate(‘unsigned’, column_type)>0,0,1)))*100) as usg from information_schema.columns inner join information_schema.tables using (table_schema, table_name) where table_schema not in (‘mysql’,’information_schema’,’performance_schema’) and table_type = ‘base table’ and extra = ‘auto_increment’ order by 5 desc, 3 desc) as get_auto_inc_usage where usg >= limit 10;
USER_INPUT -> In % (Auto Increment Usage)
Sample Output:
+---------------------------------+-------------------------------+---------------+----------------------------+---------+
| Table | Column | Current Value | Appox. Usage Period (Days) | Usage % |
+---------------------------------+-------------------------------+---------------+----------------------------+---------+
| test.tickets | id tinyint(4) signed | 100 | 0 | 78.74 |
| helpdesk.hd_ticket_priority | priority_id tinyint(4) signed | 5 | 406 | 3.94 |
| helpdesk.hd_syslog | log_id int(11) unsigned | 439118 | 198 | 0.01 |
* where usg >= USER_INPUT limit 10;
I marked the text “USER_INPUT” between , It disappeared
I also have a query to get the result
SELECT table_schema, table_name, column_name, column_type, auto_increment,max_int,ROUND(auto_increment/max_int*100,2) AS “USED %” FROM
(SELECT table_schema, table_name, column_name, column_type, auto_increment,
pow(2, case data_type
when ‘tinyint’ then 7
when ‘smallint’ then 15
when ‘mediumint’ then 23
when ‘int’ then 31
when ‘bigint’ then 63
end+(column_type like ‘% unsigned’))-1 as max_int
FROM information_schema.tables t
JOIN information_schema.columns c USING (table_schema,table_name)
WHERE t.table_schema not in (‘mysql’,’information_schema’,’performance_schema’) and t.table_type = ‘base table’
and c.extra = ‘auto_increment’ AND t.auto_increment IS NOT NULL order by auto_increment desc limit 10) TMP;
Here is my test result
+————–+————+————-+——————+—————-+————+——–+
| table_schema | table_name | column_name | column_type | auto_increment | max_int | USED % |
+————–+————+————-+——————+—————-+————+——–+
| edxapp | andy | id | int(10) unsigned | 4294967295 | 4294967295 | 100.00 |
| examlogin | andy | id | int(11) | 2147483641 | 2147483647 | 100.00 |
+————–+————+————-+——————+—————-+————+——–+
2 rows in set (0.11 sec)