MySQL 8.4 LTS 数据库巡检脚本

MySQL 8.4 LTS 数据库巡检脚本

脚本结构

复制代码
mysql_health_check.sql
├── 1.  实例基础信息
├── 2.  参数配置检查
├── 3.  连接与线程状态
├── 4.  数据库与表空间大小
├── 5.  InnoDB 引擎状态
├── 6.  缓冲池(Buffer Pool)命中率
├── 7.  复制状态(主从/GTID)
├── 8.  慢查询与 Performance Schema
├── 9.  锁与事务状态
├── 10. 索引使用情况
├── 11. 二进制日志(Binlog)状态
├── 12. 用户权限与安全
└── 13. 巡检告警汇总

完整脚本

sql 复制代码
/*==========================================================
  MySQL 8.4 LTS 数据库巡检报告
  适用版本: MySQL 8.0.x / 8.4.x LTS
  执行账号: root 或具备 PROCESS、REPLICATION CLIENT、
            SELECT ON performance_schema.* 权限的账号
  前置条件: 启用 performance_schema(默认启用)
==========================================================*/

-- 设置输出格式
\P less -SFX
-- 或使用 tee 将结果输出到文件
-- \T /tmp/mysql_check_report.log

SELECT '╔════════════════════════════════════════════════════╗' AS '';
SELECT '║        MySQL 8.4 数据库巡检报告                    ║' AS '';
SELECT '╚════════════════════════════════════════════════════╝' AS '';

SELECT
    NOW()               AS `巡检时间`,
    @@hostname          AS `主机名`,
    @@port              AS `端口`,
    @@version           AS `MySQL版本`,
    @@version_comment   AS `版本说明`,
    USER()              AS `当前用户`,
    DATABASE()          AS `当前库`;


/*==========================================================
  【1】实例基础信息
==========================================================*/
SELECT '┌─────────────────────────────────────┐' AS '';
SELECT '│  1. 实例基础信息                    │' AS '';
SELECT '└─────────────────────────────────────┘' AS '';

SELECT
    @@version                                         AS `版本`,
    @@version_compile_os                              AS `操作系统`,
    @@version_compile_machine                         AS `架构`,
    @@datadir                                         AS `数据目录`,
    @@basedir                                         AS `安装目录`,
    @@socket                                          AS `Socket路径`,
    @@server_id                                       AS `Server ID`,
    @@server_uuid                                     AS `Server UUID`;

-- 运行时长
SELECT
    DATE_SUB(NOW(), INTERVAL VARIABLE_VALUE SECOND)    AS `启动时间`,
    SEC_TO_TIME(VARIABLE_VALUE)                        AS `运行时长`,
    ROUND(VARIABLE_VALUE / 86400, 2)                   AS `运行天数`
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Uptime';


/*==========================================================
  【2】关键参数配置检查
==========================================================*/
SELECT '' AS '';
SELECT '┌─────────────────────────────────────┐' AS '';
SELECT '│  2. 关键参数配置检查                │' AS '';
SELECT '└─────────────────────────────────────┘' AS '';

SELECT
    VARIABLE_NAME                                     AS `参数`,
    VARIABLE_VALUE                                    AS `当前值`,
    CASE VARIABLE_NAME
        WHEN 'innodb_buffer_pool_size'
            THEN '建议物理内存的 50%~70%'
        WHEN 'innodb_log_file_size'
            THEN '建议 1GB~4GB,写多场景增大'
        WHEN 'innodb_flush_log_at_trx_commit'
            THEN '1=最安全 / 2=折中 / 0=最快(不推荐)'
        WHEN 'sync_binlog'
            THEN '1=最安全,高TPS场景可调整'
        WHEN 'max_connections'
            THEN '按业务并发评估,避免过大'
        WHEN 'innodb_io_capacity'
            THEN 'SSD建议 2000+,HDD建议 200'
        WHEN 'innodb_flush_method'
            THEN 'Linux 建议 O_DIRECT'
        WHEN 'binlog_expire_logs_seconds'
            THEN '建议 604800(7天) 或按业务调整'
        WHEN 'transaction_isolation'
            THEN '默认 REPEATABLE-READ'
        ELSE ''
    END                                               AS `建议`
FROM performance_schema.global_variables
WHERE VARIABLE_NAME IN (
    'innodb_buffer_pool_size',
    'innodb_buffer_pool_instances',
    'innodb_log_file_size',
    'innodb_log_files_in_group',
    'innodb_redo_log_capacity',        -- 8.0.30+ 新参数
    'innodb_flush_log_at_trx_commit',
    'innodb_flush_method',
    'innodb_io_capacity',
    'innodb_io_capacity_max',
    'sync_binlog',
    'binlog_expire_logs_seconds',
    'max_connections',
    'max_allowed_packet',
    'max_connect_errors',
    'wait_timeout',
    'interactive_timeout',
    'transaction_isolation',
    'log_bin',
    'log_error',
    'slow_query_log',
    'long_query_time',
    'performance_schema'
)
ORDER BY VARIABLE_NAME;


