MySQL SQL 调优是一个系统性的工程涉及多个层面的优化。一、性能分析工具1. 查询分析工具-- 执行计划分析EXPLAINSELECT*FROMusersWHEREage25;EXPLAINFORMATJSONSELECT*FROMusersWHEREage25;EXPLAINANALYZESELECT*FROMusersWHEREage25;-- MySQL 8.0-- 性能模式监控SELECT*FROMperformance_schema.events_statements_summary_by_digestWHEREDIGEST_TEXTLIKE%users%;-- 慢查询日志分析SHOWVARIABLESLIKEslow_query_log%;SHOWVARIABLESLIKElong_query_time;2. 系统状态监控-- 查看当前连接和状态SHOWPROCESSLIST;SHOWSTATUSLIKEThreads_connected;SHOWENGINEINNODBSTATUS;-- 关键性能指标SHOWSTATUSLIKEInnodb_rows_read%;SHOWSTATUSLIKESelect_scan%;-- 全表扫描次数SHOWSTATUSLIKESort_merge_passes%;-- 排序合并次数二、索引优化策略1. 索引设计原则-- ✅ 选择合适的索引列-- 高选择性字段优先SELECTCOUNT(DISTINCTstatus)/COUNT(*)asselectivityFROMorders;-- 选择性低的字段不适合单独建索引-- ✅ 复合索引设计最左前缀原则CREATEINDEXidx_user_status_dateONorders(user_id,status,create_time);-- ❌ 避免过多索引影响写性能SELECTTABLE_NAME,INDEX_NAME,COUNT(*)FROMinformation_schema.STATISTICSGROUPBYTABLE_NAME,INDEX_NAME;2. 索引优化实战-- 案例优化范围查询-- 原始查询性能差EXPLAINSELECT*FROMordersWHEREcreate_timeBETWEEN2024-01-01AND2024-01-31ANDstatuscompleted;-- 优化方案调整索引顺序等值查询字段在前CREATEINDEXidx_status_timeONorders(status,create_time);-- 案例覆盖索引优化-- 原始需要回表EXPLAINSELECTuser_id,order_noFROMordersWHEREstatuspending;-- 优化使用覆盖索引CREATEINDEXidx_status_coveringONorders(status,user_id,order_no);-- Extra: Using index3. 索引失效场景及解决方案-- 1. 函数操作导致索引失效 ❌SELECT*FROMusersWHEREDATE(create_time)2024-01-01;-- 优化 ✅SELECT*FROMusersWHEREcreate_time2024-01-01ANDcreate_time2024-01-02;-- 2. 隐式类型转换 ❌SELECT*FROMusersWHEREphone13800138000;-- phone是varchar类型-- 优化 ✅SELECT*FROMusersWHEREphone13800138000;-- 3. 前导通配符 ❌SELECT*FROMusersWHEREnameLIKE%john%;-- 优化 ✅如果必须模糊查询SELECT*FROMusersWHEREnameLIKEjohn%;-- 使用后缀通配符三、SQL 语句优化1. 查询重写优化-- ❌ 低效写法SELECT*FROMordersWHEREorder_idIN(SELECTorder_idFROMorder_itemsWHEREproduct_id100);-- ✅ 优化为JOINSELECTo.*FROMorders oJOINorder_items oiONo.order_idoi.order_idWHEREoi.product_id100;-- ❌ 使用HAVING过滤性能差SELECTuser_id,COUNT(*)FROMordersGROUPBYuser_idHAVINGCOUNT(*)5;-- ✅ 使用子查询优化SELECTuser_id,order_countFROM(SELECTuser_id,COUNT(*)asorder_countFROMordersGROUPBYuser_id)tWHEREorder_count5;2. 分页查询优化-- ❌ 传统分页偏移量大时性能差SELECT*FROMordersORDERBYidLIMIT10000,20;-- ✅ 基于游标的分页推荐SELECT*FROMordersWHEREid10000ORDERBYidLIMIT20;-- ✅ 延迟关联大数据量分页SELECT*FROMorders oJOIN(SELECTidFROMordersORDERBYidLIMIT10000,20)tONo.idt.id;3. 大数据量查询优化-- ❌ 一次性查询大量数据SELECT*FROMuser_behavior_logWHEREcreate_date2024-01-01;-- ✅ 分批查询SELECT*FROMuser_behavior_logWHEREcreate_date2024-01-01ANDid0ORDERBYidLIMIT1000;-- ✅ 使用分区表CREATETABLEuser_behavior_log(idBIGINT,user_idINT,actionVARCHAR(50),create_dateDATE)PARTITIONBYRANGE(YEAR(create_date))(PARTITIONp2023VALUESLESS THAN(2024),PARTITIONp2024VALUESLESS THAN(2025));四、数据库架构优化1. 读写分离-- 主库写操作INSERTINTOorders(...)VALUES(...);UPDATEusersSET...WHERE...;-- 从库读操作SELECT*FROMordersWHERE...;-- 路由到从库2. 分库分表策略-- 按用户ID分表示例CREATETABLEorders_0000LIKEorders;CREATETABLEorders_0001LIKEorders;-- ... 创建1024张分表-- 路由逻辑table_suffix user_id % 10243. 缓存策略-- 查询缓存MySQL 8.0已移除可用Redis等-- 应用层缓存热点数据SELECTSQL_NO_CACHE*FROMproductsWHEREid1;-- 绕过缓存测试真实性能五、配置参数调优1. 内存相关配置# my.cnf 优化配置 [mysqld] # 缓冲池大小通常为物理内存的50-80% innodb_buffer_pool_size 16G # 日志缓冲区大小 innodb_log_buffer_size 64M # 排序缓冲区大小 sort_buffer_size 2M read_buffer_size 2M read_rnd_buffer_size 2M2. 连接相关配置# 最大连接数 max_connections 1000 # 连接超时 wait_timeout 600 interactive_timeout 600 # 临时表配置 tmp_table_size 64M max_heap_table_size 64M六、实战调优案例案例1电商订单查询优化问题场景-- 慢查询多条件组合查询SELECT*FROMordersWHEREuser_id100ANDstatusIN(pending,shipped)ANDcreate_timeBETWEEN2024-01-01AND2024-03-01ORDERBYcreate_timeDESCLIMIT20;优化步骤分析执行计划发现全表扫描Using filesort设计复合索引CREATEINDEXidx_user_status_timeONorders(user_id,status,create_time);优化查询语句-- 使用索引覆盖避免回表SELECTid,user_id,order_no,amount,create_timeFROMordersWHEREuser_id100ANDstatusIN(pending,shipped)ANDcreate_timeBETWEEN2024-01-01AND2024-03-01ORDERBYcreate_timeDESCLIMIT20;案例2报表统计查询优化问题场景-- 月度统计报表性能差SELECTDATE_FORMAT(create_time,%Y-%m)asmonth,COUNT(*)astotal_orders,SUM(amount)astotal_amountFROMordersWHEREcreate_time2023-01-01GROUPBYDATE_FORMAT(create_time,%Y-%m);优化方案预聚合统计-- 创建统计表定时更新CREATETABLEorder_monthly_stats(stat_dateDATEPRIMARYKEY,order_countINT,total_amountDECIMAL(10,2));使用物化视图或定时任务更新统计查询优化后的统计表七、自动化调优工具1. 使用 Percona Toolkit# 分析慢查询日志pt-query-digest slow.log# 分析索引使用情况pt-index-usage slow.log# 在线修改大表结构pt-online-schema-change2. MySQL Enterprise Monitor自动性能建议实时监控告警容量规划预测八、调优检查清单✅ 索引优化检查为高频查询条件添加索引复合索引字段顺序合理等值查询在前避免冗余索引定期分析索引使用情况✅ SQL 语句检查避免 SELECT *只查询需要的字段使用 JOIN 代替子查询优化分页查询避免大偏移量合理使用事务避免长事务✅ 数据库设计检查表结构规范化/反规范化平衡选择合适的数据类型考虑分区表策略读写分离架构✅ 配置参数检查缓冲池大小设置合理日志文件配置适当连接数配置满足业务需求临时表空间充足九、性能监控体系1. 建立监控指标-- 关键性能指标监控-- QPS/TPS每秒查询/事务数-- 连接数活跃连接数量-- 慢查询比例慢查询占比-- 缓存命中率InnoDB缓冲池命中率2. 告警阈值设置慢查询数量 10个/分钟CPU使用率 80% 持续5分钟连接数 max_connections的80%磁盘空间使用率 85%总结MySQL SQL 调优是一个持续的过程需要系统化分析使用 EXPLAIN、慢查询日志等工具分层优化从SQL语句、索引、配置到架构监控反馈建立完善的监控体系预防为主在开发阶段就考虑性能问题记住黄金法则测量-分析-优化-验证通过数据驱动的方进行系统性调优。
MySQL 中如何进行 SQL 调优?
MySQL SQL 调优是一个系统性的工程涉及多个层面的优化。一、性能分析工具1. 查询分析工具-- 执行计划分析EXPLAINSELECT*FROMusersWHEREage25;EXPLAINFORMATJSONSELECT*FROMusersWHEREage25;EXPLAINANALYZESELECT*FROMusersWHEREage25;-- MySQL 8.0-- 性能模式监控SELECT*FROMperformance_schema.events_statements_summary_by_digestWHEREDIGEST_TEXTLIKE%users%;-- 慢查询日志分析SHOWVARIABLESLIKEslow_query_log%;SHOWVARIABLESLIKElong_query_time;2. 系统状态监控-- 查看当前连接和状态SHOWPROCESSLIST;SHOWSTATUSLIKEThreads_connected;SHOWENGINEINNODBSTATUS;-- 关键性能指标SHOWSTATUSLIKEInnodb_rows_read%;SHOWSTATUSLIKESelect_scan%;-- 全表扫描次数SHOWSTATUSLIKESort_merge_passes%;-- 排序合并次数二、索引优化策略1. 索引设计原则-- ✅ 选择合适的索引列-- 高选择性字段优先SELECTCOUNT(DISTINCTstatus)/COUNT(*)asselectivityFROMorders;-- 选择性低的字段不适合单独建索引-- ✅ 复合索引设计最左前缀原则CREATEINDEXidx_user_status_dateONorders(user_id,status,create_time);-- ❌ 避免过多索引影响写性能SELECTTABLE_NAME,INDEX_NAME,COUNT(*)FROMinformation_schema.STATISTICSGROUPBYTABLE_NAME,INDEX_NAME;2. 索引优化实战-- 案例优化范围查询-- 原始查询性能差EXPLAINSELECT*FROMordersWHEREcreate_timeBETWEEN2024-01-01AND2024-01-31ANDstatuscompleted;-- 优化方案调整索引顺序等值查询字段在前CREATEINDEXidx_status_timeONorders(status,create_time);-- 案例覆盖索引优化-- 原始需要回表EXPLAINSELECTuser_id,order_noFROMordersWHEREstatuspending;-- 优化使用覆盖索引CREATEINDEXidx_status_coveringONorders(status,user_id,order_no);-- Extra: Using index3. 索引失效场景及解决方案-- 1. 函数操作导致索引失效 ❌SELECT*FROMusersWHEREDATE(create_time)2024-01-01;-- 优化 ✅SELECT*FROMusersWHEREcreate_time2024-01-01ANDcreate_time2024-01-02;-- 2. 隐式类型转换 ❌SELECT*FROMusersWHEREphone13800138000;-- phone是varchar类型-- 优化 ✅SELECT*FROMusersWHEREphone13800138000;-- 3. 前导通配符 ❌SELECT*FROMusersWHEREnameLIKE%john%;-- 优化 ✅如果必须模糊查询SELECT*FROMusersWHEREnameLIKEjohn%;-- 使用后缀通配符三、SQL 语句优化1. 查询重写优化-- ❌ 低效写法SELECT*FROMordersWHEREorder_idIN(SELECTorder_idFROMorder_itemsWHEREproduct_id100);-- ✅ 优化为JOINSELECTo.*FROMorders oJOINorder_items oiONo.order_idoi.order_idWHEREoi.product_id100;-- ❌ 使用HAVING过滤性能差SELECTuser_id,COUNT(*)FROMordersGROUPBYuser_idHAVINGCOUNT(*)5;-- ✅ 使用子查询优化SELECTuser_id,order_countFROM(SELECTuser_id,COUNT(*)asorder_countFROMordersGROUPBYuser_id)tWHEREorder_count5;2. 分页查询优化-- ❌ 传统分页偏移量大时性能差SELECT*FROMordersORDERBYidLIMIT10000,20;-- ✅ 基于游标的分页推荐SELECT*FROMordersWHEREid10000ORDERBYidLIMIT20;-- ✅ 延迟关联大数据量分页SELECT*FROMorders oJOIN(SELECTidFROMordersORDERBYidLIMIT10000,20)tONo.idt.id;3. 大数据量查询优化-- ❌ 一次性查询大量数据SELECT*FROMuser_behavior_logWHEREcreate_date2024-01-01;-- ✅ 分批查询SELECT*FROMuser_behavior_logWHEREcreate_date2024-01-01ANDid0ORDERBYidLIMIT1000;-- ✅ 使用分区表CREATETABLEuser_behavior_log(idBIGINT,user_idINT,actionVARCHAR(50),create_dateDATE)PARTITIONBYRANGE(YEAR(create_date))(PARTITIONp2023VALUESLESS THAN(2024),PARTITIONp2024VALUESLESS THAN(2025));四、数据库架构优化1. 读写分离-- 主库写操作INSERTINTOorders(...)VALUES(...);UPDATEusersSET...WHERE...;-- 从库读操作SELECT*FROMordersWHERE...;-- 路由到从库2. 分库分表策略-- 按用户ID分表示例CREATETABLEorders_0000LIKEorders;CREATETABLEorders_0001LIKEorders;-- ... 创建1024张分表-- 路由逻辑table_suffix user_id % 10243. 缓存策略-- 查询缓存MySQL 8.0已移除可用Redis等-- 应用层缓存热点数据SELECTSQL_NO_CACHE*FROMproductsWHEREid1;-- 绕过缓存测试真实性能五、配置参数调优1. 内存相关配置# my.cnf 优化配置 [mysqld] # 缓冲池大小通常为物理内存的50-80% innodb_buffer_pool_size 16G # 日志缓冲区大小 innodb_log_buffer_size 64M # 排序缓冲区大小 sort_buffer_size 2M read_buffer_size 2M read_rnd_buffer_size 2M2. 连接相关配置# 最大连接数 max_connections 1000 # 连接超时 wait_timeout 600 interactive_timeout 600 # 临时表配置 tmp_table_size 64M max_heap_table_size 64M六、实战调优案例案例1电商订单查询优化问题场景-- 慢查询多条件组合查询SELECT*FROMordersWHEREuser_id100ANDstatusIN(pending,shipped)ANDcreate_timeBETWEEN2024-01-01AND2024-03-01ORDERBYcreate_timeDESCLIMIT20;优化步骤分析执行计划发现全表扫描Using filesort设计复合索引CREATEINDEXidx_user_status_timeONorders(user_id,status,create_time);优化查询语句-- 使用索引覆盖避免回表SELECTid,user_id,order_no,amount,create_timeFROMordersWHEREuser_id100ANDstatusIN(pending,shipped)ANDcreate_timeBETWEEN2024-01-01AND2024-03-01ORDERBYcreate_timeDESCLIMIT20;案例2报表统计查询优化问题场景-- 月度统计报表性能差SELECTDATE_FORMAT(create_time,%Y-%m)asmonth,COUNT(*)astotal_orders,SUM(amount)astotal_amountFROMordersWHEREcreate_time2023-01-01GROUPBYDATE_FORMAT(create_time,%Y-%m);优化方案预聚合统计-- 创建统计表定时更新CREATETABLEorder_monthly_stats(stat_dateDATEPRIMARYKEY,order_countINT,total_amountDECIMAL(10,2));使用物化视图或定时任务更新统计查询优化后的统计表七、自动化调优工具1. 使用 Percona Toolkit# 分析慢查询日志pt-query-digest slow.log# 分析索引使用情况pt-index-usage slow.log# 在线修改大表结构pt-online-schema-change2. MySQL Enterprise Monitor自动性能建议实时监控告警容量规划预测八、调优检查清单✅ 索引优化检查为高频查询条件添加索引复合索引字段顺序合理等值查询在前避免冗余索引定期分析索引使用情况✅ SQL 语句检查避免 SELECT *只查询需要的字段使用 JOIN 代替子查询优化分页查询避免大偏移量合理使用事务避免长事务✅ 数据库设计检查表结构规范化/反规范化平衡选择合适的数据类型考虑分区表策略读写分离架构✅ 配置参数检查缓冲池大小设置合理日志文件配置适当连接数配置满足业务需求临时表空间充足九、性能监控体系1. 建立监控指标-- 关键性能指标监控-- QPS/TPS每秒查询/事务数-- 连接数活跃连接数量-- 慢查询比例慢查询占比-- 缓存命中率InnoDB缓冲池命中率2. 告警阈值设置慢查询数量 10个/分钟CPU使用率 80% 持续5分钟连接数 max_connections的80%磁盘空间使用率 85%总结MySQL SQL 调优是一个持续的过程需要系统化分析使用 EXPLAIN、慢查询日志等工具分层优化从SQL语句、索引、配置到架构监控反馈建立完善的监控体系预防为主在开发阶段就考虑性能问题记住黄金法则测量-分析-优化-验证通过数据驱动的方进行系统性调优。