当前位置: 首页 > news >正文

mysql8.0 无流量表/索引统计

008-8.0无流量表/索引统计

MySQL的performance_schema.table_io_waits_summary_by_table 和 performance_schema.table_io_waits_summary_by_index_usage

table_io_waits_summary_by_table 可以统计出 无流量的表。
table_io_waits_summary_by_index_usage 可以统计出 无流量的索引。

这里要上述数据准确,那么前提是:
performance_schema = ON

启用 consumers:
UPDATE performance_schema.setup_consumers
SET ENABLED = 'YES'
WHERE NAME LIKE 'events_waits%'
OR NAME LIKE 'events_statements%';

UPDATE performance_schema.setup_consumers
SET ENABLED = 'YES'
WHERE NAME IN ('global_instrumentation', 'thread_instrumentation');

启用 instruments: (TIMED是IO等待持续时间等计量数据也要打开)
UPDATE performance_schema.setup_instruments
SET ENABLED = 'YES', TIMED = 'YES'
WHERE NAME LIKE 'wait/%';

UPDATE performance_schema.setup_instruments
SET ENABLED = 'YES', TIMED = 'YES'
WHERE NAME LIKE 'statement/%';

确认consumers否开启:
SELECT NAME, ENABLED
FROM performance_schema.setup_consumers
WHERE NAME LIKE 'events_waits%'
OR NAME LIKE 'events_statements%';

SELECT NAME, ENABLED
FROM performance_schema.setup_consumers
WHERE NAME IN ('global_instrumentation', 'thread_instrumentation');

查看instruments是否已启用
SELECT NAME, ENABLED, TIMED
FROM performance_schema.setup_instruments
WHERE NAME LIKE 'wait/%'
OR NAME LIKE 'statement/%'

启动时持久化配置(写入 my.cnf)
[mysqld]

确保 performance_schema 已开启

performance_schema = ON

--- 消费者(Consumers)启动配置 ---

performance-schema-consumer-events-waits-current = ON
performance-schema-consumer-events-waits-history = ON
performance-schema-consumer-events-waits-history-long = ON
performance-schema-consumer-events-statements-current = ON
performance-schema-consumer-events-statements-history = ON
performance-schema-consumer-events-statements-history-long = ON
performance-schema-consumer-statements-digest = ON

--- 采集器(Instruments)启动配置 ---

开启所有等待事件和语句事件

performance-schema-instrument = 'wait/%=ON'
performance-schema-instrument = 'statement/%=ON'

--------------- 知识补充 ----------------------
performance_schema.setup_consumers:消费者(consumer)决定事件数据是否被存储到 current、history 或 history_long 表中。
消费者名称 作用
events_waits_current 存储每个线程当前正在等待的事件
events_waits_history 存储每个线程最近结束的等待事件(按线程)
events_waits_history_long 存储全局最近结束的等待事件(跨线程)
events_statements_current 存储每个线程当前正在执行的 SQL 语句
events_statements_history 存储每个线程最近执行完的 SQL 语句(按线程)
events_statements_history_long 存储全局最近执行完的 SQL 语句(跨线程)

performance_schema.setup_instruments:决定哪些具体的操作会被收集事件。
所有名称以 wait/ 开头的仪器都属于等待事件,例如:
wait/synch/mutex/... (互斥锁等待)
wait/synch/rwlock/... (读写锁等待)
wait/synch/cond/... (条件变量等待)
wait/io/file/... (文件 I/O 等待)
wait/io/table/... (表 I/O 等待)
wait/lock/... (锁等待)

语句事件(Statements)相关:
statement/sql/%: 所有 SQL 语句(如 select, insert, update, create_table 等)。
statement/sp/%: 存储过程/函数内部的语句。
statement/com/%: 客户端与服务端的通信命令(如 Sleep, Quit, Init DB)。
statement/abstract/%: 抽象语句事件(如 Query, new_packet)