/*==========================================================
  【3】连接与线程状态
==========================================================*/
SELECT '' AS '';
SELECT '┌─────────────────────────────────────┐' AS '';
SELECT '│  3. 连接与线程状态                  │' AS '';
SELECT '└─────────────────────────────────────┘' AS '';

-- 3.1 连接数概况
SELECT '--- 3.1 连接数统计 ---' AS '';

SELECT
    @@max_connections                                                 AS `最大连接数`,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME='Threads_connected')                         AS `当前连接`,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME='Threads_running')                           AS `活跃线程`,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME='Max_used_connections')                      AS `历史最大连接`,
    ROUND((SELECT VARIABLE_VALUE FROM performance_schema.global_status
           WHERE VARIABLE_NAME='Threads_connected') * 100
          / @@max_connections, 2)                                     AS `使用率%`,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME='Aborted_connects')                          AS `连接失败次数`,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME='Aborted_clients')                           AS `异常断开次数`;

-- 3.2 按用户/主机分组
SELECT '--- 3.2 按用户/主机分组的连接 ---' AS '';

SELECT
    USER                                              AS `用户`,
    HOST                                              AS `主机`,
    DB                                                AS `数据库`,
    COUNT(*)                                          AS `连接数`,
    SUM(CASE WHEN COMMAND='Sleep' THEN 1 ELSE 0 END)  AS `Sleep数`,
    SUM(CASE WHEN COMMAND='Query' THEN 1 ELSE 0 END)  AS `Query数`,
    MAX(TIME)                                         AS `最长持续秒`
FROM information_schema.processlist
GROUP BY USER, HOST, DB
ORDER BY COUNT(*) DESC
LIMIT 20;

-- 3.3 Sleep 过多的连接
SELECT '--- 3.3 Sleep 超过 5 分钟的连接 ---' AS '';

SELECT
    ID                                                AS `线程ID`,
    USER                                              AS `用户`,
    HOST                                              AS `主机`,
    DB                                                AS `数据库`,
    COMMAND                                           AS `命令`,
    TIME                                              AS `持续秒`,
    STATE                                             AS `状态`
FROM information_schema.processlist
WHERE COMMAND = 'Sleep'
  AND TIME > 300
ORDER BY TIME DESC
LIMIT 20;


/*==========================================================
  【4】数据库与表空间大小
==========================================================*/
SELECT '' AS '';
SELECT '┌─────────────────────────────────────┐' AS '';
SELECT '│  4. 数据库与表空间大小              │' AS '';
SELECT '└─────────────────────────────────────┘' AS '';

-- 4.1 按数据库汇总
SELECT '--- 4.1 各数据库大小 ---' AS '';

SELECT
    TABLE_SCHEMA                                              AS `数据库`,
    COUNT(*)                                                  AS `表数量`,
    ROUND(SUM(DATA_LENGTH)/1024/1024, 2)                      AS `数据MB`,
    ROUND(SUM(INDEX_LENGTH)/1024/1024, 2)                     AS `索引MB`,
    ROUND(SUM(DATA_LENGTH+INDEX_LENGTH)/1024/1024, 2)         AS `总大小MB`,
    ROUND(SUM(DATA_FREE)/1024/1024, 2)                        AS `碎片空间MB`
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('mysql','information_schema',
                           'performance_schema','sys')
GROUP BY TABLE_SCHEMA
ORDER BY SUM(DATA_LENGTH+INDEX_LENGTH) DESC;

-- 4.2 TOP 20 大表
SELECT '--- 4.2 TOP 20 大表 ---' AS '';

SELECT
    TABLE_SCHEMA                                              AS `数据库`,
    TABLE_NAME                                                AS `表名`,
    ENGINE                                                    AS `引擎`,
    TABLE_ROWS                                                AS `行数估算`,
    ROUND(DATA_LENGTH/1024/1024, 2)                           AS `数据MB`,
    ROUND(INDEX_LENGTH/1024/1024, 2)                          AS `索引MB`,
    ROUND((DATA_LENGTH+INDEX_LENGTH)/1024/1024, 2)            AS `总大小MB`,
    ROUND(DATA_FREE/1024/1024, 2)                             AS `碎片MB`,
    ROUND(DATA_FREE * 100.0
          / NULLIF(DATA_LENGTH+INDEX_LENGTH+DATA_FREE, 0), 2) AS `碎片率%`
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('mysql','information_schema',
                           'performance_schema','sys')
  AND TABLE_TYPE = 'BASE TABLE'
ORDER BY (DATA_LENGTH+INDEX_LENGTH) DESC
LIMIT 20;

-- 4.3 碎片率高的表(建议 OPTIMIZE)
SELECT '--- 4.3 碎片率 > 30% 的表 ---' AS '';

