数据库面试题基础知识全解析
深入理解关系型数据库(MySQL/PostgreSQL)与NoSQL核心机制,掌握高并发场景下的设计原则与优化策略。
一、 数据库基础概念与范式
1.1 什么是数据库?
数据库(Database)是按照数据结构来组织、存储和管理数据的仓库。它产生的目的是方便独立于应用程序进行操作的数据集合。在现代互联网架构中,数据库是核心组件,负责持久化存储用户数据、业务状态及日志信息。
1.2 关系型 vs 非关系型
关系型数据库(RDBMS)如MySQL、Oracle,基于表格结构,支持SQL语言,强调ACID事务特性,适合结构化数据。非关系型数据库(NoSQL)如Redis、MongoDB,数据结构灵活(键值、文档、列族、图),强调高可用和高扩展性,适合海量非结构化数据。
1.3 数据库范式
范式是设计关系数据库时的规范准则。
第一范式(1NF):列不可再分,确保原子性。
第二范式(2NF):在1NF基础上,非主键列必须完全依赖于主键。
第三范式(3NF):在2NF基础上,消除传递依赖。
【面试官追问】:为什么实际开发中经常违反第三范式?
解答:为了提高查询性能。通过适当的冗余字段(反范式化),可以减少表连接(Join)操作,从而降低I/O开销和CPU计算量,这是典型的“空间换时间”策略。
二、 SQL性能优化核心策略
SQL优化是面试中的高频考点,主要关注执行计划、索引命中及查询语句编写规范。
2.1 避免全表扫描与隐式类型转换
全表扫描是性能杀手。当WHERE条件中的字段未建立索引,或数据类型不匹配时,数据库引擎会放弃索引进行全表扫描。
-- ❌ 错误示例:对索引字段进行函数运算,导致索引失效
SELECT FROM users WHERE YEAR(create_time) = 2023;
-- ✅ 正确示例:使用范围查询,利用索引有序性
SELECT FROM users WHERE create_time >= '2023-01-01' AND create_time < '2024-01-01';
2.2 深分页问题优化
当使用 LIMIT 1000000, 10 时,MySQL需要扫描前100万条记录并丢弃,效率极低。
-- 优化方案:利用覆盖索引回表
SELECT FROM users
WHERE id >= (SELECT id FROM users LIMIT 1000000, 1)
LIMIT 10;
2.3 EXPLAIN 关键字解读
使用 EXPLAIN 可以查看SQL的执行计划。重点关注以下字段:
- type:访问类型。性能从好到坏:system > const > eq_ref > ref > range > index > ALL。
- key:实际使用的索引。
- rows:预估扫描行数。
- Extra:额外信息。出现
Using filesort表示需要额外排序,出现Using temporary表示使用了临时表,均需要优化。
2.4 查询重写与索引下推
MySQL 5.6引入了索引下推(ICP, Index Condition Pushdown)优化。在传统索引扫描中,存储引擎遍历索引后回表查数据,再在Server层过滤。ICP允许在存储引擎层利用索引中包含的字段信息过滤数据,减少回表次数。
三、 B+树与索引底层原理
3.1 为什么选择B+树?
相比于B树和红黑树,B+树更适合数据库索引。原因如下:
1. 磁盘IO次数少:B+树高度通常为2-4层,一次查询仅需2-4次磁盘IO。
2. 查询效率稳定:所有数据都在叶子节点,范围查询只需遍历链表。
3. 磁盘利用率极高:非叶子节点只存索引,单个节点可容纳更多键值。
3.2 聚簇索引 vs 非聚簇索引
聚簇索引:叶子节点存储完整的数据行。InnoDB表必须有且只有一个聚簇索引(通常是主键)。
非聚簇索引(二级索引):叶子节点存储的是主键ID。查询时需要先查二级索引得到主键ID,再查聚簇索引得到数据,称为“回表”。
3.3 最左前缀法则
联合索引 (a, b, c) 遵循最左前缀匹配。查询条件必须包含索引的最左边列才会走索引。例如:
- WHERE a=1 AND b=1 → 走索引
- WHERE b=1 AND c=1 → 不走索引
- WHERE a=1 AND c=1 → 只使用a列索引
3.4 覆盖索引与回表优化
覆盖索引是指查询的列恰好是索引列,无需回表查询主键索引。例如:
-- 假设 idx_name 是 name 的索引
SELECT name FROM users WHERE name = 'Alice';
-- 此查询直接通过 idx_name 获取数据,无需回表,性能极高。
四、 事务ACID与锁机制
| 特性 | 说明 | 实现机制 |
|---|---|---|
| 原子性 (Atomicity) | 事务是一个不可分割的工作单位,要么全做,要么全不做。 | Undo Log:记录数据的反向操作,用于回滚。 |
| 一致性 (Consistency) | 事务前后,数据的完整性约束没有被破坏。 | 结合ACID其他三个特性共同实现。 |
| 隔离性 (Isolation) | 多个事务并发执行时,互不干扰。 | 锁机制 + MVCC(多版本并发控制)。 |
| 持久性 (Durability) | 事务一旦提交,对数据的修改是永久的。 | Redo Log:记录物理修改,用于崩溃恢复。 |
4.1 隔离级别与问题
- 读未提交 (Read Uncommitted):允许脏读。
- 读已提交 (Read Committed, RC):解决脏读,但不可重复读。Oracle默认。
- 可重复读 (Repeatable Read, RR):解决脏读、不可重复读。MySQL InnoDB默认。
- 串行化 (Serializable):解决幻读,性能最低。
4.2 MVCC(多版本并发控制)
InnoDB通过MVCC实现高并发下的非阻塞读。它利用隐藏列(trx_id, roll_pointer)、Undo Log版本链和Read View来实现快照读。
Read View是事务读数据时生成的可见性规则。在RR级别下,第一次查询生成Read View,后续查询复用;在RC级别下,每次查询都生成新的Read View。
五、 NoSQL与MySQL选型对比
在微服务架构中,数据库选型至关重要。以下是常见NoSQL数据库的特性对比:
Redis (键值型)
优势:极速读写(内存数据库),支持复杂数据结构(String, List, Set, Hash, ZSet)。
场景:缓存、计数器、排行榜、分布式锁。
注意:数据量受内存限制,需考虑持久化(RDB/AOF)及集群方案。
MongoDB (文档型)
优势:JSON格式存储,模式灵活,支持地理空间索引。
场景:内容管理系统(CMS)、日志存储、物联网数据。
注意:事务支持较弱,聚合查询性能不如关系型数据库。
Elasticsearch (搜索型)
优势:基于倒排索引,全文检索能力极强,支持海量数据分析。
场景:日志分析、商品搜索、复杂条件筛选。
注意:集群维护复杂,数据一致性不如MySQL。
六、 网友还关心:高频实战问题解答
以下整理了近期求职者反馈最多的实战问题,结合真实业务场景进行深度解析。
Q1: 缓存穿透、缓存击穿、缓存雪崩区别?
缓存穿透:查询不存在的数据,请求直达数据库。对策:布隆过滤器、缓存空对象。
缓存击穿:热点Key过期,大量请求瞬间打到DB。对策:互斥锁、逻辑过期。
缓存雪崩:大量Key同时过期或Redis宕机。对策:随机过期时间、Redis集群高可用。
Q2: 如何设计百万级数据的分页查询?
除了上述提到的延迟关联,还可以采用游标法(基于上次查询的最大ID)或搜索引擎法(ES分页)。避免使用大偏移量 LIMIT。
Q3: 主从同步延迟怎么解决?
MySQL主从异步复制存在延迟。解决策略:1. 强一致性场景强制读主库;2. 增加从库数量;3. 使用半同步复制(Semi-sync)牺牲部分性能换取安全性。
Q4: 分库分表方案有哪些?
当单表数据超过500万或单库超过100GB时需分片。策略:
1. 垂直拆分:按业务模块拆分库。
2. 水平拆分:按哈希(Hash)或范围(Range)拆分表。
中间件:ShardingSphere、MyCat。
📚 拓展阅读:数据库架构演进之路
从单体数据库到分布式数据库,技术选型随业务增长而演变:
- 阶段一:单一MySQL实例,满足初创期需求。
- 阶段二:主从复制 + 读写分离,提升读性能。
- 阶段三:引入Redis缓存层,降低DB压力。
- 阶段四:分库分表,解决写入瓶颈。
- 阶段五:分布式数据库(如TiDB、OceanBase),实现水平扩展与高可用一体化。