开启 events_waits 和 events_statements 会带来一定的性能开销(尤其是 history_long 表和细粒度的 wait/io 事件)。在生产环境中,建议:
仅开启必要的采集器(例如只开启 wait/io/file/innodb/% 而不是 wait/%)。
如果只需要计数而不需要精确的耗时统计,可以将 TIMED 设为 NO,或在启动配置中使用 COUNTED 代替 ON(例如 performance-schema-instrument = 'wait/synch/mutex/%=COUNTED')。

================================ 有哪些情况下table_io_waits_summary_by_index_usage 统计不到 =====================

即使 SQL 实际执行并使用了索引,performance_schema.table_io_waits_summary_by_index_usage 视图中的统计值(如 COUNT_STAR、COUNT_READ、COUNT_FETCH 等)依然可能显示为 0。这通常由以下两大类原因导致:

(1)监控未开 (如上)
(2)统计窗口太短
该视图只统计开启监控后新发生的 I/O 操作。由于是内存数据,如果数据库刚重启、刚清空过历史数据(FLUSH TABLES),或者刚启用监控但查询频次极低,历史计数为 0 并不代表该索引从未被使用过。

二、 索引的特殊使用场景(未触发常规 I/O 等待事件)
(1)覆盖索引(Covering Index):当查询走的是覆盖索引(执行计划 Extra 显示 Using index),底层直接读取二级索引 B+ 树的叶子节点,不触发常规的 “index read” 等待事件,因此 COUNT_FETCH 不会上涨。

(2)仅用于排序或分组:如果索引仅用于 ORDER BY 或 GROUP BY 以避免 filesort,且没有 WHERE 条件触发实际的索引查找(纯排序扫描),它不会触发 COUNT_READ。

(3)极低频查询或采样丢失:查询带了 LIMIT 1 且命中首行,实际只触发了一次索引查找,但 performance_schema 的统计粒度是“等待事件”,可能会被聚合或采样漏掉。

(4)隐式失效或全表扫描:某些操作(如隐式类型转换、LIKE '%xxx' 前缀模糊匹配)会让 MySQL 优化器跳过索引进行全表扫描。虽然你“以为”它该用索引,但实际上并未走索引,自然不会有记录。

(5)DDL 或内部隐式访问:某些 DDL 操作(如 ANALYZE TABLE)会触发隐式索引访问,但不走常规查询路径,不会被计入统计。

(6)查询命中缓存:在 MySQL 5.7 及更早版本中,如果查询结果被 Query Cache 缓存,后续响应不经过索引扫描路径,也不会记录 I/O 等待。

(7)如果是 select * from tt where t_num = 1241359; 中t_num有普通索引。而这个值 1241359 查找不到,返回空 情况下,COUNT_STAR、COUNT_READ、COUNT_FETCH 均不会计数。 而如果是主键或唯一索引,即使返回空,也会计数。(具体测试后面)

为什么 InnoDB 的自适应哈希索引(AHI) 同样会记录索引扫描?
统计视图的本质:table_io_waits_summary_by_index_usage 记录的是底层 B+ 树索引的 I/O 等待事件。只要查询触发了对 B+ 树索引的访问(无论是读还是写),这个计数器就会累加。

AHI 的本质:自适应哈希索引是 InnoDB 引擎在内存中基于 B+ 树自动构建的哈希表。当查询命中 AHI 时,它确实跳过了 B+ 树的逐层遍历(将 O(log n) 降为 O(1)),但它依然是依附于底层 B+ 树索引存在的。
结论:即使查询完全命中了 AHI,InnoDB 内部依然会记录这次访问是针对哪个 B+ 树索引的,因此 COUNT_READ 等指标依然会正常增加。AHI 只是加速了查找过程,并没有“抹除”底层索引的使用记录。

注意:sys.schema_unused_indexes 底层也是用的 performance_schema.table_io_waits_summary_by_index_usage
相当于这样一个视图
SELECT object_schema, object_name, index_name
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE index_name IS NOT NULL
AND count_star = 0
AND object_schema <> 'mysql'
AND index_name <> 'PRIMARY'

-- 开启 AHI
SET GLOBAL innodb_adaptive_hash_index = ON;

[mysqld]
innodb_adaptive_hash_index = ON # 或 OFF