SELECT
    TABLE_SCHEMA                                              AS `数据库`,
    TABLE_NAME                                                AS `表名`,
    ROUND((DATA_LENGTH+INDEX_LENGTH)/1024/1024, 2)            AS `总大小MB`,
    ROUND(DATA_FREE/1024/1024, 2)                             AS `碎片MB`,
    ROUND(DATA_FREE * 100.0
          / NULLIF(DATA_LENGTH+INDEX_LENGTH+DATA_FREE, 0), 2) AS `碎片率%`,
    CONCAT('OPTIMIZE TABLE `', TABLE_SCHEMA, '`.`',
           TABLE_NAME, '`;')                                  AS `建议命令`
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('mysql','information_schema',
                           'performance_schema','sys')
  AND TABLE_TYPE = 'BASE TABLE'
  AND DATA_LENGTH + INDEX_LENGTH > 100 * 1024 * 1024   -- >100MB
  AND DATA_FREE * 100.0 / NULLIF(DATA_LENGTH+INDEX_LENGTH+DATA_FREE, 0) > 30
ORDER BY DATA_FREE DESC
LIMIT 20;


/*==========================================================
  【5】InnoDB 引擎状态
==========================================================*/
SELECT '' AS '';
SELECT '┌─────────────────────────────────────┐' AS '';
SELECT '│  5. InnoDB 引擎状态                 │' AS '';
SELECT '└─────────────────────────────────────┘' AS '';

-- 5.1 InnoDB 关键状态
SELECT '--- 5.1 InnoDB 关键指标 ---' AS '';

SELECT
    VARIABLE_NAME                                     AS `指标`,
    VARIABLE_VALUE                                    AS `值`
FROM performance_schema.global_status
WHERE VARIABLE_NAME IN (
    'Innodb_rows_read',
    'Innodb_rows_inserted',
    'Innodb_rows_updated',
    'Innodb_rows_deleted',
    'Innodb_row_lock_current_waits',
    'Innodb_row_lock_waits',
    'Innodb_row_lock_time',
    'Innodb_row_lock_time_avg',
    'Innodb_row_lock_time_max',
    'Innodb_deadlocks',
    'Innodb_data_reads',
    'Innodb_data_writes',
    'Innodb_log_waits',
    'Innodb_log_writes',
    'Innodb_os_log_fsyncs'
)
ORDER BY VARIABLE_NAME;

-- 5.2 InnoDB 活跃事务
SELECT '--- 5.2 InnoDB 活跃事务 ---' AS '';

SELECT
    trx_id                                            AS `事务ID`,
    trx_state                                         AS `状态`,
    trx_started                                       AS `开始时间`,
    TIMESTAMPDIFF(SECOND, trx_started, NOW())         AS `持续秒`,
    trx_mysql_thread_id                               AS `线程ID`,
    trx_rows_locked                                   AS `锁行数`,
    trx_rows_modified                                 AS `修改行数`,
    trx_isolation_level                               AS `隔离级别`,
    LEFT(trx_query, 200)                              AS `当前SQL`
FROM information_schema.INNODB_TRX
ORDER BY trx_started;


/*==========================================================
  【6】缓冲池 (Buffer Pool) 命中率
==========================================================*/
SELECT '' AS '';
SELECT '┌─────────────────────────────────────┐' AS '';
SELECT '│  6. InnoDB Buffer Pool 命中率       │' AS '';
SELECT '└─────────────────────────────────────┘' AS '';

SELECT
    ROUND(@@innodb_buffer_pool_size/1024/1024, 2)    AS `BP配置MB`,
    @@innodb_buffer_pool_instances                   AS `实例数`,

    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME='Innodb_buffer_pool_pages_total')        AS `总页数`,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME='Innodb_buffer_pool_pages_free')         AS `空闲页数`,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME='Innodb_buffer_pool_pages_dirty')        AS `脏页数`,

    ROUND(
        (1 - (SELECT VARIABLE_VALUE FROM performance_schema.global_status
              WHERE VARIABLE_NAME='Innodb_buffer_pool_reads')
           / NULLIF((SELECT VARIABLE_VALUE FROM performance_schema.global_status
                     WHERE VARIABLE_NAME='Innodb_buffer_pool_read_requests'), 0)
        ) * 100, 4)                                              AS `命中率%`,

    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME='Innodb_buffer_pool_read_requests')      AS `逻辑读请求`,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME='Innodb_buffer_pool_reads')              AS `物理读次数`;

-- 按实例查看 Buffer Pool
SELECT '--- 6.1 Buffer Pool 各实例状态 ---' AS '';

