1. 什么是统计信息?
统计信息就像是数据库的"地图",它告诉优化器:
-
每个表有多大(有多少行数据)
-
每个索引的"区分度"(有多少不同的值)
-
数据分布情况(哪些值出现频率高)
2. 为什么需要统计信息?
想象你要找一本图书馆的书:
-
没有统计信息:盲目地在每个书架搜索
-
有统计信息:直接去最可能存放的书架查找
二、统计信息核心内容全景图
统计信息类型 | 存储位置 | 更新方式 | 影响范围 |
---|---|---|---|
表大小/行数 | data_dictionary | 自动/手动ANALYZE | 全表扫描成本计算 |
索引基数(Cardinality) | mysql.innodb_index_stats | 自动/手动ANALYZE | 索引选择 |
直方图(Histogram) | column_statistics | 手动ANALYZE TABLE | 等值查询优化 |
索引深度 | 内存计算 | 自动 | 范围查询效率 |
三、统计信息全生命周期管理
1. 创建阶段
sql
-- 创建表时指定统计信息策略
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(100),
INDEX (name)
) STATS_PERSISTENT=1 STATS_AUTO_RECALC=1;
2. 更新机制
自动更新触发条件:
-
表中数据变化超过10%(默认阈值)
-
第一次打开表时
-
执行SHOW TABLE STATUS时(某些版本)
手动更新方法:
sql
-- 标准语法
ANALYZE TABLE users;
-- 更新直方图统计(MySQL 8.0+)
ANALYZE TABLE users UPDATE HISTOGRAM ON name WITH 32 BUCKETS;
- 监控方法
sql
-- 检查统计信息时效性
SELECT
table_name,
update_time,
CONCAT(ROUND(data_length/1024/1024,2),'MB') AS size,
IF(DATEDIFF(NOW(),update_time)>7,'⚠️过期','✅正常') AS status
FROM
information_schema.tables
WHERE
table_schema=DATABASE();
-- 查看索引统计详情
SELECT * FROM mysql.innodb_index_stats WHERE table_name='users';
四、生产环境最佳实践
1. 参数配置建议
sql
[mysqld]
# 启用持久化统计(重启不丢失)
innodb_stats_persistent=ON
# 自动重新计算统计
innodb_stats_auto_recalc=ON
# 增加采样页面提高准确性
innodb_stats_persistent_sample_pages=32
2. 维护方案
日常维护:
sql
-- 每周维护脚本示例
SET @db = DATABASE();
SELECT CONCAT('ANALYZE TABLE ', table_name, ';')
FROM information_schema.tables
WHERE table_schema = @db
AND DATEDIFF(NOW(),update_time) > 7
INTO OUTFILE '/tmp/analyze_tables.sql';
SOURCE /tmp/analyze_tables.sql;
大表特殊处理:
sql
# 使用pt-table-sync分批分析
pt-table-sync --analyze h=localhost,D=dbname,t=large_table
五、常见问题解决方案
1. 统计信息不准的症状
-
执行计划突然变差
-
相同的查询有时快有时慢
-
EXPLAIN显示预估行数与实际严重不符
2. 紧急处理方法
sql
-- 立即刷新问题表的统计信息
FLUSH TABLE users;
ANALYZE TABLE users;
-- 强制使用指定索引(临时方案)
SELECT /*+ INDEX(users name_idx) */ * FROM users WHERE name LIKE 'A%';
3. 自动化监控方案
sql
-- 创建监控事件
CREATE EVENT monitor_stats
ON SCHEDULE EVERY 1 DAY
DO
BEGIN
DECLARE cnt INT;
SELECT COUNT(*) INTO cnt FROM information_schema.tables
WHERE table_schema=DATABASE() AND DATEDIFF(NOW(),update_time)>7;
IF cnt > 0 THEN
-- 发送报警邮件或记录日志
CALL send_alert(CONCAT(cnt, ' tables need analyze'));
END IF;
END;
六、可视化学习工具
-
统计信息查看:
sql-- 使用MySQL Workbench可视化统计信息 -- 右键表 → Table Inspector → Indexes/Statistics
-
执行计划对比:
sql-- 更新统计前后对比 EXPLAIN FORMAT=TREE SELECT * FROM users WHERE age > 30; ANALYZE TABLE users; EXPLAIN FORMAT=TREE SELECT * FROM users WHERE age > 30;