========== 如何验证索引是否真的被使用? =======================

由于 performance_schema 的统计是“必要但不充分条件”,不能单凭它为 0 就判定索引无用。建议结合以下方式交叉验证:

1、使用 EXPLAIN 分析执行计划:查看 key 字段是否命中该索引,以及 type 是否为 ref、range 或 index。
2、检查慢查询日志/或者审计日志:在慢日志中查看该 SQL 的执行计划,确认 Extra: Using index 是否明确带有该索引名。
3、排查代码硬编码:检查应用代码或 SQL代理日志/审计日志,确认是否使用了 FORCE INDEX (idx_name) 或 USE INDEX 强制指定了该索引。
4、检查外键与约束依赖:确认该索引是否被外键约束依赖,或被 INSERT ... ON DUPLICATE KEY UPDATE 等语句用于冲突判断,这类索引即使查询不用也不能删。

查询 table_io_waits_summary_by_index_usage 表数据, 统计未流量表或者索引,使用COUNT_STAR、COUNT_READ、COUNT_FETCH字段判断。

select * from
performance_schema.table_io_waits_summary_by_index_usage t
where object_schema='dbtest'
and object_name = 'tt'
and index_name = 'idx_num'

字段说明: table_io_waits_summary_by_index_usage 和 table_io_waits_summary_by_table

通用 I/O 等待统计列
COUNT_STAR:I/O 等待事件的总次数。
SUM_TIMER_WAIT:I/O 等待的总时间。
MIN_TIMER_WAIT:I/O 等待的最小时间。
AVG_TIMER_WAIT:I/O 等待的平均时间。
MAX_TIMER_WAIT:I/O 等待的最大时间。

读操作统计列(Read / Fetch)
COUNT_READ / COUNT_FETCH:读取(获取)操作的总次数。
SUM_TIMER_READ / SUM_TIMER_FETCH:读取操作的总等待时间。
MIN/AVG/MAX_TIMER_READ:读取操作的最小、平均、最大等待时间。

写操作统计列(Write)
COUNT_WRITE:写入操作的总次数。
SUM_TIMER_WRITE:写入操作的总等待时间。
MIN/AVG/MAX_TIMER_WRITE:写入操作的最小、平均、最大等待时间。

table_io_waits_summary_by_table 特有细分
这张表将写操作进一步细分为具体的 DML 操作:
COUNT_INSERT / SUM_TIMER_INSERT / MIN/AVG/MAX_TIMER_INSERT:INSERT 操作的次数与等待时间统计。
COUNT_UPDATE / SUM_TIMER_UPDATE / MIN/AVG/MAX_TIMER_UPDATE:UPDATE 操作的次数与等待时间统计。
COUNT_DELETE / SUM_TIMER_DELETE / MIN/AVG/MAX_TIMER_DELETE:DELETE 操作的次数与等待时间统计。

table_io_waits_summary_by_index_usage 特有字段
INDEX_NAME:索引名称。这是该表最核心的维度列。
如果值为 PRIMARY:表示 I/O 操作使用了主键索引。
如果值为具体的索引名(如 idx_user_id):表示使用了该二级索引。
如果值为 NULL:表示表 I/O 没有使用任何索引(全表扫描)或者该操作是 INSERT(插入操作无法利用现有索引进行查找)。
同样包含与 by_table 表相同的 INSERT、UPDATE、DELETE 细分统计列。

运维与使用注意事项
(1)数据重置机制:
这两张表允许使用 TRUNCATE TABLE 语句。执行后,统计计数器会重置为零,但不会删除表中的行记录。
联动重置:对 table_io_waits_summary_by_table 执行 TRUNCATE 时,会隐式触发 table_io_waits_summary_by_index_usage 的重置。
DDL 影响:如果对表执行了 DDL 语句(如添加、删除、修改索引结构),该表相关的索引统计信息会被重置。