SELECT
    POOL_ID                                          AS `实例ID`,
    POOL_SIZE                                        AS `总页数`,
    FREE_BUFFERS                                     AS `空闲页`,
    DATABASE_PAGES                                   AS `数据页`,
    MODIFIED_DATABASE_PAGES                          AS `脏页`,
    ROUND(HIT_RATE/10, 2)                            AS `命中率%`,
    PAGES_MADE_YOUNG                                 AS `年轻化页`,
    PAGES_NOT_MADE_YOUNG                             AS `未年轻化页`,
    NUMBER_PAGES_READ                                AS `已读页`,
    NUMBER_PAGES_CREATED                             AS `已创建页`,
    NUMBER_PAGES_WRITTEN                             AS `已写页`
FROM information_schema.INNODB_BUFFER_POOL_STATS;


/*==========================================================
  【7】复制状态 (主从/GTID)
==========================================================*/
SELECT '' AS '';
SELECT '┌─────────────────────────────────────┐' AS '';
SELECT '│  7. 主从复制状态                    │' AS '';
SELECT '└─────────────────────────────────────┘' AS '';

-- 7.1 主库状态
SELECT '--- 7.1 主库 Binlog 状态 ---' AS '';

-- MySQL 8.4 已废弃 SHOW MASTER STATUS,改用 SHOW BINARY LOG STATUS
SHOW BINARY LOG STATUS;

-- 7.2 从库复制线程(通过 performance_schema,8.4 推荐)
SELECT '--- 7.2 复制通道状态(performance_schema)---' AS '';

SELECT
    CHANNEL_NAME                                     AS `通道名`,
    SERVICE_STATE                                    AS `IO状态`,
    LAST_ERROR_NUMBER                                AS `最后错误码`,
    LAST_ERROR_MESSAGE                               AS `错误信息`,
    LAST_ERROR_TIMESTAMP                             AS `错误时间`
FROM performance_schema.replication_connection_status;

SELECT
    CHANNEL_NAME                                     AS `通道名`,
    SERVICE_STATE                                    AS `SQL状态`,
    LAST_ERROR_NUMBER                                AS `最后错误码`,
    LAST_ERROR_MESSAGE                               AS `错误信息`,
    LAST_ERROR_TIMESTAMP                             AS `错误时间`
FROM performance_schema.replication_applier_status;

-- 7.3 从库延迟(关键指标)
SELECT '--- 7.3 复制延迟 ---' AS '';

SELECT
    CHANNEL_NAME                                             AS `通道名`,
    WORKER_ID                                                AS `Worker`,
    APPLYING_TRANSACTION                                     AS `正在应用事务`,
    LAST_APPLIED_TRANSACTION                                 AS `最近应用事务`,
    LAST_APPLIED_TRANSACTION_END_APPLY_TIMESTAMP             AS `最近应用完成时间`,
    TIMESTAMPDIFF(SECOND,
        LAST_APPLIED_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP,
        LAST_APPLIED_TRANSACTION_END_APPLY_TIMESTAMP)        AS `应用延迟秒`
FROM performance_schema.replication_applier_status_by_worker;

-- 7.4 GTID 状态
SELECT '--- 7.4 GTID 状态 ---' AS '';

SELECT
    @@GLOBAL.gtid_executed                           AS `已执行GTID`,
    @@GLOBAL.gtid_purged                             AS `已清理GTID`,
    @@GLOBAL.gtid_mode                               AS `GTID模式`,
    @@GLOBAL.enforce_gtid_consistency                AS `GTID一致性`;


/*==========================================================
  【8】慢查询与 Performance Schema
==========================================================*/
SELECT '' AS '';
SELECT '┌─────────────────────────────────────┐' AS '';
SELECT '│  8. TOP 慢 SQL 分析                 │' AS '';
SELECT '└─────────────────────────────────────┘' AS '';

-- 8.1 慢查询配置
SELECT '--- 8.1 慢查询配置 ---' AS '';

SELECT
    @@slow_query_log                                 AS `慢日志开关`,
    @@slow_query_log_file                            AS `慢日志路径`,
    @@long_query_time                                AS `慢查询阈值秒`,
    @@log_queries_not_using_indexes                  AS `记录无索引SQL`,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME='Slow_queries')             AS `慢查询总数`;

-- 8.2 总耗时 TOP 10 SQL
SELECT '--- 8.2 总耗时 TOP 10 SQL ---' AS '';

SELECT
    SCHEMA_NAME                                                   AS `数据库`,
    COUNT_STAR                                                    AS `执行次数`,
    ROUND(SUM_TIMER_WAIT/1000000000000, 3)                        AS `总耗时秒`,
    ROUND(AVG_TIMER_WAIT/1000000000, 3)                           AS `平均耗时ms`,
    ROUND(MAX_TIMER_WAIT/1000000000, 3)                           AS `最大耗时ms`,
    SUM_ROWS_SENT                                                 AS `总返回行`,
    SUM_ROWS_EXAMINED                                             AS `总扫描行`,
    SUM_NO_INDEX_USED                                             AS `未用索引次数`,
    SUM_CREATED_TMP_TABLES                                        AS `创建临时表`,
    SUM_CREATED_TMP_DISK_TABLES                                   AS `磁盘临时表`,
    LEFT(DIGEST_TEXT, 200)                                        AS `SQL模板`
