|
30 | 30 | "collect.info_schema.processlist", false,
|
31 | 31 | "Collect current thread state counts from the information_schema.processlist",
|
32 | 32 | )
|
| 33 | + collectTableSchema = flag.Bool( |
| 34 | + "collect.info_schema.tables", true, |
| 35 | + "Collect metrics from information_schema.tables", |
| 36 | + ) |
| 37 | + tableSchemaDatabases = flag.String( |
| 38 | + "collect.info_schema.tables.databases", "*", |
| 39 | + "The list of databases to collect table stats for, or '*' for all", |
| 40 | + ) |
33 | 41 | collectGlobalStatus = flag.Bool(
|
34 | 42 | "collect.global_status", true,
|
35 | 43 | "Collect from SHOW GLOBAL STATUS",
|
@@ -204,7 +212,29 @@ const (
|
204 | 212 | COUNT_MISC, SUM_TIMER_MISC
|
205 | 213 | FROM performance_schema.file_summary_by_event_name
|
206 | 214 | `
|
207 |
| - userStatQuery = `SELECT * FROM information_schema.USER_STATISTICS` |
| 215 | + userStatQuery = `SELECT * FROM information_schema.USER_STATISTICS` |
| 216 | + tableSchemaQuery = ` |
| 217 | + SELECT |
| 218 | + TABLE_SCHEMA, |
| 219 | + TABLE_NAME, |
| 220 | + TABLE_TYPE, |
| 221 | + ifnull(ENGINE, 'NONE') as ENGINE, |
| 222 | + ifnull(VERSION, '0') as VERSION, |
| 223 | + ifnull(ROW_FORMAT, 'NONE') as ROW_FORMAT, |
| 224 | + ifnull(TABLE_ROWS, '0') as TABLE_ROWS, |
| 225 | + ifnull(DATA_LENGTH, '0') as DATA_LENGTH, |
| 226 | + ifnull(INDEX_LENGTH, '0') as INDEX_LENGTH, |
| 227 | + ifnull(DATA_FREE, '0') as DATA_FREE, |
| 228 | + ifnull(CREATE_OPTIONS, 'NONE') as CREATE_OPTIONS |
| 229 | + FROM information_schema.tables |
| 230 | + WHERE TABLE_SCHEMA = '%s' |
| 231 | + ` |
| 232 | + dbListQuery = ` |
| 233 | + SELECT |
| 234 | + SCHEMA_NAME |
| 235 | + FROM information_schema.schemata |
| 236 | + WHERE SCHEMA_NAME NOT IN ('mysql', 'performance_schema', 'information_schema') |
| 237 | + ` |
208 | 238 | )
|
209 | 239 |
|
210 | 240 | // landingPage contains the HTML served at '/'.
|
@@ -255,6 +285,21 @@ var (
|
255 | 285 | "The max value of an auto_increment column from information_schema.",
|
256 | 286 | []string{"schema", "table", "column"}, nil,
|
257 | 287 | )
|
| 288 | + infoSchemaTablesVersionDesc = prometheus.NewDesc( |
| 289 | + prometheus.BuildFQName(namespace, informationSchema, "table_version"), |
| 290 | + "The version number of the table's .frm file", |
| 291 | + []string{"schema", "table", "type", "engine", "row_format", "create_options"}, nil, |
| 292 | + ) |
| 293 | + infoSchemaTablesRowsDesc = prometheus.NewDesc( |
| 294 | + prometheus.BuildFQName(namespace, informationSchema, "table_rows"), |
| 295 | + "The estimated number of rows in the table from information_schema.tables", |
| 296 | + []string{"schema", "table"}, nil, |
| 297 | + ) |
| 298 | + infoSchemaTablesSizeDesc = prometheus.NewDesc( |
| 299 | + prometheus.BuildFQName(namespace, informationSchema, "table_size"), |
| 300 | + "The size of the table components from information_schema.tables", |
| 301 | + []string{"schema", "table", "component"}, nil, |
| 302 | + ) |
258 | 303 | globalPerformanceSchemaLostDesc = prometheus.NewDesc(
|
259 | 304 | prometheus.BuildFQName(namespace, globalStatus, "performance_schema_lost_total"),
|
260 | 305 | "Total number of MySQL instrumentations that could not be loaded or created due to memory constraints.",
|
@@ -671,6 +716,12 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
|
671 | 716 | return
|
672 | 717 | }
|
673 | 718 | }
|
| 719 | + if *collectTableSchema { |
| 720 | + if err = scrapeTableSchema(db, ch); err != nil { |
| 721 | + log.Println("Error scraping table schema:", err) |
| 722 | + return |
| 723 | + } |
| 724 | + } |
674 | 725 | if *collectAutoIncrementColumns {
|
675 | 726 | if err = scrapeInformationSchema(db, ch); err != nil {
|
676 | 727 | log.Println("Error scraping information schema:", err)
|
@@ -1448,6 +1499,93 @@ func scrapeProcesslist(db *sql.DB, ch chan<- prometheus.Metric) error {
|
1448 | 1499 | return nil
|
1449 | 1500 | }
|
1450 | 1501 |
|
| 1502 | +func scrapeTableSchema(db *sql.DB, ch chan<- prometheus.Metric) error { |
| 1503 | + var dbList []string |
| 1504 | + if *tableSchemaDatabases == "*" { |
| 1505 | + dbListRows, err := db.Query(dbListQuery) |
| 1506 | + if err != nil { |
| 1507 | + return err |
| 1508 | + } |
| 1509 | + defer dbListRows.Close() |
| 1510 | + |
| 1511 | + var database string |
| 1512 | + |
| 1513 | + for dbListRows.Next() { |
| 1514 | + if err := dbListRows.Scan( |
| 1515 | + &database, |
| 1516 | + ); err != nil { |
| 1517 | + return err |
| 1518 | + } |
| 1519 | + dbList = append(dbList, database) |
| 1520 | + } |
| 1521 | + } else { |
| 1522 | + dbList = strings.Split(*tableSchemaDatabases, ",") |
| 1523 | + } |
| 1524 | + |
| 1525 | + for _, database := range dbList { |
| 1526 | + tableSchemaRows, err := db.Query(fmt.Sprintf(tableSchemaQuery, database)) |
| 1527 | + if err != nil { |
| 1528 | + return err |
| 1529 | + } |
| 1530 | + defer tableSchemaRows.Close() |
| 1531 | + |
| 1532 | + var ( |
| 1533 | + tableSchema string |
| 1534 | + tableName string |
| 1535 | + tableType string |
| 1536 | + engine string |
| 1537 | + version uint64 |
| 1538 | + rowFormat string |
| 1539 | + tableRows uint64 |
| 1540 | + dataLength uint64 |
| 1541 | + indexLength uint64 |
| 1542 | + dataFree uint64 |
| 1543 | + createOptions string |
| 1544 | + ) |
| 1545 | + |
| 1546 | + for tableSchemaRows.Next() { |
| 1547 | + err = tableSchemaRows.Scan( |
| 1548 | + &tableSchema, |
| 1549 | + &tableName, |
| 1550 | + &tableType, |
| 1551 | + &engine, |
| 1552 | + &version, |
| 1553 | + &rowFormat, |
| 1554 | + &tableRows, |
| 1555 | + &dataLength, |
| 1556 | + &indexLength, |
| 1557 | + &dataFree, |
| 1558 | + &createOptions, |
| 1559 | + ) |
| 1560 | + if err != nil { |
| 1561 | + return err |
| 1562 | + } |
| 1563 | + ch <- prometheus.MustNewConstMetric( |
| 1564 | + infoSchemaTablesVersionDesc, prometheus.GaugeValue, float64(version), |
| 1565 | + tableSchema, tableName, tableType, engine, rowFormat, createOptions, |
| 1566 | + ) |
| 1567 | + ch <- prometheus.MustNewConstMetric( |
| 1568 | + infoSchemaTablesRowsDesc, prometheus.GaugeValue, float64(tableRows), |
| 1569 | + tableSchema, tableName, |
| 1570 | + ) |
| 1571 | + ch <- prometheus.MustNewConstMetric( |
| 1572 | + infoSchemaTablesSizeDesc, prometheus.GaugeValue, float64(dataLength), |
| 1573 | + tableSchema, tableName, "data_length", |
| 1574 | + ) |
| 1575 | + ch <- prometheus.MustNewConstMetric( |
| 1576 | + infoSchemaTablesSizeDesc, prometheus.GaugeValue, float64(indexLength), |
| 1577 | + tableSchema, tableName, "index_length", |
| 1578 | + ) |
| 1579 | + ch <- prometheus.MustNewConstMetric( |
| 1580 | + infoSchemaTablesSizeDesc, prometheus.GaugeValue, float64(dataFree), |
| 1581 | + tableSchema, tableName, "data_free", |
| 1582 | + ) |
| 1583 | + } |
| 1584 | + } |
| 1585 | + |
| 1586 | + return nil |
| 1587 | +} |
| 1588 | + |
1451 | 1589 | func newDesc(subsystem, name, help string) *prometheus.Desc {
|
1452 | 1590 | return prometheus.NewDesc(
|
1453 | 1591 | prometheus.BuildFQName(namespace, subsystem, name),
|
|
0 commit comments