第一板块:SQL 调优与索引底层(日常开发必考)
1:怎么解决 MySQL 的深分页(超大分页)问题?
“超大分页通常发生在数据量极大的情况下,如果直接使用 LIMIT 1000000, 10 并且带排序,MySQL 会查出前一百万零十条数据,然后丢弃前面的,效率极低。
我们的解决方案是:子查询 + 覆盖索引(延迟关联)。
首先,通过覆盖索引快速查出目标分页的 ID 列表(因为只查 ID,不查整行,所以不用回表,速度极快);然后再用这些 ID 作为条件,通过子查询或 JOIN 回表去查出真正需要的数据行。这样极大地减少了回表的次数,提升了查询效率。”
2:索引的创建原则有哪些?
[满分回答]:
“我们在建索引时主要遵循以下几个原则:
- 看数据量:表数据量超过 10 万以上才考虑建索引,太少没必要。
- 看高频词:选择查询条件(WHERE)、排序(ORDER BY)、分组(GROUP BY)最频繁的字段。
- 白嫖‘覆盖索引’:尽量使用联合(复合)索引,把 SELECT 需要的字段全包进去,避免回表。
- 看区分度:区分度高的字段放联合索引前面,区分度不高的(比如性别)放后面。
- 前缀索引:对于内容极长的文本字段,只给前面的几十个字符建前缀索引,省空间。
- 克制:严格控制单表索引数量,因为索引本质是 B+ 树,建多了会严重拖慢 INSERT 和 UPDATE 的速度。”
3:什么情况下索引会失效?
[满分回答]:
“常见的踩坑场景有这四个:
- 没守规矩:没有遵循联合索引的‘最左匹配原则’。
- 开头模糊:使用了
LIKE模糊查询,且%放在了最前面(比如LIKE '%abc',这就没法走字典序了)。 - 字段加工:在索引字段上进行了数学运算,或者隐式的类型转换(比如把字符串当数字查)。
- 范围截断:在联合索引中,如果中间某个字段使用了范围查询(比如
>,<),会导致它右边所有字段的索引全部失效。”
4:总结一下你们团队做 MySQL 性能优化的核心思路?(为你补充的问题)
[满分回答]:
“我们的调优口诀分五步:
- 省空间:精选字段类型,能用
TINYINT不用INT,让 B+ 树更胖、层级更低。 - 做目录:精准建立索引,利用 B+ 树的双向链表空降定位。
- 少折腾:坚决不写
SELECT *避免回表;用UNION ALL替代UNION拒绝无谓的内部排序。 - 搞分工:引入读写分离,主库负责写,从库负责读。
- 拆大表:当单表过载(超过千万级)时,进行分库分表,把高耸的 B+ 树强行压矮。”
第二板块:事务、锁与底层日志(内功深度必考)
5:事务的特性(ACID)是什么?可以详细说一下吗?
[满分回答]:
“事务的特性是 ACID,以 A 向 B 转账 500 元为例:
- 原子性 (Atomicity):这 500 块钱的扣除和增加,要么一起成功,要么一起失败,绝不拖泥带水。
- 一致性 (Consistency):转账前后,总金额数据要守恒。A 扣了 500,B 必须刚好增加 500。
- 隔离性 (Isolation):A 转账给 B 的过程中,不受 C 给 A 转账等其他并发事务的干扰。
- 持久性 (Durability):一旦转账提交成功,数据就被永久写在硬盘上了,哪怕下一秒拔网线宕机,钱也不会丢。”
6:讲讲 MySQL 的四种隔离级别,分别解决了什么问题?
[满分回答]:
“可以把隔离级别看作是数据库配的‘滤网’:
- RU (读未提交):没滤网。别人正在写、还没提交的半成品数据你能看见。他一回滚,你就遇到脏读。
- RC (读已提交):动态滤网。别人没提交你看不见,解决了脏读。但因为滤网是每次查询动态生成的,别人中途提交了,你下一秒查数据就变了,这叫不可重复读。
- RR (可重复读 - MySQL 默认):静态滤网。开启事务瞬间世界静止,别人怎么改你看到的都是初见的样子,解决了不可重复读。并且 InnoDB 极其强悍,用间隙锁(Gap Lock)锁住了查询范围的空位,把别人偷偷插数据的幻读问题也一并解决了大半。
- Serializable (串行化):退化成悲观锁单行道。读写互相排队,解决一切并发异常,但也把系统性能干瘫痪了。”
7:undo log 和 redo log 的区别是什么?
[满分回答]:
“它们是保证 ACID 的底层核心,区别在于:
- redo log(重做日志):记录的是数据页的物理变化。它是为了应对服务突然宕机后的数据恢复,保证了事务的 持久性 (D)。
- undo log(回滚日志):记录的是逻辑日志(比如你 Insert,它就记 Delete)。它是为了事务失败时能回滚恢复原始数据,以及支持 MVCC 多版本控制,保证了事务的 原子性 (A) 和 一致性 (C)。”
第三板块:高并发架构演进(项目亮点必考)
8:说说 MySQL 主从同步的原理是什么?
[满分回答]:
“主从同步核心靠的是二进制日志(Binlog),分为三步接力:
- 主库在事务提交时,将数据变更记录到自己的 Binlog 中。
- 从库通过 I/O 线程,拉取主库的 Binlog,并将其原封不动地写入自己的中继日志(Relay Log)。
- 从库的 SQL 线程读取中继日志中的事件,在本地重做一遍,从而保证数据同步。”
9:你们的项目为什么要做分库分表?具体是怎么落地的?(整合了你的真实项目经历)
[满分回答]:
“随着我们业务的演进,垂直和水平拆分我们都用到了。
- 垂直拆分:配合微服务,我们把业务解耦,比如把订单库和用户库物理分离开来。
- 水平拆分(解决痛点):后来『订单库』单表突破了一千万,成为严重瓶颈。我们在 SkyRoute 短链项目中引入了 ShardingSphere 做了单库内的水平分表。
- 路由策略:我们将短链表按
short_code、日志表按tenant_id(租户)进行 Hash 取模,路由到了 4 张物理表中。 - 最终效果:对业务代码完全无感知。C 端短链跳转查询能通过
short_code精确击中单表,速度极快;如果是后台那种没带分片键的报表查询,ShardingSphere 会自动广播到 4 张表查询后再在内存里合并结果。彻底解决了海量数据下的单表读写性能瓶颈。”
Comments NOTHING