FROM performance_schema.events_statements_summary_by_digest
WHERE SCHEMA_NAME NOT IN ('mysql','performance_schema',
                         'information_schema','sys')
   OR SCHEMA_NAME IS NULL
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;

-- 8.3 平均耗时 TOP 10 SQL
SELECT '--- 8.3 平均耗时 TOP 10 SQL ---' AS '';

SELECT
    SCHEMA_NAME                                                   AS `数据库`,
    COUNT_STAR                                                    AS `执行次数`,
    ROUND(AVG_TIMER_WAIT/1000000000, 3)                           AS `平均耗时ms`,
    ROUND(MAX_TIMER_WAIT/1000000000, 3)                           AS `最大耗时ms`,
    ROUND(SUM_ROWS_EXAMINED/COUNT_STAR)                           AS `平均扫描行`,
    LEFT(DIGEST_TEXT, 200)                                        AS `SQL模板`
FROM performance_schema.events_statements_summary_by_digest
WHERE COUNT_STAR > 10
  AND (SCHEMA_NAME NOT IN ('mysql','performance_schema',
                          'information_schema','sys')
       OR SCHEMA_NAME IS NULL)
ORDER BY AVG_TIMER_WAIT DESC
LIMIT 10;

-- 8.4 未使用索引的 SQL
SELECT '--- 8.4 未使用索引的 TOP SQL ---' AS '';

SELECT
    SCHEMA_NAME                                                   AS `数据库`,
    COUNT_STAR                                                    AS `执行次数`,
    SUM_NO_INDEX_USED                                             AS `未用索引次数`,
    ROUND(AVG_TIMER_WAIT/1000000000, 3)                           AS `平均耗时ms`,
    SUM_ROWS_EXAMINED                                             AS `总扫描行`,
    LEFT(DIGEST_TEXT, 200)                                        AS `SQL模板`
FROM performance_schema.events_statements_summary_by_digest
WHERE SUM_NO_INDEX_USED > 0
  AND COUNT_STAR > 10
ORDER BY SUM_NO_INDEX_USED DESC
LIMIT 10;


/*==========================================================
  【9】锁与事务状态
==========================================================*/
SELECT '' AS '';
SELECT '┌─────────────────────────────────────┐' AS '';
SELECT '│  9. 锁与事务状态                    │' AS '';
SELECT '└─────────────────────────────────────┘' AS '';

-- 9.1 当前锁等待(8.0+ 使用 performance_schema.data_locks / data_lock_waits)
SELECT '--- 9.1 当前锁等待 ---' AS '';

SELECT
    r.trx_id                              AS `等待事务`,
    r.trx_mysql_thread_id                 AS `等待线程`,
    LEFT(r.trx_query, 100)                AS `等待SQL`,
    b.trx_id                              AS `阻塞事务`,
    b.trx_mysql_thread_id                 AS `阻塞线程`,
    LEFT(b.trx_query, 100)                AS `阻塞SQL`,
    TIMESTAMPDIFF(SECOND,
        r.trx_wait_started, NOW())        AS `等待秒数`
FROM performance_schema.data_lock_waits w
JOIN information_schema.INNODB_TRX b
    ON b.trx_id = w.BLOCKING_ENGINE_TRANSACTION_ID
JOIN information_schema.INNODB_TRX r
    ON r.trx_id = w.REQUESTING_ENGINE_TRANSACTION_ID;

-- 9.2 长事务告警(>5分钟)
SELECT '--- 9.2 长事务 (>5分钟) ---' AS '';

SELECT
    trx_id                                AS `事务ID`,
    trx_state                             AS `状态`,
    trx_started                           AS `开始时间`,
    TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS `已运行秒`,
    trx_mysql_thread_id                   AS `线程ID`,
    trx_rows_locked                       AS `锁行数`,
    trx_rows_modified                     AS `修改行数`,
    trx_isolation_level                   AS `隔离级别`,
    LEFT(trx_query, 200)                  AS `当前SQL`
FROM information_schema.INNODB_TRX
WHERE TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 300
ORDER BY trx_started;

-- 9.3 元数据锁(MDL)
SELECT '--- 9.3 元数据锁 (MDL) ---' AS '';

SELECT
    OBJECT_TYPE                           AS `对象类型`,
    OBJECT_SCHEMA                         AS `数据库`,
    OBJECT_NAME                           AS `对象名`,
    LOCK_TYPE                             AS `锁类型`,
    LOCK_STATUS                           AS `锁状态`,
    OWNER_THREAD_ID                       AS `线程ID`,
    OWNER_EVENT_ID                        AS `事件ID`
FROM performance_schema.metadata_locks
WHERE OBJECT_TYPE NOT IN ('GLOBAL','COMMIT','BACKUP_LOCK')
  AND OBJECT_SCHEMA NOT IN ('mysql','performance_schema',
                           'information_schema','sys')
