一、事务的ACID特性到底是啥??
这个问题绝对稳居面试问题排行榜TOP1!!!(重要程度五颗星)很多同学背得滚瓜烂熟,但一被追问就露馅。咱们先看这个经典场景:
-- 转账操作示例
START TRANSACTION;
UPDATE account SET balance = balance - 500 WHERE user_id = 'A';
UPDATE account SET balance = balance + 500 WHERE user_id = 'B';
COMMIT;
**原子性(Atomicity)**就像打包快递(这个比喻超形象!),要么整个包裹成功送达,要么原封不动退回。数据库通过undo log实现回滚能力。
**一致性(Consistency)**是终极BOSS(重要但容易被忽略),它确保数据从一个合法状态转换到另一个合法状态。比如转账前后总额不变,这个要靠应用层和数据库双重保障。
**隔离性(Isolation)**的坑最多!!上次我们项目就遇到个幻读问题——上午查库存10件,下单时突然变成5件,最后发现是其他事务插入了负库存记录(血泪教训啊)。
**持久性(Durability)**靠的是redo log这个神器。突然断电时,重启后数据库会通过redo log把没刷盘的数据重新写入。
二、索引怎么用才能起飞??
先看这个死亡案例(新手必踩坑):
CREATE INDEX idx_name ON user(name);
SELECT * FROM user WHERE UPPER(name) = 'JOHN'; -- 索引失效!
B+树索引就像字典目录(这个类比绝了),但以下情况会翻车:
- 列参与计算(age+1=20)
- 使用函数(UPPER(name))
- 模糊查询(LIKE ‘%abc’)
- 类型转换(字符串转数字)
复合索引的最左前缀原则要牢记!比如索引(a,b,c):
- ✅能用的:a=1 / a=1 AND b=2
- ❌没用的:b=2 / c=3 / b=2 AND c=3
有个优化秘诀(一般人我不告诉):用覆盖索引避免回表。比如:
-- 普通查询
SELECT * FROM orders WHERE user_id = 100; -- 需要回表
-- 优化版
SELECT order_id FROM orders WHERE user_id = 100; -- 使用索引覆盖
三、慢查询怎么破??
先记住这个排查口诀(亲测有效):
- 开慢查询日志(slow_query_log)
- 用EXPLAIN看执行计划
- 重点关注type列(最好到ref级别)
- 看Extra列有没有Using filesort
最近优化过的一个案例:
-- 优化前(执行时间2.3秒)
SELECT * FROM logs
WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31'
ORDER BY user_id DESC
LIMIT 1000;
-- 优化后(0.2秒!)
ALTER TABLE logs ADD INDEX idx_time_user (create_time, user_id);
SELECT create_time, user_id, action FROM logs
WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31'
ORDER BY user_id DESC
LIMIT 1000;
关键点:
- 避免SELECT *
- 利用索引排序
- 控制返回字段
- 合理使用分页(大数据量用WHERE id > xxx代替LIMIT)
四、锁机制有多重要??
去年我们系统遇到个诡异的bug:用户充值偶尔会重复到账。最后发现是这么写的:
BEGIN;
SELECT balance FROM account WHERE user_id=123 FOR UPDATE;
-- 这里有个外部API调用,耗时2秒!
UPDATE account SET balance = balance + 100 WHERE user_id=123;
COMMIT;
问题分析:
- 长时间持有行锁会导致并发骤降
- 外部调用应该放在事务之外
- 改用乐观锁(版本号控制)更合适
不同隔离级别的锁表现:
- 读未提交:基本没锁
- 读已提交:写锁保持到事务结束
- 可重复读:间隙锁防止幻读
- 串行化:直接加表锁
五、三大日志怎么选??
binlog(归档日志):
- 主从复制的核心
- 支持statement/row/mixed格式
- 数据恢复的最后防线
redo log(重做日志):
- 保证持久性
- 采用循环写入方式
- crash recovery时重放
undo log(回滚日志):
- 实现事务回滚
- 支持MVCC多版本控制
- 会被定期清理
有个冷知识(面试加分项):刷盘策略怎么选?
- sync_binlog=0:靠OS自己刷
- sync_binlog=1:每次commit都刷(最安全但性能差)
- sync_binlog=N:累积N次刷一次
六、总结与建议
MySQL就像乐高积木(这个比喻绝了),每个模块都要严丝合缝。建议大家:
- 用docker起个测试环境,随便搞破坏
- 多关注官方文档的版本变化(比如MySQL8.0的窗口函数)
- 实操时打开general_log看完整执行过程
- 定期用pt-query-digest分析慢日志
最后送大家一句话:读十遍文档不如亲手搞崩一次数据库(真的!故障是最好的老师)。遇到问题别慌,先explain再优化,记住索引不是万能的,但没有索引是万万不能的!