一、日志系统
1. redo log(重做日志)
-
属于InnoDB 引擎层****。
-
作用:保证事务持久性,崩溃恢复
-
机制:**Write Ahead Log(WAL)**预写日志,先写redo log再异步写磁盘,提升性能
-
结构:固定大小,循环写,不会无限膨胀
-
总结:宕机重启靠 redo log 恢复数据,保证提交了就不丢**。**
2. undo log(回滚日志)
-
属于InnoDB 引擎层,
-
作用:保证事务 原子性 ,回滚使用;实现 MVCC 多版本并发控制
-
总结:回滚靠 undo log,MVCC 也靠 undo log。
3. binlog(归档日志)
-
属于server层,与引擎无关,追加写,文件会不断生成 。
-
作用:主从同步;数据基于时间点恢复
-
与 redo log 区别:redo 是 InnoDB 独有,binlog 是 Server 层,逻辑日志
4. 两阶段提交 2PC
保证 redo log 和 binlog 数据一致,是主从同步 + 数据恢复的基础
- ① 写 redo log,状态 prepare
- ② 写 binlog
- ③ 提交 redo log,状态 commit
二、高可用架构
核心目标:故障自动切换;数据不丢或少丢;业务不间断;读写分离、水平扩展
1、主从复制(高可用基石)
(1) 复制原理
-
① 主库写入,生成 binlog
-
② 从库通过 IO 线程拉取 binlog,写入 relay log
-
③ 从库 SQL 线程重放 relay log,保持数据一致
(2) 3种复制模式
-
异步复制:主库不关心从库是否同步完,性能最好,但可能丢数据
-
半同步复制:至少一个从库 ACK 后才提交,数据更安全,性能略有下降
-
组复制(MGR):分布式一致性协议,强一致,高可用
(3) 主从延迟问题
-
原因:大事务、从库压力大、网络延迟
-
解决:
-
并行复制
-
减少大事务:大事务拆分为小事务、禁止在事务里调用外部接口、批量操作分批提交
-
优化SQL,减少从库压力
-
降低主库写入压力:读写分离、热点数据分离、异步化、削峰填谷
-
2、主流高可用方案
2.1. MHA(Master High Availability)
最经典、最常用
- 架构:1 主 + N 从 + MGR 管理节点
- 优点:成熟稳定、切换快、对侵入小
- 缺点:
- 需手动 / 脚本部署
- 无法自动补主,切完后需要手动加从
- 依赖 VIP 漂移
2.2. MGR(MySQL Group Replication)
官方推荐、未来趋势、强一致
- 单主模式 / 多主模式
- 基于 Paxos 分布式一致性协议
- 自动故障检测、自动选主、自动恢复
- 优点:强一致、高可用、无需额外组件
- 缺点:对网络要求高、性能一般、表必须有主键
2.3. InnoDB Cluster + MySQL Router
- 基于 MGR 封装
- 管理更简单
- Router 做读写分离、负载均衡、故障透明
2.4. 主从 + Keepalived(VIP)
- 简单粗暴
- 主库挂了,VIP 飘到从库
- 优点:简单
- 缺点:切换不智能、可能脑裂、数据一致性无保障
3、读写分离
- 写:主库
- 读:从库
- 实现方式:
- 应用内部分离
- Sharding-JDBC
- MySQL Router
- MyCat
三、分库分表
分库分表作用:水平扩容,把压力分散到多个库、多张表,提高并发和容量。解决单表数据过大(千万~亿级)导致的:
- 查询慢、索引大、IO 高
- 写入性能瓶颈
- 备份、DDL 极其困难
1、拆分方案
-
垂直拆分
-
按业务拆分,如用户库、订单库、商品库
-
优点:简单清晰,隔离业务
-
缺点:不能解决单表过大问题
-
-
水平拆分
-
把同一张表的数据按规则切到多张表 / 多个库,例如:order 拆成 order_0 ~ order_15
-
优点:真正解决单表容量、并发瓶颈
-
缺点:复杂度高,需要中间件支持
-
2、常见分片策略
-
范围分片
- 按 ID 范围、时间范围,如:1~1000w 放表 1,1000w~2000w 放表 2
- 优点:扩容方便
- 缺点:容易出现数据倾斜、热点库
-
哈希分片(最常用)
-
shard_idx = hash(user_id) % 分片数 -
优点:数据分布均匀
-
缺点:扩分片需要rehash 迁移数据
-
-
一致性哈希
- 解决扩缩容大量迁移问题
- 一般中间件内置,业务不用自己写
-
按业务字段分片
-
订单按 user_id,日志按日期
-
保证同用户数据在同分片,方便查询
-
3、分库分表带来的问题
- 跨分片分页、排序、count 复杂
- 跨库事务难保证(分布式事务)
- join 困难,不能随意 join 不同分片表
- 分布式 ID,不能用本地自增主键
- 扩容、数据迁移成本高
- 运维复杂度提升,排错难
4、分布式 ID 方案
分表后不能用 MySQL auto_increment,常用:
- 雪花算法(Snowflake)
- 号段模式(Segment)
- Redis 自增
- UUID(不推荐,无序导致索引性能差)
5、分布式事务
- 最大努力交付
- TCC
- SAGA
- 本地消息表
- 2PC/3PC(性能差,少用)
实际业务尽量避免分布式事务:
- 按同字段分片,让相关数据在同库
- 使用最终一致性
6、分库分表常用中间件
-
客户端模式(Java 生态主流):**Sharding-JDBC(ShardingSphere)**轻量、无部署、性能高
-
服务端代理模式:MyCat、Sharding-Proxy、DBLE
四、SQL优化
1. 定位慢SQL
- ① 开启 慢查询日志
- ② 使用
show processlist看耗时 SQL - ③ 使用 profiling 查看执行耗时
- ④ 生产用监控:Prometheus、Grafana、SkyWalking
2. 优化步骤
- ① 用
explain查看执行计划 - ② 看是否走索引、是否回表、是否文件排序
- ③ 优化索引、优化 SQL、分库分表
3. 查看执行计划
重点字段
- type:优到最差
system > const > eq_ref > ref > range > index > ALL,最好到range以上,ref、eq_ref、const最优 - key:实际使用的索引
- rows:扫描行数越少越好
- Extra
Using index:覆盖索引,极好Using where:正常Using filesort:文件排序,需优化Using temporary:用到临时表,需优化
4. 索引优化
- 为 where、order by、group by、join 字段建索引
- 优先建 联合索引 ,遵循最左匹配
- 联合索引,等值放前,范围放后
- 使用 覆盖索引,避免回表
- 禁止在低基数列建索引(性别、状态)
- 禁止无序主键(UUID),避免页分裂
- 单表索引控制在 3~5 个
5. SQL语句优化
-
禁止
select *:只查需要的字段,更容易命中覆盖索引。 -
避免索引失效:
- 不在索引列做运算、函数、类型转换
- 禁止
like '%xxx'前置模糊 - 避免
or一侧无索引 - 避免
!=、is null、not in导致索引失效
-
优化分页:大分页
limit 100000,20极慢,用主键过滤 :where id>100000 limit 10 -
优化
in:值太多用in会导致索引失效或全表扫描拆分批、或改用join -
避免
order by导致 filesort:排序字段建索引,或使用覆盖索引 -
避免
group by产生临时表:分组字段建索引 -
JOIN优化:
-
小表驱动大表
; -
关联字段必须建索引
; -
尽量不超过3张表JOIN
; -
禁止JOIN大结果集
-
-
子查询优化:
-
尽量将
in (子查询)改为join; -
避免相关子查询(循环执行)
-
-
大表优化:
-
读写分离;
-
分库分表(sharding);
-
冷热数据分离;
加缓存(Redis);
-
避免大事务、批量操作分批执行
-
6. 总结
SQL 优化主要从索引、SQL 语句、业务设计、数据库架构四个层面入手:
- 通过 explain 分析执行计划,确保命中索引;
- 建立合理的联合索引、覆盖索引,避免索引失效;
- 优化 SQL 写法,禁止
select *,避免函数运算、前置模糊、无效in; - 优化分页、排序、分组,减少
filesort和临时表; - 大表采用分库分表、读写分离、缓存等架构优化;
- 控制事务大小,避免锁等待与主从延迟。
五、常见问题
- MySQL 为什么用 B + 树?
B+ 树矮胖 ,IO 次数少;叶子节点双向链表,范围查询 / 排序极快
- varchar (50) 和 varchar (200) 区别?
存储空间一样,但内存临时表大小不同,尽量用合适长度
- 大表怎么加索引不锁表?
大表不能直接执行普通 alter,会锁表导致业务不可用。
正确方案:
① MySQL 5.6 及以上使用在线 DDL ,指定 ALGORITHM=INPLACE, LOCK=NONE 实现无锁加索引;
② 超大表使用第三方工具 pt-osc 或 gh-ost,通过临时表 + 增量同步方式平滑变更;
③ 操作选择低峰期进行,并关注主从延迟与数据库负载
- 哪些情况会导致索引失效?
①索引列运算、函数、隐式转换
② like '%xx' 前置模糊
③ or 一侧无索引
④ != / not in / is null 优化器放弃索引
- 事务 ACID 分别靠什么保证?
原子性:undo log
一致性:约束 + 事务整体
隔离性:MVCC + 锁
持久性:redo log
- RC 和 RR 的本质区别是什么?
Read View 生成时机不同:RC每次查询生成新的,RR事务开始时生成一次
- MySQL 性能瓶颈怎么排查?
慢查询 → explain → 索引优化 → 分库分表 → 硬件升级
- 主键用自增 id 还是 uuid?
自增 id:连续、索引性能高;uuid:无序、页分裂、性能差