LIMIT 30;


/*==========================================================
  【10】索引使用情况
==========================================================*/
SELECT '' AS '';
SELECT '┌─────────────────────────────────────┐' AS '';
SELECT '│  10. 索引使用情况                   │' AS '';
SELECT '└─────────────────────────────────────┘' AS '';

-- 10.1 未使用的索引(自实例启动后未被使用)
SELECT '--- 10.1 未使用的索引 ---' AS '';

SELECT
    OBJECT_SCHEMA                         AS `数据库`,
    OBJECT_NAME                           AS `表名`,
    INDEX_NAME                            AS `索引名`,
    CONCAT('DROP INDEX `', INDEX_NAME,
           '` ON `', OBJECT_SCHEMA, '`.`',
           OBJECT_NAME, '`;')             AS `建议命令`
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE INDEX_NAME IS NOT NULL
  AND INDEX_NAME <> 'PRIMARY'
  AND COUNT_STAR = 0
  AND OBJECT_SCHEMA NOT IN ('mysql','performance_schema',
                           'information_schema','sys')
ORDER BY OBJECT_SCHEMA, OBJECT_NAME
LIMIT 30;

-- 10.2 冗余索引(sys schema 提供)
SELECT '--- 10.2 冗余/重复索引 ---' AS '';

SELECT
    table_schema                          AS `数据库`,
    table_name                            AS `表名`,
    redundant_index_name                  AS `冗余索引`,
    dominant_index_name                   AS `主导索引`,
    sql_drop_index                        AS `建议删除语句`
FROM sys.schema_redundant_indexes
LIMIT 20;

-- 10.3 热点索引(使用频繁 TOP 10)
SELECT '--- 10.3 热点索引 TOP 10 ---' AS '';

SELECT
    OBJECT_SCHEMA                         AS `数据库`,
    OBJECT_NAME                           AS `表名`,
    INDEX_NAME                            AS `索引名`,
    COUNT_STAR                            AS `使用次数`,
    COUNT_READ                            AS `读次数`,
    COUNT_WRITE                           AS `写次数`,
    ROUND(SUM_TIMER_WAIT/1000000000, 2)   AS `总耗时ms`
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE INDEX_NAME IS NOT NULL
  AND COUNT_STAR > 0
  AND OBJECT_SCHEMA NOT IN ('mysql','performance_schema',
                           'information_schema','sys')
ORDER BY COUNT_STAR DESC
LIMIT 10;


/*==========================================================
  【11】二进制日志 (Binlog) 状态
==========================================================*/
SELECT '' AS '';
SELECT '┌─────────────────────────────────────┐' AS '';
SELECT '│  11. 二进制日志状态                 │' AS '';
SELECT '└─────────────────────────────────────┘' AS '';

SELECT
    @@log_bin                             AS `Binlog开关`,
    @@log_bin_basename                    AS `Binlog路径`,
    @@binlog_format                       AS `格式`,
    @@sync_binlog                         AS `sync_binlog`,
    @@binlog_expire_logs_seconds          AS `保留秒数`,
    ROUND(@@binlog_expire_logs_seconds/86400, 2) AS `保留天数`,
    @@max_binlog_size                     AS `单文件上限`;

-- Binlog 文件列表(MySQL 8.4 推荐)
SELECT '--- 11.1 Binlog 文件列表 ---' AS '';
SHOW BINARY LOGS;


/*==========================================================
  【12】用户权限与安全检查
==========================================================*/
SELECT '' AS '';
SELECT '┌─────────────────────────────────────┐' AS '';
SELECT '│  12. 用户权限与安全                 │' AS '';
SELECT '└─────────────────────────────────────┘' AS '';

-- 12.1 空密码用户
SELECT '--- 12.1 空密码或匿名用户 ---' AS '';

SELECT
    USER                                  AS `用户名`,
    HOST                                  AS `主机`,
    plugin                                AS `认证插件`,
    password_expired                      AS `密码过期`,
    account_locked                        AS `账号锁定`
FROM mysql.user
WHERE (USER = ''
       OR authentication_string = ''
       OR authentication_string IS NULL);

-- 12.2 允许任意主机登录的用户(HOST='%')
SELECT '--- 12.2 允许任意主机的账号 ---' AS '';

SELECT
    USER                                  AS `用户名`,
    HOST                                  AS `主机`,
    plugin                                AS `认证插件`,
    account_locked                        AS `是否锁定`,
    password_expired                      AS `密码过期`,
    password_last_changed                 AS `密码最后修改`
FROM mysql.user
WHERE HOST = '%'
  AND USER <> 'mysql.sys'
  AND USER <> 'mysql.session'
  AND USER <> 'mysql.infoschema';

-- 12.3 拥有超级权限的账号
SELECT '--- 12.3 超级权限账号 ---' AS '';

SELECT
    GRANTEE                               AS `账号`,
    PRIVILEGE_TYPE                        AS `权限`
