前言

        继Java基础、进阶、Spring全家桶之后,数据库是后端开发者必须攻克的另一座高峰。MySQL作为最流行的开源关系型数据库,其性能优化和高可用架构直接决定了系统的稳定性和响应速度。本文将从索引优化SQL调优执行计划分析慢查询日志数据库锁机制分库分表,到主从复制读写分离高可用方案(MHA、MGR、Orchestrator),带你全面掌握MySQL核心技能。


一、MySQL架构与存储引擎

1. 逻辑架构分层

text

客户端连接
    ↓
连接层(连接池、认证、线程管理)
    ↓
服务层(SQL解析器、查询缓存、优化器、执行计划)
    ↓
存储引擎层(InnoDB、MyISAM、Memory等)
    ↓
文件系统(数据文件、日志文件)

2. InnoDB vs MyISAM(5.7版本对比)

特性 InnoDB MyISAM
事务 支持 不支持
外键 支持 不支持
行级锁 支持 仅表锁
MVCC 支持 不支持
全文索引 支持(5.6+) 支持
聚簇索引
崩溃恢复

📌 结论:生产环境99%使用InnoDB,只有只读表或数据仓库可考虑MyISAM。


二、索引优化

1. 索引类型

  • B+Tree索引(默认):适合等值、范围查询、排序。

  • Hash索引(Memory引擎):等值查询快,不支持范围。

  • 全文索引:文本搜索(MATCH AGAINST)。

  • 空间索引:地理数据。

2. 索引设计原则

  • 区分度高的列:如主键、唯一ID。

  • 最左前缀原则:复合索引(a,b,c),查询条件必须包含a才能使用索引。

  • 避免索引失效

    • 对索引列使用函数、运算:WHERE DATE(create_time) = '2024-01-01' → 改用create_time BETWEEN ...

    • 隐式类型转换:WHERE phone = 13800001111(phone是varchar)

    • LIKE '%keyword'(前导模糊)

    • OR条件中如果一侧无索引则全表扫描

  • 覆盖索引:索引包含查询所需的所有列,避免回表。

3. 索引优化实战

-- 不良SQL
SELECT * FROM orders WHERE YEAR(create_time) = 2024;

-- 优化后
SELECT * FROM orders WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01';

-- 复合索引: (status, create_time)
SELECT * FROM orders WHERE status = 1 AND create_time > '2024-06-01';

4. 索引监控

-- 查看未使用的索引
SELECT * FROM sys.schema_unused_indexes;

-- 查看重复/冗余索引
SELECT * FROM sys.schema_redundant_indexes;

三、SQL执行计划(EXPLAIN)

关键字段解读

列名 含义 关键值
id 查询序列号 id相同执行顺序从上到下,不同从大到小
select_type 查询类型 SIMPLE(简单)、PRIMARY(最外层)、SUBQUERY
table 表名
type 访问类型(性能从好到差) system > const > eq_ref > ref > range > index > ALL
possible_keys 可能用到的索引
key 实际使用的索引
key_len 索引使用长度 越短越好
rows 预估扫描行数 越小越好
Extra 额外信息 Using index(覆盖索引)、Using where、Using filesort(需优化)、Using temporary(需优化)

示例分析

EXPLAIN SELECT u.name, o.amount 
FROM users u 
JOIN orders o ON u.id = o.user_id 
WHERE u.status = 1 AND o.create_time > '2024-01-01';
  • 如果type出现ALLindex,说明缺少索引。

  • Extra出现Using filesort说明需要排序优化(可能排序字段无索引)。


四、慢查询日志与分析

1. 开启慢查询

-- 查看状态
SHOW VARIABLES LIKE 'slow_query_log%';
-- 开启并设置阈值(2秒)
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 2;
SET GLOBAL log_queries_not_using_indexes = ON;  -- 记录未走索引的SQL

2. 分析工具:pt-query-digest

bash

pt-query-digest /var/lib/mysql/slow.log > slow_report.txt

报告会按查询耗时、扫描行数、执行次数排序,找出最需要优化的SQL。

