不可见索引:安全删除索引的中间态工具
MySQL 8.0引入的不可见索引(Invisible Index)允许将索引标记为对优化器不可见,但索引本身仍然被维护(写入时仍会更新)。这个特性在生产环境的价值在于:提供了一个低成本验证索引删除影响的安全路径,而非直接DROP INDEX后发现问题再紧急重建。
-- 将索引设为不可见
ALTER TABLE orders ALTER INDEX idx_create_time INVISIBLE;
-- 验证优化器行为
EXPLAIN SELECT * FROM orders WHERE create_time > '2026-08-01';
-- 如果查询计划走了全表扫描或选择了其他索引,说明该索引确实可删
-- 确认安全后删除
ALTER TABLE orders DROP INDEX idx_create_time;
-- 如果发现问题(查询变慢),立即恢复
ALTER TABLE orders ALTER INDEX idx_create_time VISIBLE;
关键细节:不可见索引在主从复制中正常同步,从库的索引状态与主库一致。当使用FORCE INDEX或USE INDEX提示时,不可见索引不会被强制使用,即使显式指定也会报索引不存在的错误。
不可见索引的典型应用场景
场景一:清理冗余索引前的灰度验证。生产库中积累的历史索引往往难以判断是否还有查询依赖。先将索引设为不可见,观察一周慢查询日志,无异常则安全删除。
场景二:新索引上线前的性能对比。创建新索引后,先设为不可见,通过optimizer_switch在会话级别启用不可见索引进行A/B对比:
-- 创建新索引(设为不可见)
ALTER TABLE orders ADD INDEX idx_status_create (status, create_time) INVISIBLE;
-- 会话级别启用不可见索引
SET SESSION optimizer_switch = 'use_invisible_indexes=on';
-- 在当前会话中验证新索引效果
EXPLAIN SELECT * FROM orders WHERE status = 1 AND create_time > '2026-08-01';
-- 确认效果后全局可见
ALTER TABLE orders ALTER INDEX idx_status_create VISIBLE;
降序索引:真正的倒序存储实现
MySQL 8.0之前,CREATE TABLE中的DESC索引定义会被静默忽略,索引实际按ASC存储,ORDER BY … DESC只能靠反向扫描实现。MySQL 8.0的降序索引真正支持倒序存储,这对于复合排序查询的性能优化至关重要。
-- MySQL 5.7:DESC被忽略,实际还是ASC索引
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
user_id BIGINT,
create_time DATETIME,
INDEX idx_user_time (user_id, create_time DESC) -- DESC被忽略
);
-- MySQL 8.0:真正的降序索引
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
user_id BIGINT,
create_time DATETIME,
INDEX idx_user_time (user_id ASC, create_time DESC) -- 真正降序存储
);
降序索引的实际性能差距体现在以下查询:
-- 查询:获取某用户最近的订单
SELECT * FROM orders
WHERE user_id = 1001
ORDER BY create_time DESC
LIMIT 20;
-- MySQL 5.7执行计划:Extra列出现Backward index scan
-- 虽然能用索引,但反向扫描在高并发下性能下降明显
-- MySQL 8.0执行计划:正向扫描降序索引,无Backward标记
-- 扫描效率更高,特别是在范围查询场景
降序索引的生产级优化案例
业务场景:电商订单列表,需要按用户ID筛选并按创建时间倒序分页,同时按订单状态正序排序(状态相同则时间倒序)。
-- 原索引(全ASC)
ALTER TABLE orders ADD INDEX idx_user_status_time (user_id, status, create_time);
-- 查询
SELECT * FROM orders
WHERE user_id = 1001
ORDER BY status ASC, create_time DESC
LIMIT 20;
-- EXPLAIN显示:Using index condition; Backward index scan
-- 5.7下需要filesort
-- MySQL 8.0优化索引
ALTER TABLE orders ADD INDEX idx_user_status_time_v2
(user_id, status ASC, create_time DESC);
-- EXPLAIN显示:Using index condition
-- 无filesort,无Backward scan
-- 压测对比:QPS从3200提升至5800,P99延迟从18ms降至7ms
降序索引的使用限制与踩坑
限制一:降序索引不支持MIN()和MAX()优化。对降序列执行MAX()时,MySQL无法直接取索引第一条记录,退化为全索引扫描。
限制二:降序索引与JSON列上的函数索引不兼容,创建时会报错。
限制三:GROUP BY的隐式排序行为改变。MySQL 8.0中降序索引导致GROUP BY结果不再隐式排序,必须显式添加ORDER BY。这是一个常见的升级兼容性问题。
-- MySQL 5.7:GROUP BY隐含ORDER BY
SELECT status, COUNT(*) FROM orders GROUP BY status;
-- 结果按status排序
-- MySQL 8.0:无隐式排序
SELECT status, COUNT(*) FROM orders GROUP BY status;
-- 结果可能无序
-- 修复:显式ORDER BY
SELECT status, COUNT(*) FROM orders GROUP BY status ORDER BY status;
这两个特性配合使用的最佳实践:先用不可见索引灰度验证新索引效果,确认降序索引的排序方向确实匹配业务查询后,再切换可见性并清理旧索引。整个过程对生产环境零影响。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/mysql80-bu-ke-jian-suo-yin-yu-jiang-xu-suo-yin-de-sheng/