FROM information_schema.USER_PRIVILEGES
WHERE PRIVILEGE_TYPE IN ('SUPER','SHUTDOWN','FILE','PROCESS',
                        'RELOAD','CREATE USER')
ORDER BY GRANTEE, PRIVILEGE_TYPE;


/*==========================================================
  【13】巡检告警汇总
==========================================================*/
SELECT '' AS '';
SELECT '┌─────────────────────────────────────┐' AS '';
SELECT '│  13. 巡检告警汇总                   │' AS '';
SELECT '└─────────────────────────────────────┘' AS '';

DROP TEMPORARY TABLE IF EXISTS tmp_alert;
CREATE TEMPORARY TABLE tmp_alert (
    level VARCHAR(20),
    item VARCHAR(500)
);

-- 连接使用率 > 80%
INSERT INTO tmp_alert
SELECT
    CASE WHEN ratio >= 90 THEN '🔴 严重' ELSE '🟡 警告' END,
    CONCAT('连接使用率: ', ratio, '%')
FROM (
    SELECT ROUND(
        (SELECT VARIABLE_VALUE FROM performance_schema.global_status
         WHERE VARIABLE_NAME='Threads_connected') * 100
        / @@max_connections, 2) AS ratio
) t
WHERE ratio >= 80;

-- Buffer Pool 命中率 < 95%
INSERT INTO tmp_alert
SELECT '🟡 警告',
       CONCAT('InnoDB Buffer Pool 命中率低: ', hit_rate, '%')
FROM (
    SELECT ROUND(
        (1 - (SELECT VARIABLE_VALUE FROM performance_schema.global_status
              WHERE VARIABLE_NAME='Innodb_buffer_pool_reads')
           / NULLIF((SELECT VARIABLE_VALUE FROM performance_schema.global_status
                     WHERE VARIABLE_NAME='Innodb_buffer_pool_read_requests'), 0)
        ) * 100, 2) AS hit_rate
) t
WHERE hit_rate < 95;

-- 长事务
INSERT INTO tmp_alert
SELECT '🟡 警告',
       CONCAT('长事务 trx_id=', trx_id,
              ' 线程=', trx_mysql_thread_id,
              ' 持续=', TIMESTAMPDIFF(SECOND, trx_started, NOW()), '秒')
FROM information_schema.INNODB_TRX
WHERE TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 3600;

-- 锁等待
INSERT INTO tmp_alert
SELECT '🔴 严重',
       CONCAT('存在锁等待: ', COUNT(*), ' 个会话被阻塞')
FROM performance_schema.data_lock_waits
HAVING COUNT(*) > 0;

-- 死锁告警
INSERT INTO tmp_alert
SELECT '🟡 警告',
       CONCAT('InnoDB 死锁次数: ', VARIABLE_VALUE)
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_deadlocks'
  AND CAST(VARIABLE_VALUE AS UNSIGNED) > 0;

-- 连接失败次数过多
INSERT INTO tmp_alert
SELECT '🟡 警告',
       CONCAT('连接失败次数过多: ', VARIABLE_VALUE)
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Aborted_connects'
  AND CAST(VARIABLE_VALUE AS UNSIGNED) > 1000;

-- 复制错误
INSERT INTO tmp_alert
SELECT '🔴 严重',
       CONCAT('复制通道 [', CHANNEL_NAME, '] 错误: ', LAST_ERROR_MESSAGE)
FROM performance_schema.replication_applier_status
WHERE LAST_ERROR_NUMBER <> 0;

INSERT INTO tmp_alert
SELECT '🔴 严重',
       CONCAT('复制连接 [', CHANNEL_NAME, '] 错误: ', LAST_ERROR_MESSAGE)
FROM performance_schema.replication_connection_status
WHERE LAST_ERROR_NUMBER <> 0;

-- 空密码或 '%' 主机账号
INSERT INTO tmp_alert
SELECT '🔴 严重',
       CONCAT('存在空密码账号: ', USER, '@', HOST)
FROM mysql.user
WHERE (authentication_string = '' OR authentication_string IS NULL)
  AND USER NOT IN ('mysql.sys','mysql.session','mysql.infoschema');

-- 碎片率高的大表
INSERT INTO tmp_alert
SELECT '🟡 警告',
       CONCAT('表 [', TABLE_SCHEMA, '.', TABLE_NAME,
              '] 碎片率: ',
              ROUND(DATA_FREE*100.0/NULLIF(DATA_LENGTH+INDEX_LENGTH+DATA_FREE,0),2), '%')
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('mysql','information_schema',
                           'performance_schema','sys')
  AND TABLE_TYPE = 'BASE TABLE'
  AND DATA_LENGTH + INDEX_LENGTH > 1024*1024*1024    -- >1GB
  AND DATA_FREE * 100.0 / NULLIF(DATA_LENGTH+INDEX_LENGTH+DATA_FREE, 0) > 40;