3. 常见优化策略

  • 分页优化LIMIT 100000, 10 改为 WHERE id > last_id LIMIT 10(使用游标)

  • 批量操作:使用INSERT ... VALUES多行,减少交互。

  • 避免SELECT *:只取需要的列,尤其TEXT/BLOB字段。


五、锁机制与事务隔离级别

1. InnoDB锁类型

  • 共享锁(S锁)SELECT ... LOCK IN SHARE MODE

  • 排他锁(X锁)SELECT ... FOR UPDATE

  • 意向锁:表级别锁,用于快速判断是否有行锁。

  • 间隙锁(Gap Lock):锁定范围(RR隔离级别下防止幻读)。

  • 临键锁(Next-Key Lock):行锁+间隙锁。

2. 事务隔离级别

级别 脏读 不可重复读 幻读
READ UNCOMMITTED 可能 可能 可能
READ COMMITTED (RC) 不可能 可能 可能
REPEATABLE READ (RR) 不可能 不可能 可能(InnoDB通过MVCC+间隙锁解决)
SERIALIZABLE 不可能 不可能 不可能

MySQL默认REPEATABLE READ

3. 死锁排查与解决

-- 查看当前事务
SELECT * FROM information_schema.INNODB_TRX;
-- 查看锁等待
SELECT * FROM performance_schema.data_locks;
-- 查看死锁日志(MySQL会自动记录)
SHOW ENGINE INNODB STATUS;

解决方法

  • 统一访问顺序(例如按主键升序加锁)。

  • 使用LOCK TABLES或乐观锁(版本号机制)。

  • 缩短事务时间,减少锁持有。


六、分库分表实战

当单表数据量超过500万~1000万或单库压力过大时,需要考虑分库分表。

1. 垂直拆分 vs 水平拆分

  • 垂直分表:将宽表拆为多张(如商品主表+详情表)。

  • 垂直分库:按业务拆分(订单库、用户库)。

  • 水平分表:同一张表的数据分散到多张结构相同的表(如订单按用户ID hash)。

  • 水平分库:数据分布到多个数据库实例。

2. 分片键选择

  • 选择查询频率高的字段(如用户ID、订单ID)。

  • 避免跨分片查询(尽量将关联数据路由到同一分片)。

3. 分库分表中间件

中间件 说明
ShardingSphere-JDBC 轻量级,适合Java应用,支持分库分表、读写分离
MyCat 独立代理,对应用透明
Vitess YouTube开源,适合大规模

4. ShardingSphere-JDBC 示例

yml

spring:
  shardingsphere:
    datasource:
      names: ds0,ds1
      ds0: url: jdbc:mysql://localhost:3306/db0
      ds1: url: jdbc:mysql://localhost:3306/db1
    sharding:
      tables:
        t_order:
          actual-data-nodes: ds$->{0..1}.t_order_$->{0..1}
          table-strategy:
            standard:
              sharding-column: order_id
              sharding-algorithm-name: order-inline
          key-generator:
            column: order_id
            type: SNOWFLAKE
      sharding-algorithms:
        order-inline:
          type: INLINE
          props:
            algorithm-expression: t_order_$->{order_id % 2}

5. 全局ID生成方案

  • 雪花算法(Snowflake):64位,时间戳+机器ID+序列号,推荐。

  • 数据库分段号:批量获取ID区间(如Leaf)。

  • Redis incr:简单但有持久化风险。


七、主从复制与读写分离

1. 主从复制原理(异步/半同步)

  1. 主库提交事务后写入binlog

  2. 从库IO线程读取binlog并写入relay log

  3. 从库SQL线程执行relay log中的SQL。

2. 搭建主从(基于GTID简化)

-- 主库配置 my.cnf
server-id=1
log_bin=mysql-bin
gtid_mode=ON
enforce_gtid_consistency=ON

-- 从库配置
server-id=2
gtid_mode=ON
enforce_gtid_consistency=ON

-- 从库执行
CHANGE MASTER TO MASTER_HOST='master_ip', MASTER_PORT=3306, MASTER_USER='repl', MASTER_PASSWORD='password', MASTER_AUTO_POSITION=1;
START SLAVE;
SHOW SLAVE STATUS\G;