(2)典型应用场景:
查找慢表:通过查询 table_io_waits_summary_by_table 中 SUM_TIMER_WAIT 最大的记录,快速定位导致数据库 I/O 瓶颈的表。
识别未使用的索引:查询 table_io_waits_summary_by_index_usage 中 COUNT_STAR、 COUNT_READ、COUNT_FETCH 都为0 且 INDEX_NAME != 'PRIMARY' 的记录,找出从未被使用过的冗余索引,以便安全删除,节省存储空间并提升写入性能。
发现全表扫描:查询 INDEX_NAME IS NULL 的记录,找出频繁发生全表扫描的表,提示需要优化 SQL 或添加索引。

=================== 普通索引 返回空 情况下,table_io_waits_summary_by_index_usage 不计数 ======================

CREATE TABLE tt (
id bigint(20) NOT NULL,
idx_num bigint(20) DEFAULT NULL,
uk_num bigint(20) NOT NULL,
invalid varchar(100) default null,
PRIMARY KEY (id),
UNIQUE KEY uk_num (uk_num),
KEY idx_num (idx_num)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC

id 主键。 uk_num 唯一键。 idx_num普通索引。

主键:
select * from tt where id = 1241359; # 不存在值ID
select * from tt where id = 366; # 存在值ID

唯一键:
select * from tt where uk_num = 1241359; # 不存在值ID
select * from tt where uk_num = 366; # 存在值ID

普通索引:
select * from tt where idx_num = 1241359; # 不存在值ID
select * from tt where idx_num = 6289113; # 存在值ID

查询SQL:
select object_schema,object_name,index_name,
count_star,count_fetch,count_read,count_write,count_insert,count_update,count_delete from
performance_schema.table_io_waits_summary_by_index_usage t
where object_schema='dbtest'
and object_name = 'tt'

示例:

alter table tt modify column invalid varchar(300) default null; # 修改结构会清空该表数据
select * from tt where idx_num = 1241359; # 不存在值ID
select object_schema,object_name,index_name,
count_star,count_fetch,count_read,count_write,count_insert,count_update,count_delete from
performance_schema.table_io_waits_summary_by_index_usage t
where object_schema='dbtest'
and object_name = 'tt'

至于原因:来源于AI
Performance Schema 的 table_io_waits 统计的是“物理 I/O 操作”或“明确的记录级访问”,而不是“逻辑上的查询尝试”。

普通索引(Secondary Index)查不到数据时: InnoDB 优化器判断这是一次“无效的索引查找”,虽然 B+ 树遍历发生了(读了页),但在 PFS 的埋点逻辑中,这次遍历被视为“未命中”,不触发针对该索引的 I/O Wait 计数器增加。

唯一索引/主键(Unique/PK)查不到数据时: InnoDB 必须确认“这条记录确实不存在”以保证唯一性约束或主键定位的准确性。这种查找被定义为一次确定的记录访问尝试(即使结果是空),因此会触发 PFS 计数。

但是status状态值。
handler_read_key 索引读取是要计数的。

验证:
FLUSH STATUS;
SELECT * FROM tt WHERE t_num = 1241359;
SHOW STATUS LIKE 'handler_read_key';

清理无用索引,建议使用8.0的visible方案。 可以快速恢复,避免删除后导致查询慢。恢复需要较长时间。

修改已有索引的可见性:
ALTER TABLE t1 ALTER INDEX i_idx INVISIBLE;
ALTER TABLE t1 ALTER INDEX i_idx VISIBLE;

创建时指定。
ALTER TABLE t1 ADD INDEX k_idx (k) INVISIBLE;
CREATE INDEX j_idx ON t1 (j) INVISIBLE;

通过数据字典表 INFORMATION_SCHEMA.STATISTICS 或者SHOW INDEX命令可以查看索引的可见性。
SELECT
TABLE_SCHEMA,
TABLE_NAME,
INDEX_NAME,
IS_VISIBLE
FROM
INFORMATION_SCHEMA.STATISTICS
WHERE
TABLE_SCHEMA = 'dbtest'
AND TABLE_NAME = 'tt';

或者: SHOW INDEX FROM tt;
或者: show create table tt 也是可以的

快速查询出隐藏索引:
SELECT
TABLE_SCHEMA,
TABLE_NAME,
INDEX_NAME,
COLUMN_NAME,
IS_VISIBLE
FROM
INFORMATION_SCHEMA.STATISTICS
WHERE
IS_VISIBLE = 'NO';

索引的可见性不会影响索引的维护。例如,无论索引是否可见,每次修改表中的数据时都需要对相应索引进行更新,而且唯一索引都会阻止插入重复的列值。

http://www.gsyq.cn/news/1554801.html

相关文章:

  • 2026 广州奢侈品黄金回收门店甄选评测:正规优质渠道选耀辉 - 奢侈品回收
  • 2026武汉黄金回收门店实力排名 禹竞名奢汇全域布局优势领跑全城 - 名奢变现站
  • 东莞闲置腕表变现,2026靠谱名表回收实体店汇总 - 名奢变现站
  • 旧金高价出手!宁波全域可上门,连锁老店放心托付 - 奢侈品交易观察员
  • 2026重庆名表回收实力星级榜|5家实体店测评,收的顶荣膺标杆 - 奢侈品回收测评
  • 如何扩展PHP-DDD-Cargo-Sample:添加新领域服务与集成外部系统的完整指南
  • ToolsFx:一站式密码学工具箱的终极使用指南
  • 如何永久保存微信聊天记录:打造属于你的个人AI记忆库
  • 2026 福州黄金回收黑白名单更新!持证备案合规门店共 6 家实地测评 - 奢侈品回收评测
  • 2026搬家寄快递怎么省钱?大件小件省钱技巧+比价神器 - 快递物流资讯
  • 技术深度解析:ComfyUI-WanVideoWrapper AI视频生成架构与实战应用
  • 2026 年 6 月上海手表回收行情,闲置劳力士欧米茄高价出手指南 - 讯息早知道
  • 2026淮南最新发布|淮南初中生医护3+2中高职贯通招生招生热线周老师:19355104487 - 小张zc
  • 宁夏医院洁净工程一站式承建,四川华锐净化专注手术室检验科专项建设 - 洁净室推广助手
  • 沈阳没保卡手表能回收吗?2026 估价标准科普 - 奢品小当家
  • 合肥政务区 管道疏通|维小达|马桶、蹲便器、地漏、洗菜盆、洗手盆、浴缸、主管道一站式疏通清淤服务 - 维小达科技
  • 2026 南充防水补漏靠谱服务商盘点:屋面 / 厨卫 / 外墙 / 地下室渗水维修详解,适配南充嘉陵江沿岸川中丘陵防潮防水甄选指南 - 宅安选房屋修缮
  • 2026西安奢侈品回收避坑!90%的人都被隐形压价了 - 讯息早知道
  • 【NI-RIO实战】从零部署CompactRIO:软件栈安装与兼容性全解析
  • 济南闲置黄金变现渠道测评!五家回收机构综合参考指南 - 奢品小当家
  • 2026年6月梅州瓦楞纸箱厂质量排名权威榜:对口箱/天地盖/裹包式箱/异型箱,高品质纸箱精准适配各行业产品防护需求 - 东社造纸
  • 大连黄金回收怎么选门店?2026 合规商家实测对比,远离缺斤少两 - 奢侈品交易观察员
  • 2026贵阳各区县黄金回收测评 教你看懂实时金价防套路 - 润富黄金回收
  • 大连首饰回收商家排名,报价公道实体店推荐 - 讯息早知道
  • 10分钟快速上手AVBD-demo2d:Web版与原生版运行完全指南
  • 2026重庆闲置包包回收全指南|本地7家门店实测+行业科普,新手变现不踩坑 - 沉迷学习28
  • 2026 上海黄金回收品牌分层测评,多维度解析门店配套增值服务 - 奢侈品回收测评
  • 老板娘财税培训机构怎么选?看哪几个能力维度 | 4个维度对比 - 欢欢在创业
  • 海口国贸连锁奢品中心鉴表现场实录,机芯无损检测全流程记录 - 开心测评
  • CSDNGreener终极净化指南:彻底告别CSDN广告困扰,开启纯净技术阅读之旅