文章目录
一、基础内功篇:数据库核心机制
1.1 事务的四大护体真气(ACID)
事务就像数据库里的金钟罩(原理超重要!),必须满足:
- 原子性(Atomicity):要么全成功,要么全失败(比如转账时双方账户必须同步更新)
- 一致性(Consistency):数据状态永远合法(账户余额不能为负数)
- 隔离性(Isolation):多个事务并行时互不干扰(后面会讲MVCC这个神技)
- 持久性(Durability):提交后数据永久保存(掉电也不怕)
1.2 索引的独孤九剑
索引是查询加速的武林秘籍,但用不好反而伤身:
-- 创建组合索引的正确姿势
CREATE INDEX idx_user ON orders(user_id, create_time);
高频考点:
- 最左前缀原则:where user_id=1能用索引,单查create_time就用不了
- 索引失效的七种暗器:like通配符打头、隐式类型转换、函数操作等
- B+树结构:三层树就能存2000万数据(叶子节点双向链表超实用!)
二、进阶心法篇:性能优化实战
2.1 Explain执行计划解读
看懂这个输出等于掌握SQL体检报告:
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | user | NULL | ref | idx_age | idx_age | 4 | const | 100 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
关键指标解读:
- type列:ALL(全表扫描)→ index(索引扫描)→ range(范围扫描)→ ref(索引查找)
- rows列:预估扫描行数(超过1万就要警惕!)
- Extra列:Using filesort(需要额外排序)是大忌
2.2 慢查询优化三板斧
- 加索引:组合索引覆盖查询条件
- 改写法:用join代替子查询,避免select *
- 调参数:适当增加sort_buffer_size等配置
三、高并发场景下的绝世武功
3.1 MVCC多版本并发控制
InnoDB的读不阻塞写的独门绝技:
- 每个事务都有唯一的事务ID
- 通过undo log实现版本链
- 快照读(select)使用ReadView判断可见性
幻读解决方案对比:
方案 | 原理 | 性能影响 |
---|---|---|
串行化 | 完全隔离 | 差 |
Next-Key锁 | 间隙锁+记录锁 | 中等 |
乐观锁 | 版本号控制 | 好 |
3.2 分库分表的乾坤大挪移
当单表突破500万行时需要考虑:
// 分库分表路由算法示例
public String routeDB(String orderId) {
int hash = orderId.hashCode() & 0x7FFFFFFF;
return "order_db_" + (hash % 8);
}
拆分策略:
- 水平拆分:按时间/范围分表(适合日志类数据)
- 垂直拆分:把大字段分离到扩展表
- 一致性哈希:减少数据迁移影响
四、死锁攻防实战案例
4.1 经典死锁场景重现
两个事务互相等待:
事务A:
UPDATE account SET balance=balance-100 WHERE id=1;
UPDATE account SET balance=balance+100 WHERE id=2;
事务B:
UPDATE account SET balance=balance-200 WHERE id=2;
UPDATE account SET balance=balance+200 WHERE id=1;
解决方案(划重点):
- 统一操作顺序:都先操作id小的账户
- 设置锁超时时间:innodb_lock_wait_timeout=50
- 重试机制:捕获死锁异常后自动重试
4.2 如何阅读死锁日志
查看show engine innodb status输出的LATEST DETECTED DEADLOCK部分:
*** (1) TRANSACTION:
TRANSACTION 2312, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 7, OS thread handle 1401, query id 29 localhost root updating
UPDATE t SET name='a' WHERE id=1
*** (1) HOLDS THE LOCK(S): # 当前持有的锁
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: # 正在等待的锁
五、高频灵魂拷问TOP10
-
为什么用自增主键?
- 顺序写入减少页分裂
- 避免UUID随机写入导致的性能问题
- 但分布式场景可以用雪花算法ID
-
redo log和binlog区别?
- redo log是物理日志,保证崩溃恢复
- binlog是逻辑日志,用于主从复制
- 两阶段提交保证二者一致性
-
说下MySQL三大日志体系
- undo log:实现事务回滚和MVCC
- redo log:确保事务持久性
- binlog:主从复制和数据恢复
-
索引下推是什么黑科技?
- MySQL5.6推出的ICP优化
- 在存储引擎层过滤数据
- 减少回表次数提升性能
-
如何实现亿级数据秒级分页?
- 禁止使用offset/limit
- 使用where id>xxx limit 10
- 配合业务做游标分页
六、终极实战:电商系统优化案例
某电商平台遇到数据库CPU飙升至90%的紧急状况,通过以下步骤解决:
-
抓取现场数据
SHOW PROCESSLIST; -- 发现大量Sending data状态
-
分析慢日志
mysqldumpslow -t 10 /var/log/mysql-slow.log
-
优化核心SQL
-- 原语句(执行时间2.8s) SELECT * FROM orders WHERE status=1 AND create_time > '2024-01-01' ORDER BY amount DESC LIMIT 1000,10; -- 优化后(0.02s) SELECT id FROM orders WHERE status=1 AND create_time > '2024-01-01' ORDER BY amount DESC LIMIT 1000,10; -- 再通过IN查询获取完整数据
-
架构升级
- 引入Redis缓存热点商品信息
- 读写分离部署
- 将统计类查询迁移到ClickHouse
经过上述优化,数据库负载下降至30%,QPS从1500提升到8500!
七、面试加分秘籍
-
最新特性展示
- MySQL8.0的窗口函数
- 直方图统计信息
- 原子DDL操作
-
扩展技术栈
- 了解TiDB分布式数据库
- 熟悉Percona Toolkit工具集
- 掌握pt-query-digest分析技巧
-
场景设计能力
- 如何设计朋友圈数据库?
- 怎么实现分布式ID生成?
- 分库分表后怎么做全局查询?
记住:面试官最想听到的是你的思考过程!遇到不会的问题可以说:“这个问题我之前没有深入研究过,但根据我的理解应该是…您看这样理解对吗?”(超级管用!)