MySQL
1. 事务的ACID特性是什么?如何保证的?
- 原子性 (Atomicity):事务是最小执行单元,全部成功或全部失败。通过
Undo Log实现回滚。 - 一致性 (Consistency):事务前后数据库保持合法状态。由应用层和数据库约束共同保证。
- 隔离性 (Isolation):事务间操作相互隔离。通过锁和
MVCC机制实现。 - 持久性 (Durability):事务提交后数据永久保存。通过
Redo Log和刷盘策略保证。
2. MySQL的隔离级别有哪些?分别解决什么问题?
- 读未提交 (RU):可能脏读、不可重复读、幻读。
- 读已提交 (RC):解决脏读,但可能出现不可重复读和幻读(默认隔离级别)。
- 可重复读 (RR):解决脏读、不可重复读(MySQL默认级别,通过
MVCC+Next-Key Locks避免幻读)。 - 串行化 (Serializable):完全隔离,性能最低。
3. 什么是MVCC?如何实现的?
- MVCC (多版本并发控制):通过快照读实现非阻塞读,解决读写冲突。
- 实现机制:
- 每行数据包含隐藏字段
DB_TRX_ID(事务ID)和DB_ROLL_PTR(回滚指针)。 - 通过
Undo Log构建版本链,结合ReadView判断数据可见性。 - 只在
RC和RR隔离级别下生效。
4. InnoDB的索引结构是什么?B+树相比B树的优势?
- B+树索引:非叶子节点只存键值,叶子节点存储数据(聚集索引存整行,二级索引存主键)。
- 优势:
- 叶子节点形成有序链表,范围查询高效。
- 非叶子节点存储更多键,树高更低,减少IO次数。
- 数据只存在叶子节点,查询稳定性更高。
5. 死锁是如何产生的?如何检测和避免?
- 产生条件:互斥、请求与保持、不可剥夺、循环等待。
- 检测:通过
等待图 (Wait-for Graph)检测循环依赖,触发死锁回滚。 - 避免方案:
- 事务按固定顺序访问资源。
- 降低事务粒度,减少锁竞争。
- 设置合理的事务超时时间(
innodb_lock_wait_timeout)。 - 使用
SELECT ... FOR UPDATE NOWAIT(MySQL 8.0+)。
6. 如何优化慢查询?
- 定位慢SQL:开启
slow_query_log,分析慢日志。 - Explain分析:关注
type(扫描方式)、key(使用索引)、rows(扫描行数)、Extra(是否Using filesort/Using temporary)。 - 优化方向:
- 添加缺失索引,避免全表扫描。
- 优化SQL写法(如拆分复杂查询、避免
SELECT *)。 - 使用覆盖索引,减少回表操作。
- 调整表结构(如范式化/反范式化)。
7. 主从复制原理是什么?延迟如何解决?
- 原理:主库通过
Binlog记录变更,从库通过I/O线程拉取日志,SQL线程重放。 - 延迟解决:
- 并行复制(
MTS,基于库/组提交/事务粒度)。 - 调整
sync_binlog和innodb_flush_log_at_trx_commit参数平衡安全与性能。 - 使用半同步复制(
semisync)确保主从数据一致性。
8. 分库分表的常见方案及优缺点?
- 垂直拆分:按业务拆分(如订单库、用户库)。
- 优点:解耦业务,降低单库压力。
- 缺点:无法解决单表数据量过大的问题。
- 水平拆分:按规则分散数据(如按ID取模、按时间范围)。
- 优点:解决单表数据膨胀问题。
- 缺点:跨分片查询复杂,事务管理困难。
- 代理层 vs 客户端分片:ShardingSphere、MyCAT等工具实现。
9. Explain中的Using index condition和Using index有何区别?
- Using index:仅用索引即可返回所需数据(覆盖索引)。
- Using index condition:使用索引条件下推(ICP),在存储引擎层过滤数据,减少回表次数。
10. 什么是覆盖索引?举例说明。
- 定义:查询的字段全部被索引覆盖,无需回表。
- 示例:
-- 创建索引 (age, name)
CREATE INDEX idx_age_name ON user(age, name);
-- 查询命中覆盖索引
SELECT age, name FROM user WHERE age \> 20;
聚集索引 vs 非聚集索引详解
1. 核心概念
聚集索引 (Clustered Index)
- 数据即索引:索引的叶子节点直接存储完整行数据
- 物理有序:表中的数据行按聚集索引的键值顺序存储(类似电话簿按姓名排序)
- 唯一性:一张表有且仅有一个聚集索引(InnoDB强制要求)
非聚集索引 (Non-Clustered Index)
- 索引与数据分离:叶子节点存储主键值(InnoDB中)或行指针(MyISAM中)
- 逻辑有序:索引本身有序,但数据行物理存储无序
- 可多个共存:一张表可以创建多个非聚集索引(二级索引)
2. 存储结构对比
InnoDB引擎实现
| 特征 | 聚集索引 | 非聚集索引 |
|---|---|---|
| 叶子节点内容 | 存储完整数据行 | 存储主键值 |
| B+树结构 | 主键构建的B+树即数据文件 | 独立B+树结构 |
| 查找流程 | 直接返回数据 | 先查主键,再通过主键查聚集索引(回表) |
示例结构图
3. 关键差异分析
| 对比维度 | 聚集索引 | 非聚集索引 |
|---|---|---|
| 数据存储方式 | 数据按索引顺序物理存储 | 索引与数据物理分离 |
| 查询速度 | 主键查询极快(直接定位数据) | 需要二次查找(回表) |
| 插入效率 | 顺序插入高效,随机插入可能产生页分裂 | 插入只需更新索引树 |
| 空间占用 | 无额外存储空间 | 需要额外存储索引结构 |
| 更新代价 | 主键修改代价高(需移动数据行) | 只需更新索引树 |
4. 典型场景示例
场景1:主键查询
SELECT * FROM users WHERE id = 5;
执行流程: 聚集索引:直接通过B+树定位到数据行 非聚集索引:不适用(主键本身就是聚集索引)
场景2:二级索引查询
-- 假设在email字段上创建了非聚集索引
SELECT * FROM users WHERE email = 'user@example.com';
执行流程: 在email索引树中找到对应记录,获取主键id 用主键id到聚集索引树中查找完整数据(回表)
- 常见误区 误区1:"主键一定是聚集索引" 事实:在InnoDB中主键自动成为聚集索引,但若未定义主键: 选择第一个UNIQUE且非空的字段 否则隐式创建6字节的DB_ROW_ID作为聚集索引 误区2:"非聚集索引查询性能差" 优化方案: 通过覆盖索引避免回表 使用索引条件下推(ICP,Index Condition Pushdown)