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_schema 和 information_schema,只读、无锁、无影响,可以放心使用。