用面试题的方式复盘数据库基础:范式、约束、事务、隔离级别、MVCC、锁、日志、索引、慢查询、主从复制和分库分表。
准备八股一方面是为了面试,另一方面也是用"刷题"思维检查自己到底有没有掌握概念。数据库八股尤其适合这样学,因为很多问题不是孤立的:事务会连到 ACID,隔离级别会连到 MVCC 和锁,索引会连到执行计划和慢查询,主从复制又会连到 binlog。
这篇先以 MySQL / InnoDB 为主线整理。答案尽量写成"能直接说出口"的版本,但也保留一点理解层次,避免只背关键词。
一、基础概念
Q1:数据库的三范式是什么?
三范式是关系型数据库设计中减少数据冗余、避免更新异常的基本规范。
| 范式 | 核心要求 | 直觉 |
|---|---|---|
| 第一范式 1NF | 字段不可再分 | 一列只存一个原子值 |
| 第二范式 2NF | 非主属性完全依赖主键 | 联合主键场景里,字段不能只依赖主键的一部分 |
| 第三范式 3NF | 非主属性不能传递依赖主键 | 字段只描述当前实体,不要绕一层依赖 |
举个直觉例子:订单表里不要反复存用户邮箱。订单只存 user_id,用户邮箱放在用户表里。否则用户改邮箱时,要更新很多订单行,容易不一致。
但真实业务不一定越范式化越好。为了查询性能,有时会做反范式设计,比如订单表冗余一份下单时的商品名称、价格快照。
1NF是数据库最低要求,2NF、3NF依次更规范,范式越高数据冗余越少,但查询可能更复杂。1NF比较容易理解,就是一列存一个值;2NF主要出现在"联合主键场景",比如在 (order_id, product_id) 中,product_name 就不适合放在这里,因为它只依赖于其中之一的主键 product_id,quantity适合放在这里,因为即使同样的product_id,不同批次的 order_id 也可能质量不同;3NF主要是防止依赖传递,比如主键是user_id,传递链:user_id -> department_id -> department_name,department_name实际依赖的是 department_id,那么更好的设计就是分表,一张表只描述一种实体。
Q2:超键、候选键、主键、外键分别是什么?
- 超键:能唯一标识一行的字段集合。
- 候选键:最小的超键,去掉任意字段后就不能唯一标识。
- 主键:从候选键里选出来作为主要标识的键。
- 外键:一张表里的字段引用另一张表的主键或唯一键,用来表达和约束关系。
比如用户表:
users(id, email, name)如果 id 唯一,email 也唯一,那么 {id} 和 {email} 都可能是候选键。实际常用 id 做主键,因为它稳定、无业务含义;订单表里的 user_id 就可以作为外键引用 users.id。
Q3:主键用自增 ID 还是 UUID?
常见答案是:大多数单体或普通业务系统用自增 ID 更简单;分布式系统、离线生成 ID、跨库合并数据时可能使用 UUID、雪花 ID 等。
自增 ID 优点:
- 短,存储和索引成本低。
- 有序,适合 InnoDB 聚簇索引插入。
- 排查问题时更直观。
自增 ID 缺点:
- 分布式多库生成时容易冲突。
- 容易暴露数据规模。
UUID 优点:
- 全局唯一,适合分布式生成。
- 不依赖数据库自增。
UUID 缺点:
- 长,索引占用更大。
- 随机 UUID 插入可能导致索引页分裂,影响写入性能。
所以一句话:能用稳定短 ID 就先用;分布式场景再考虑 UUID、雪花 ID、ULID 这类方案。
Q4:SQL 约束有哪几种?
常见约束有:
| 约束 | 含义 |
|---|---|
PRIMARY KEY | 主键,唯一且非空 |
FOREIGN KEY | 外键,保证引用关系合法 |
UNIQUE | 唯一约束,可以防止重复 |
NOT NULL | 非空约束 |
DEFAULT | 默认值 |
CHECK | 检查约束,如金额不能小于 0 |
约束的意义是让数据库参与保护数据质量,不要把所有规则都只放在应用代码里。
Q5:varchar 和 char 有什么区别?
char 是定长字符串,varchar 是变长字符串。
| 类型 | 特点 | 适合场景 |
|---|---|---|
char(n) | 固定长度,不足可能补空格 | 固定长度编码,如性别、状态码、MD5 |
varchar(n) | 按实际长度存储 | 用户名、邮箱、标题等变长文本 |
实际业务中,变长字段通常用 varchar。char 更适合长度天然固定的字段。
Q6:in 和 exists 有什么区别?
IN 更像"值是否在集合里",EXISTS 更像"子查询是否存在结果"。
SELECT *FROM usersWHERE id IN ( SELECT user_id FROM orders);SELECT *FROM users uWHERE EXISTS ( SELECT 1 FROM orders o WHERE o.user_id = u.id);一般来说,IN 适合子查询结果集较小、语义上是集合匹配的场景;EXISTS 适合相关子查询,只关心是否存在。现代 MySQL 优化器会做很多改写,实际性能要看执行计划,不能只背"谁一定更快"。
Q7:drop、delete、truncate 有什么区别?
| 命令 | 作用 | 是否删表结构 | 是否可带 WHERE | 常见特点 |
|---|---|---|---|---|
DELETE | 删除行数据 | 否 | 可以 | DML,可按条件删,通常记录 undo/redo |
TRUNCATE | 清空整表 | 否 | 不可以 | DDL,速度快,通常重置自增 |
DROP | 删除表 | 是 | 不可以 | 表结构和数据都没了 |
简单记:DELETE 删数据行,TRUNCATE 清空表,DROP 删除表本身。
DML:Data Manipulation Language,数据操作语言;DDL:Data Definition Language,数据定义语言。
Q8:什么是存储过程?优缺点是什么?
存储过程是保存在数据库里的 SQL 程序,可以封装多条 SQL、条件判断、循环等逻辑。
优点:
- 减少应用和数据库之间的多次往返。
- 可以复用复杂数据库逻辑。
- 权限可以集中控制。
缺点:
- 逻辑沉到数据库里,版本管理和测试不如应用代码方便。
- 可移植性差,不同数据库语法不同。
- 业务逻辑分散,排查问题可能更麻烦。
现在很多互联网业务更倾向把业务逻辑放在应用层,存储过程更多用于历史系统、报表、批处理、强数据库中心化的场景。
Q9:MySQL 支持哪些存储引擎?
常见存储引擎有 InnoDB、MyISAM、Memory、Archive 等。
| 引擎 | 特点 |
|---|---|
| InnoDB | 默认引擎,支持事务、行锁、外键、MVCC |
| MyISAM | 不支持事务,表锁,读多写少的老场景 |
| Memory | 数据在内存里,重启丢失 |
| Archive | 适合归档写入,查询能力较弱 |
现在最常用的是 InnoDB,因为它支持事务、崩溃恢复、行级锁和 MVCC,更适合通用业务系统。
存储引擎可以理解成:数据库真正负责"怎么把数据存到磁盘、怎么读出来、怎么加锁、怎么保证事务"的底层模块。MySQL 本身像一个"数据库外壳",负责:连接、权限、SQL 解析、SQL 优化、执行器;但真正到了"拿数据"的时候,执行器会去调用存储引擎。
二、事务、隔离级别与 MVCC
Q10:什么是数据库事务?
事务是一组数据库操作组成的逻辑单元,要么全部成功,要么全部失败。
比如下单:
- 创建订单。
- 扣库存。
- 写支付流水。
这些操作不能只成功一部分。事务就是用来保证这种"整体性"的。
Q11:事务的 ACID 是什么?
| 特性 | 含义 | 直觉 |
|---|---|---|
| Atomicity 原子性 | 要么全成功,要么全失败 | 不做半截 |
| Consistency 一致性 | 事务前后数据满足约束 | 不破坏规则 |
| Isolation 隔离性 | 并发事务互不随便干扰 | 不乱串 |
| Durability 持久性 | 提交后数据可靠保存 | 不丢结果 |
面试里可以用转账举例:A 扣钱和 B 加钱必须一起成功,不能 A 扣了钱 B 没收到。
Q12:MySQL 的四种隔离级别是什么?
从低到高:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| Read Uncommitted | 可能 | 可能 | 可能 |
| Read Committed | 避免 | 可能 | 可能 |
| Repeatable Read | 避免 | 避免 | InnoDB 大多可避免 |
| Serializable | 避免 | 避免 | 避免 |
MySQL InnoDB 默认隔离级别是 Repeatable Read。
Read Uncommitted:别人没提交我也能看见;Read Committed:别人提交后我才能看见;Repeatable Read:我事务开始后,看到的快照尽量不变;Serializable:大家排队来,最安全也最慢;MySQL InnoDB 默认是 Repeatable Read,Oracle、PostgreSQL 默认一般是 Read Committed。
Q13:脏读、不可重复读、幻读是什么?
脏读:读到了其他事务还没提交的数据。对方回滚后,你读到的就是脏数据。
不可重复读:同一个事务里,两次读取同一行,结果不一样。通常是因为别的事务提交了更新。
幻读:同一个事务里,两次按同一条件查询,结果行数变了。通常是因为别的事务插入或删除了符合条件的行。
直觉区分:
- 不可重复读关注"同一行变了"。
- 幻读关注"符合条件的行变多或变少了"。
Q14:事务的实现原理是什么?
InnoDB 事务主要依赖:
- undo log:用于回滚,也用于 MVCC 读取历史版本。
- redo log:用于崩溃恢复,保证提交后的修改不丢。
- 锁:控制并发修改。
- MVCC:在不加锁读的情况下提供一致性读。
可以粗略理解:undo log 保证能撤回,redo log 保证能恢复,锁和 MVCC 保证并发下读写有秩序。
Q15:redo log、undo log、binlog 分别是什么?
| 日志 | 属于 | 作用 |
|---|---|---|
| undo log | InnoDB | 回滚、MVCC 历史版本 |
| redo log | InnoDB | 崩溃恢复,保证持久性 |
| binlog | MySQL Server 层 | 主从复制、时间点恢复 |
redo log 是物理日志,记录页级别修改;binlog 更偏逻辑日志,记录 SQL 或行变化。事务提交时,InnoDB redo log 和 MySQL binlog 要通过两阶段提交保证一致。
Q16:什么是 MVCC?
MVCC 是多版本并发控制。它的目标是:读不阻塞写,写也尽量不阻塞读。
InnoDB 会为行记录维护隐藏字段和 undo log 版本链。普通 SELECT 在一致性读时,不一定读最新版本,而是根据 ReadView 判断哪个版本对当前事务可见。
所以 MVCC 的核心是:
- 一行数据可能有多个历史版本。
- 事务读数据时,根据可见性规则选择一个版本。
- 这样可以减少读写冲突。
可见性规则就是 MVCC 用来判断"当前事务能不能看到某个数据版本"的规则。
Q17:MVCC 的实现原理是什么?
InnoDB MVCC 主要依赖:
- 隐藏事务 ID:记录最后修改该行的事务。
- 回滚指针:指向 undo log 中的旧版本。
- undo log:保存旧版本数据。
- ReadView:判断哪些事务版本可见。
在 Repeatable Read 下,一个事务第一次一致性读时生成 ReadView,后续复用它,所以同一事务里多次查询结果更稳定。
在 Read Committed 下,每次一致性读都会生成新的 ReadView,所以能读到其他事务已经提交的新版本。
一致性读,可以理解成:事务读取数据时,不一定读取数据库"此刻最新"的数据,而是读取某个时间点上的一致快照。
三、锁与并发
Q18:为什么要加锁?
锁是为了解决并发读写冲突,保证数据正确性。
比如两个事务同时扣同一件商品库存。如果没有锁,都读到库存是 1,然后都扣成 0,就可能超卖。锁可以让修改同一行的事务排队。
Q19:按粒度分,数据库锁有哪些?
常见有:
- 表锁:锁整张表,粒度大,并发低。
- 行锁:锁具体行,粒度小,并发高。
- 页锁:锁数据页,介于表锁和行锁之间,MySQL InnoDB 较少直接强调。
InnoDB 支持行锁,这是它适合高并发业务的重要原因之一。
Q20:MySQL 里常见锁类型有哪些?
按锁模式:
- 共享锁 S Lock:读锁,多个事务可以同时持有。
- 排他锁 X Lock:写锁,其他事务不能同时写,也通常不能加读锁。
按 InnoDB 行锁算法:
- Record Lock:锁单条索引记录。
- Gap Lock:锁索引记录之间的间隙。
- Next-Key Lock:Record Lock + Gap Lock,用来避免幻读。
Q21:乐观锁和悲观锁是什么?
悲观锁:假设冲突经常发生,操作前先加锁。
SELECT stockFROM productsWHERE id = 1FOR UPDATE;FOR UPDATE 就是告诉数据库我现在读这行,不只是看看。我接下来大概率要改它,所以你先帮我把它锁住,别让别的事务同时改;等到这个事务用过了COMMIT之后,别的事务才能继续读。
乐观锁:假设冲突不多,更新时检查版本号或条件。
UPDATE productsSET stock = stock - 1, version = version + 1WHERE id = 1 AND version = 3 AND stock > 0;影响行数为 0 就说明更新失败,需要重试或返回失败。
读数据的同时读出版本号,修改时检查我读到的版本号是否仍然有效。乐观锁就是更新时带上"我以为的数据状态",如果状态没变就更新,变了就失败。
Q22:InnoDB 的行锁是怎么实现的?
InnoDB 的行锁是加在索引上的。查询条件如果命中索引,就锁对应索引记录;如果没有合适索引,可能扫描更多记录,导致锁范围扩大。
所以行锁不是"天然只锁一行"。写更新语句时,合适的索引非常重要。
有些 SQL 锁的不是"某个已经存在的值",而是"这个查询条件对应的范围"。如果精确找到,确实是锁精确值。但是如果是没找到,要锁应该出现的位置,比如BETWEEN AND,就要防止在这个范围内插入。
Q23:什么是死锁?怎么解决?
死锁是多个事务互相等待对方释放锁,形成循环等待。
比如:
事务 A 锁住 user:1,等待 user:2事务 B 锁住 user:2,等待 user:1解决思路:
- 保持固定的加锁顺序。
- 事务尽量短,少做无关操作。
- 给查询条件加合适索引,减少锁范围。
- 捕获死锁异常后重试。
- 避免大事务。
Q24:隔离级别和锁有什么关系?
隔离级别越高,对并发异常限制越强,通常需要更多锁或更严格的可见性规则。
InnoDB 在 Repeatable Read 下通过 MVCC 解决一致性读,通过 Next-Key Lock 等机制处理当前读中的幻读问题。
普通 SELECT 多数是快照读,不加锁;SELECT ... FOR UPDATE、UPDATE、DELETE 属于当前读,会读取最新数据并加锁。
四、索引与 SQL 优化
Q25:索引的分类方式有哪些?
常见分类:
- 按数据结构:B+ 树索引、Hash 索引、全文索引、空间索引。
- 按字段数量:单列索引、联合索引。
- 按唯一性:普通索引、唯一索引。
- 按物理组织:聚簇索引、二级索引。
MySQL InnoDB 最常见的是 B+ 树索引。
Q26:聚簇索引和非聚簇索引有什么区别?
InnoDB 中,聚簇索引就是按主键组织整行数据的索引。叶子节点存的是完整行数据。
二级索引,也叫非聚簇索引,叶子节点存的是索引列和主键值。通过二级索引找到主键后,如果还需要其他列,就要回到聚簇索引查整行,这叫回表。
所以:
- 主键查询通常很快。
- 二级索引如果覆盖了查询所需列,就可以避免回表。
如果 InnoDB 表没有显式主键,它会选择一个唯一非空索引作为聚簇索引;如果还没有,就生成隐藏的 row id 作为聚簇索引。所以,聚簇索引不一定永远等于你声明的主键索引。但是一般都会建主键,所以InnoDB中聚簇索引 ≈ 主键索引 / 一级索引,非聚簇索引 ≈ 二级索引 / 辅助索引
Q27:B+ 树有什么特点?为什么 MySQL 用 B+ 树?
B+ 树是多叉平衡搜索树,特点是:
- 树高低,减少磁盘 IO。
- 数据都在叶子节点。
- 叶子节点之间有链表,适合范围查询。
- 节点有序,适合排序和范围扫描。
相比二叉树,B+ 树更"矮胖",更适合磁盘页读取。相比 Hash,B+ 树不仅能等值查询,还能范围查询和排序。
Q28:创建联合索引要注意什么?
联合索引要注意最左前缀原则。
比如:
CREATE INDEX idx_user_status_time ON orders(user_id, status, created_at);通常适合:
WHERE user_id = 1WHERE user_id = 1 AND status = 'paid'WHERE user_id = 1 AND status = 'paid' ORDER BY created_at但不太适合只按 status 查询,因为跳过了最左边的 user_id。
设计联合索引时,要看真实查询条件,不是字段越多越好。
Q29:什么是索引失效?
索引失效是指明明建了索引,但查询没有有效使用它。
常见原因:
- 对索引列使用函数:
LOWER(email) = ... - 隐式类型转换:字符串字段拿数字比较。
- 左模糊匹配:
LIKE '%abc' - 联合索引不满足最左前缀。
OR条件中有列没有索引。- 查询返回大量数据,优化器认为全表扫描更划算。
解决思路是看 EXPLAIN,确认访问类型、使用索引、扫描行数,再调整 SQL 或索引。
Q30:如何定位和优化慢 SQL?
常见流程:
- 开启慢查询日志,找到慢 SQL。
- 用
EXPLAIN看执行计划。 - 看是否全表扫描、是否走错索引、扫描行数是否过大。
- 优化索引、改写 SQL、减少返回列和返回行。
- 必要时做分页优化、归档冷热数据、分库分表。
- 重新压测或观察指标。
不要只凭感觉加索引。索引要服务具体查询。
EXPLAIN 里可以重点看这几个字段:
| 字段 | 重点看什么 |
|---|---|
type | 访问类型,常见从好到差大致是 const、ref、range、index、ALL |
possible_keys | 理论上可能用到哪些索引 |
key | 实际用了哪个索引 |
rows | 预计扫描多少行 |
Extra | 是否出现 Using filesort、Using temporary、Using index 等信息 |
如果看到 type = ALL、rows 很大、Extra 里有 Using filesort 或 Using temporary,就要重点检查查询条件、排序字段和索引设计。
Q31:大表查询怎么优化?
常见方向:
- 建合适索引。
- 避免
SELECT *。 - 加时间范围或业务范围,减少扫描。
- 分页避免深 offset。
- 归档历史数据,做冷热分离。
- 用汇总表、物化视图或定时统计减少实时聚合。
- 必要时分库分表。
核心是减少扫描行数、减少回表、减少排序临时表。
Q32:超大分页怎么处理?
普通分页:
LIMIT 1000000, 20会跳过大量行,越往后越慢。
优化方式:
- 游标分页:
SELECT *FROM ordersWHERE id > 1000000ORDER BY idLIMIT 20;- 延迟关联:
SELECT o.*FROM orders oJOIN ( SELECT id FROM orders ORDER BY id LIMIT 1000000, 20) t ON o.id = t.id;- 产品上限制深分页,只允许按条件筛选。
Q33:什么是覆盖索引?
如果一个查询需要的列都在同一个索引里,数据库不用回表,这就是覆盖索引。
比如有索引:
CREATE INDEX idx_users_email_name ON users(email, name);查询:
SELECT email, nameFROM usersWHERE email = 'owen@example.com';只需要读索引就能得到结果,不必再回到聚簇索引取整行。
Q34:count(*)、count(1)、count(column) 有什么区别?
count(*):统计行数,包括所有行。count(1):也是统计行数,现代数据库里通常和count(*)差别不大。count(column):统计该列非 NULL 的行数。
面试里可以说:MySQL 中一般推荐用 count(*) 表达统计行数,语义最清楚,优化器也会处理。
五、查询执行与 SQL 语义
Q35:MySQL 执行查询的大致过程是什么?
大致流程:
- 客户端发送 SQL。
- 连接器处理连接和权限。
- 解析器做词法、语法分析。
- 预处理器检查表、字段、权限。
- 优化器选择执行计划。
- 执行器调用存储引擎接口读取数据。
- 返回结果。
现在 MySQL 8 已经没有老版本的查询缓存作为主路径了,回答时不要把 query cache 当成核心流程。
Q36:WHERE 和 HAVING 的区别?
WHERE 在分组前过滤原始行,HAVING 在分组后过滤聚合结果。
SELECT user_id, COUNT(*) AS order_countFROM ordersWHERE status = 'paid'GROUP BY user_idHAVING COUNT(*) > 3;这里先用 WHERE 过滤已支付订单,再按用户分组,最后用 HAVING 找订单数大于 3 的用户。
Q37:JOIN 的 ON 和 WHERE 有什么区别?
ON 是连接条件,WHERE 是对连接后的结果继续过滤。
在 INNER JOIN 中,有些条件放 ON 或 WHERE 结果可能一样;但在 LEFT JOIN 中差别很大。
SELECT u.id, o.idFROM users uLEFT JOIN orders o ON u.id = o.user_id AND o.status = 'paid';这会保留没有支付订单的用户。
如果写成:
SELECT u.id, o.idFROM users uLEFT JOIN orders o ON u.id = o.user_idWHERE o.status = 'paid';就会把没有匹配订单的用户过滤掉,效果接近内连接。
补充:SQL 注入是什么?怎么防?
SQL 注入是把用户输入当成 SQL 语句的一部分拼接执行,导致查询逻辑被恶意改变。
危险写法:
SELECT *FROM usersWHERE email = '${email}' AND password = '${password}';如果用户输入里包含特殊 SQL 片段,就可能绕过条件或执行危险操作。
正确思路是使用参数化查询,也就是让 SQL 模板和参数分离:
SELECT *FROM usersWHERE email = ? AND password = ?;应用代码把 email、password 作为参数传给数据库驱动,不直接拼接进 SQL 字符串。这样数据库会把它们当成值,而不是当成 SQL 语法。
六、主从复制与高可用
Q38:什么是 MySQL 主从复制?
主从复制是把主库的数据变更同步到从库。主库负责写入,从库复制主库的 binlog 并重放。
人话就是一台数据库负责写,另一台或多台数据库跟着复制它的数据。
常见用途:
- 读写分离。
- 数据备份。
- 故障切换。
- 报表查询隔离。
Q39:MySQL 主从复制流程是什么?
简化流程:
- 主库提交事务,写 binlog。
- 从库 IO 线程拉取主库 binlog,写入 relay log。
- 从库 SQL 线程或 worker 线程读取 relay log 并重放。
- 从库数据逐步追上主库。
MySQL 复制可以是异步、半同步,也可以配置 GTID 方便故障切换和定位。
Q40:主从延迟怎么解决?
主从延迟原因可能是:
- 主库写入压力大。
- 从库机器性能弱。
- 大事务重放慢。
- 网络延迟。
- 从库并行复制能力不足。
解决思路:
- 避免大事务,拆批处理。
- 提升从库配置。
- 开启并行复制。
- 读写分离时,对强一致读走主库。
- 业务上接受最终一致,做延迟提示或重试。
- 监控复制延迟。
Q41:如何实现读写分离?
常见做法:
- 写请求走主库。
- 普通读请求走从库。
- 强一致读、刚写完马上读的请求走主库。
- 通过中间件、代理层或应用代码做路由。
要注意主从延迟。不是所有读都适合走从库。
七、分库分表
Q42:为什么要分库分表?
当单库单表在容量、并发、维护成本上扛不住时,可以考虑分库分表。
常见原因:
- 单表数据量太大,索引和查询变慢。
- 单库连接数、CPU、IO 压力太高。
- 数据增长过快,备份和 DDL 成本高。
- 希望按业务或租户拆分。
分库分表不是银弹,会增加复杂度。能通过索引、归档、缓存、读写分离解决的问题,不一定要立刻分库分表。
Q43:垂直拆分和水平拆分有什么区别?
垂直拆分:按业务或字段拆。
- 垂直分库:用户库、订单库、支付库。
- 垂直分表:把大字段、低频字段拆到扩展表。
水平拆分:按行拆,把同一张表的数据分散到多个表或库。
比如订单按 user_id 取模:
orders_0orders_1orders_2orders_3垂直拆分解决业务耦合和单表字段过宽,水平拆分解决单表数据量和单库压力。
Q44:分库分表会带来哪些问题?
常见问题:
- 跨库 JOIN 变困难。
- 分布式事务复杂。
- 全局唯一 ID 需要单独设计。
- 跨分片分页、排序、聚合成本高。
- 扩容和数据迁移复杂。
- SQL 兼容性受中间件限制。
所以分库分表要谨慎,最好先确认瓶颈和增长趋势。
Q45:常见分库分表中间件有哪些?
常见有 ShardingSphere-JDBC、Mycat、TDDL、Atlas、Cobar 等。
现在更常见的回答可以重点说 ShardingSphere:
- ShardingSphere-JDBC:轻量,作为 Java JDBC 增强层接入,应用内路由。
- Mycat:代理式中间件,对应用透明一些,但多一层代理。
不同方案主要差别在接入方式、SQL 支持度、运维复杂度和生态活跃度。
八、面试回答策略
Q46:实际发现查询很慢,有哪些解决方案?
可以按这个顺序答:
- 先确认慢在哪里:慢查询日志、监控、执行时间。
- 用
EXPLAIN看执行计划。 - 看是否全表扫描、索引失效、扫描行数过大、排序临时表过大。
- 优化 SQL:减少列、减少行、避免函数包索引列、避免深分页。
- 优化索引:单列、联合、覆盖索引。
- 优化表结构:冷热分离、归档、冗余汇总。
- 优化架构:缓存、读写分离、分库分表。
这样回答比较完整,不会一上来只说"加索引"。
Q47:数据库知识怎么串起来?
可以用一条主线串:
表结构设计-> 约束保证数据合法-> 事务保证一组修改可靠-> 隔离级别 / MVCC / 锁解决并发-> 索引和执行计划解决查询性能-> 日志保证恢复和复制-> 主从 / 分库分表解决规模问题面试题看起来很多,但它们其实都围绕一个核心问题:数据库如何在正确性、性能和可扩展性之间做平衡。
专题阅读
SQL
这篇文章属于同一条阅读链。你可以直接在这里切换,不用再回到列表页重新找。
部分信息可能已经过时