
1. MySQL优化全攻略从索引设计到分库分表的完整方案作为关系型数据库的标杆产品MySQL的性能优化一直是开发者关注的焦点。我在过去五年处理过上百个性能瓶颈案例发现80%的慢查询问题都源于索引设计不当。本文将系统梳理从单表索引优化到分布式架构设计的完整知识体系特别会分享生产环境中验证过的实战技巧。2. 索引优化核心原理与实践2.1 B树索引的底层运作机制MySQL的InnoDB引擎采用B树作为索引数据结构其特点是所有数据都存储在叶子节点且叶子节点通过指针相连。这种结构使得范围查询效率极高——当执行WHERE id 100这类查询时引擎只需定位到第一个符合条件的叶子节点然后沿着指针遍历即可。我曾在电商系统中处理过一个典型案例商品表最初使用UUID作为主键导致写入时频繁发生页分裂。改为自增ID后插入性能提升近3倍。这是因为自增ID的递增特性使新数据总是插入到B树最右侧节点避免了中间插入导致的树结构调整。2.2 复合索引的最左前缀原则建立(a,b,c)复合索引时实际相当于创建了三个索引(a)(a,b)(a,b,c)但以下查询无法使用该索引WHERE b 1 WHERE c 1 WHERE b 1 AND c 1去年优化过一个物流系统其订单查询包含(region_id, create_time, status)三个条件。通过将复合索引顺序调整为(region_id, status, create_time)查询耗时从1200ms降至80ms。关键是要把区分度高的字段放在前面并通过EXPLAIN验证索引使用情况。2.3 索引失效的六大陷阱隐式类型转换当字段定义为varchar但查询使用数字时如WHERE mobile 13800138000使用函数操作WHERE DATE(create_time) 2023-01-01!或操作符WHERE status ! 1前导通配符WHERE name LIKE %张OR条件未全覆盖WHERE a1 OR b2需分别在a、b建索引索引列参与运算WHERE id1 100特别提醒IS NULL判断在某些场景下会导致索引失效建议设置NOT NULL约束并设置默认值3. SQL语句优化实战技巧3.1 执行计划深度解读EXPLAIN输出中的关键字段type从优到差依次为 system const eq_ref ref range index ALLrows预估扫描行数应尽可能小Extra出现Using filesort或Using temporary时需要警惕最近排查的一个案例某报表查询耗时15秒EXPLAIN显示扫描了200万行。发现是由于GROUP BY与ORDER BY字段顺序不一致导致双重排序。调整后查询仅需0.3秒。3.2 分页查询优化方案典型错误写法SELECT * FROM orders ORDER BY id LIMIT 1000000, 10优化方案-- 方案1使用主键定位 SELECT * FROM orders WHERE id 1000000 ORDER BY id LIMIT 10 -- 方案2延迟关联 SELECT t.* FROM orders t JOIN (SELECT id FROM orders ORDER BY id LIMIT 1000000, 10) tmp ON t.id tmp.id在用户行为分析系统中方案2使分页查询从12秒降至0.8秒。核心思路是先定位主键再回表避免大数据量偏移。4. 分库分表架构设计4.1 拆分策略对比策略类型优点缺点适用场景水平拆分扩展性强跨分片查询复杂数据量大且增长快垂直拆分业务清晰单表容量未解决字段冷热分离明显哈希取模分布均匀扩容困难无明显查询特征范围分片易于扩容可能热点集中有时间或ID范围特征去年设计的金融交易系统采用双维度分片先按用户ID哈希分库再按交易月份范围分表。配合ShardingSphere中间件实现了日均亿级交易量的稳定处理。4.2 分布式ID生成方案雪花算法64位ID 时间戳(41bit) 机器ID(10bit) 序列号(12bit)数据库号段每次从DB获取一个号段范围缓存在内存UUID简单但无序影响索引性能我们最终选择改良版雪花算法将机器ID部分改为ZK动态分配解决了容器环境下机器ID固定的问题。同时将时间戳基准调整为项目启动时间支持更早的时间戳。5. 生产环境踩坑实录5.1 在线DDL操作的血泪教训某次在800万用户表上直接执行ALTER TABLE ADD COLUMN导致数据库CPU飙升至100%持续40分钟。后续改用以下方案-- 使用pt-online-schema-change工具 pt-online-schema-change \ --alterADD COLUMN age INT \ Ddatabase,tuser \ --execute工具原理是创建影子表通过触发器同步数据变更最后原子性切换表名。整个过程对业务影响小于5秒。5.2 连接池配置的黄金法则监控发现某服务频繁出现MySQL server has gone away错误最终定位到连接池配置问题。推荐配置# Druid连接池 spring.datasource.druid.initial-size5 spring.datasource.druid.max-active20 spring.datasource.druid.min-idle5 spring.datasource.druid.max-wait60000 spring.datasource.druid.time-between-eviction-runs-millis60000 spring.datasource.druid.min-evictable-idle-time-millis300000 spring.datasource.druid.validation-querySELECT 1关键是要设置合理的空闲检测时间避免使用已被服务端关闭的连接。同时max-active不宜过大否则会导致数据库连接数暴涨。6. 高级优化技巧6.1 索引跳跃扫描优化MySQL 8.0新增的Index Skip Scan特性允许优化器在某些情况下使用复合索引的非前缀列。例如索引(gender, age)查询WHERE age 20时优化器会先枚举gender的所有值M,F然后分别执行WHERE genderM AND age20和WHERE genderF AND age20。实测在用户画像系统中这种特性使某些查询速度提升8倍。可通过optimizer_switch参数控制SET optimizer_switchskip_scanon;6.2 不可见索引的使用场景MySQL 8.0支持创建不可见索引INVISIBLE INDEX这对索引变更验证非常有用-- 创建不可见索引 CREATE INDEX idx_name ON users(name) INVISIBLE; -- 切换可见性 ALTER TABLE users ALTER INDEX idx_name VISIBLE;我们在灰度发布时先创建不可见索引通过USE INDEX指令让部分查询试用确认效果后再全局启用实现了索引变更零停机。