数据模型与范式设计
数据库不是直接存硬盘,而是先经过三层抽象:概念模型(ER图)→ 逻辑模型(关系模式)→ 物理模型(存储结构)。范式(Normal Form)的本质是消除数据冗余与异常,但并非越高越好。
- 第一范式(1NF):原子性——字段不可再分。如“地址”应拆为省、市、区、街道。
- 第二范式(2NF):非主属性完全依赖主键——消除部分依赖。订单明细表不能仅用“商品ID”做主键,必须组合“订单ID+商品ID”。
- 第三范式(3NF):消除传递依赖——非主属性不依赖其他非主属性。员工表不应存部门名称,应通过部门ID关联。
- 反范式设计:为提升查询性能,可适度冗余字段,如订单表中冗余“用户姓名”与“商品名称”,避免频繁JOIN。
SQL执行引擎与查询优化
很多人认为“SQL是声明式语言,写法决定性能”——这是误区。SQL语句只是“目标”,数据库引擎会自动重写、优化执行计划。真正影响性能的,是:索引设计、统计信息准确性、执行计划选择。
例如:以下两条语句逻辑等价,但性能差异可达10倍:
SELECT FROM orders WHERE YEAR(order_date) = 2024;
SELECT FROM orders WHERE order_date BETWEEN '2024-01-01' AND '2024-12-31';
使用EXPLAIN查看执行计划是数据库原理答案改写的必备技能:
EXPLAIN SELECT FROM orders WHERE user_id = 10025;
当type为ALL(全表扫描)且rows极大时,必须添加或优化索引。
事务与ACID保障
事务是数据库的“保险丝”,确保操作要么全成功,要么全失败。ACID特性缺一不可:
- Atomicity(原子性):通过undo log实现回滚
- Consistency(一致性):约束(主键、外键、唯一性)保障数据正确
- Isolation(隔离性):通过锁机制与MVCC实现并发控制
- Durability(持久性):通过redo log写入磁盘保障
隔离级别是数据库原理答案改写中的高频考点,也是真实业务的痛点。常见问题如下:
脏读(Dirty Read)
事务A读到事务B未提交的数据 → 若B回滚,A读到的就是脏数据
不可重复读(Non-repeatable Read)
同一事务内,两次读同一行,结果不同(因其他事务UPDATE并提交)
幻读(Phantom Read)
同一事务内,两次查询同一范围,行数不同(因其他事务INSERT新行)
MySQL默认隔离级别为REPEATABLE READ,通过MVCC(多版本并发控制)解决幻读问题;而Oracle默认为READ COMMITTED,需显式加锁防幻读。
索引结构与设计哲学
索引是数据库的“导航系统”,但建索引不是越多越好!B+树索引(MySQL InnoDB默认)具有以下关键特性:
- 叶子节点存储完整数据:聚簇索引的叶子节点即数据页,非聚簇索引(二级索引)叶子节点存储主键值
- 有序性与平衡性:所有叶子节点在同一层,支持范围查询与排序
- 最左前缀原则:复合索引(a,b,c)可支持查询条件为(a)、(a,b)、(a,b,c),但无法单独支持(b,c)
数据库原理答案改写中必须强调:索引是空间换时间的典型实践。每增加一个索引,INSERT/UPDATE/DELETE性能下降约15%,但SELECT可提升10倍以上。设计原则如下:
✅ 高选择性字段优先建索引(如user_id、order_no)
✅ 复合索引按“高频查询字段+排序字段+等值条件字段”排序
❌ 避免在低基数字段建索引(如性别、状态,取值仅2~3种)
❌ 避免在频繁更新的字段建索引(如点赞数、浏览量)
分库分表与高可用架构
当单表数据超500万行,查询性能会显著下降。此时需考虑水平拆分:分库(按业务)+ 分表(按范围/哈希/时间)。
例如电商订单表,常见拆分策略:
- 按用户ID哈希分表:user_id % 128 → 确保订单均匀分布
- 按订单时间分表:月度分表(orders_202401),适合查询近期数据的场景
- 全局ID生成器:Snowflake算法生成唯一ID,避免跨分表ID冲突
分表后面临新问题:跨分表JOIN。解决方案包括:
• 应用层聚合:先查各分表,再内存合并(适合小数据量)
• 建立全局索引表:单独维护user_id→order_id映射关系
• 使用中间件:如ShardingSphere、MyCat,自动路由SQL