3. 读写分离

应用层实现:使用ShardingSphere-JDBC或Spring AbstractRoutingDataSource。

@Configuration
public class RoutingDataSourceConfig {
    @Bean
    public DataSource routingDataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("master", masterDataSource());
        targetDataSources.put("slave", slaveDataSource());
        RoutingDataSource routing = new RoutingDataSource();
        routing.setDefaultTargetDataSource(masterDataSource());
        routing.setTargetDataSources(targetDataSources);
        return routing;
    }
}

结合@Transactional(readOnly = true)自动路由到从库。


八、高可用架构方案

1. MHA(Master High Availability)

  • 监控主库,故障时自动选举新主,并修复其他从库。

  • 缺点:需要额外管理节点,MySQL 5.7后逐步被替代。

2. MySQL Group Replication(MGR)

  • 内置原生高可用,基于Paxos协议,支持多主/单主模式。

  • 要求:MySQL 5.7.17+,所有节点配置一致。

3. Orchestrator + Raft

  • 开源工具,支持故障检测、自动切换、拓扑可视化。

  • 常与ProxySQL配合实现应用层透明切换。

4. 基于Kubernetes Operator(如RadonDB)

  • 容器化部署MySQL,利用K8s的StatefulSet、PV、服务发现,实现自动恢复和滚动升级。

5. 备份与恢复

  • 逻辑备份mysqldump(适合小库)

  • 物理备份XtraBackup(支持热备,速度快)

  • 备份策略:全量(每周)+ binlog增量(每天)

  • 恢复演练:定期从备份恢复数据到测试环境验证。


九、性能压测与监控

1. 压测工具

  • sysbench:最常用,支持OLTP测试。

sysbench oltp_read_write --mysql-db=test --mysql-user=root --mysql-password=123 --table-size=1000000 --threads=16 --time=300 run
  • mysqlslap:MySQL自带,简单压测。

2. 监控指标

  • QPS/TPSSHOW GLOBAL STATUS LIKE 'Queries'

  • 慢查询数量SHOW GLOBAL STATUS LIKE 'Slow_queries'

  • 连接数Threads_connected vs max_connections

  • InnoDB缓冲池命中率(Innodb_buffer_pool_read_requests - Innodb_buffer_pool_reads) / Innodb_buffer_pool_read_requests

  • 锁等待Innodb_row_lock_waits

3. 常用监控系统

  • Prometheus + Grafana + mysqld_exporter

  • Percona Monitoring and Management (PMM):专为MySQL优化的一体化监控。


十、经典优化案例

案例1:大量ORDER BY RAND()导致性能崩溃

-- 原SQL
SELECT * FROM product ORDER BY RAND() LIMIT 10;

优化:使用子查询 + 主键范围随机

SELECT * FROM product WHERE id >= (SELECT FLOOR(MAX(id) * RAND()) FROM product) LIMIT 10;

案例2:分页越往后越慢

-- 原SQL(offset 1000000)
SELECT * FROM logs ORDER BY id LIMIT 1000000, 10;

优化:记录上一页最大id

SELECT * FROM logs WHERE id > last_id ORDER BY id LIMIT 10;

案例3:IN子句元素过多

SELECT * FROM user WHERE id IN (1,2,3,...,10000);

优化:使用临时表或分批查询,避免一次发送大量数据。


结语:成为数据库专家

MySQL优化不是一蹴而就的,需要理论结合实践,通过不断分析和调整来提升性能。建议读者:

  1. 建立基线:压测获得系统QPS/TPS上限。

  2. 监控告警:慢查询、锁等待、复制延迟达到阈值即告警。

  3. 定期review:使用pt-query-digest每周分析慢日志。

  4. 演练故障:主从切换、备份恢复至少每季度一次。

下期预告:Redis深度实战:高性能缓存架构与分布式挑战。
如果觉得本文对你有帮助,欢迎点赞、收藏、转发,你的支持是我持续创作的动力!

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