-- 输出告警
SELECT level AS `级别`, item AS `告警项`
FROM tmp_alert
ORDER BY CASE level
    WHEN '🔴 严重' THEN 1
    WHEN '🟡 警告' THEN 2
    ELSE 3 END;

DROP TEMPORARY TABLE IF EXISTS tmp_alert;

SELECT '' AS '';
SELECT '═══════════ 巡检完成 ═══════════' AS '';

使用方式

方式一:mysql 客户端直接执行

bash 复制代码
mysql -uroot -p -h127.0.0.1 < mysql_health_check.sql > check_$(date +%Y%m%d).log

方式二:带格式输出

bash 复制代码
mysql -uroot -p --table < mysql_health_check.sql > check_report.txt

方式三:定时任务(crontab)

bash 复制代码
# 每天 8:00 巡检并发邮件
0 8 * * * /usr/bin/mysql -uroot -p'password' \
          < /opt/dba/mysql_health_check.sql \
          > /var/log/mysql_check/check_$(date +\%Y\%m\%d).log 2>&1 \
          && mail -s "MySQL 8.4 日常巡检" dba@company.com \
          < /var/log/mysql_check/check_$(date +\%Y\%m\%d).log

MySQL 8.4 专项变更点

MySQL 8.4 LTS 相比 8.0 的重要变化,巡检脚本已适配:

项目 MySQL 8.0 MySQL 8.4
主库状态命令 SHOW MASTER STATUS SHOW BINARY LOG STATUS(原命令已废弃)
从库命令 CHANGE MASTER TO / SHOW SLAVE STATUS CHANGE REPLICATION SOURCE TO / SHOW REPLICA STATUS
mysql_native_password 默认启用 默认禁用(建议 caching_sha2_password)
Redo Log 固定文件数 innodb_redo_log_capacity(8.0.30+ 引入,8.4 推荐)
Group Replication 接口 含旧接口 移除部分已弃用的 GR 函数

关键指标阈值参考

监控项 正常 警告 严重
连接使用率 < 70% 70%~90% > 90%
Buffer Pool 命中率 > 99% 95%~99% < 95%
锁等待会话数 0 1~5 > 5
长事务时长 < 5 分钟 5~60 分钟 > 1 小时
Innodb_deadlocks 0 1~10/天 > 10/天
复制延迟 < 1 秒 1~10 秒 > 10 秒
表碎片率 < 10% 10%~30% > 30%
慢查询数 < 100/小时 100~1000/小时 > 1000/小时
Aborted_connects < 100 100~1000 > 1000

前置权限准备

建议创建专用巡检账号(MySQL 8.4 用新的权限语法):

sql 复制代码
-- 创建专用账号
CREATE USER 'dba_monitor'@'%' IDENTIFIED BY 'Strong_Pwd@2026';

-- 授予监控所需的最小权限
GRANT SELECT, PROCESS, REPLICATION CLIENT, SHOW VIEW,
      SHOW DATABASES ON *.* TO 'dba_monitor'@'%';

GRANT SELECT ON performance_schema.* TO 'dba_monitor'@'%';
GRANT SELECT ON sys.* TO 'dba_monitor'@'%';
GRANT SELECT ON mysql.user TO 'dba_monitor'@'%';

FLUSH PRIVILEGES;

巡检频率建议

频率 检查项
每日 1/3/5/6/7/9/13 节(实例/连接/锁/复制/告警)
每周 4/8/10 节(空间/慢SQL/索引)
每月 2/11/12 节(参数/Binlog/权限)
按需 出现问题时全量跑一遍

脚本可直接应用于生产环境,所有查询均来自 performance_schemainformation_schema只读、无锁、无影响,可以放心使用。

相关推荐
AI木马人2 小时前
9.【AI任务队列实战】如何在高并发下保证系统不崩?(Redis + Celery完整方案)
数据库·人工智能·redis·神经网络·缓存
2401_883600252 小时前
golang如何理解weak pointer弱引用_golang weak pointer弱引用总结
jvm·数据库·python
aLTttY2 小时前
【Redis实战】分布式锁的N种实现方案对比与避坑指南
数据库·redis·分布式
2301_773553623 小时前
mysql如何评估SQL语句的索引开销_mysql性能追踪与分析
jvm·数据库·python
pele4 小时前
PHP源码运行受主板供电影响吗_供电相数重要性说明【技巧】
jvm·数据库·python
sinat_383437364 小时前
CSS如何实现元素悬浮在页面底部_利用fixed定位与底部间距
jvm·数据库·python
gmaajt4 小时前
mysql如何备份与恢复函数定义_mysql mysqldump导出存储对象
jvm·数据库·python
阿丰资源4 小时前
基于SpringBoot的在线视频教育平台的设计与实现(附源码+数据库+文档,一键运行)
数据库·spring boot·后端
qq_460978404 小时前
Python爬虫怎么模拟手机端抓取_设置手机型号User-Agent字符串
jvm·数据库·python