日期:2026-09-27
标签:#面试 #MySQL #数据库
难度:中等
来源:牛面 MySQL 题库
答案说明:根据可访问的题目详情改写
一句话答案
1NF强调字段原子性,2NF消除对复合键的部分依赖,3NF消除非主属性间的传递依赖;反范式化是有代价的性能取舍。
面试口语版(约 60 秒)
1NF 要求列值在当前模型中不可再分;2NF 在 1NF 基础上消除非键列对复合候选键的部分依赖;3NF 再消除非键列对键的传递依赖。
范式化减少重复和更新异常,适合交易主数据;反范式化是用额外存储和同步成本换读取便利,应以真实查询负载决定。
原理拆解与场景
例如订单明细 (order_id, product_id, product_name, qty) 的键是 (order_id, product_id),而 product_name 只依赖 product_id,违反 2NF;拆为产品表并保留 product_id 引用。若订单必须展示下单时的商品名称,product_name_snapshot 是明确业务快照,并非无理由重复。
3NF 例子:员工表同时存 dept_id 与可由部门确定的 dept_name,名称变更会造成多行更新;可拆部门表。若读链路频繁 JOIN,可加受控冗余、物化汇总或缓存,并定义更新事件、重试、对账。
关键边界与工程取舍
“原子”由业务语义决定,地址是否拆省市区要看查询要求。候选键不止主键;2NF 的部分依赖问题只在存在复合候选键时才典型。冗余字段不能只加不管,需定义写入权威源和一致性容忍时间。
面试官递进追问
1. 如何举例说明部分依赖和传递依赖?
参考答案: 部分依赖是非主属性只依赖复合候选键的一部分。例如订单明细以 (order_id, product_id) 为键,但当前 product_name 仅由 product_id 决定,应拆到产品表。传递依赖可用员工表说明:employee_id → dept_id → dept_name,部门名经由部门 ID 依赖员工键,宜拆部门表。判断依据是业务中的函数依赖和全部候选键,不能仅凭“表里加了一个自增主键”就认定已经满足所有范式要求。
2. 订单商品名称为什么可能要做快照?
参考答案: 因为订单需要回答“成交当时买了什么”,商品主表回答的是“现在叫什么”。若只实时关联商品名称,商家改名后历史订单、退款凭证和客服记录都会变化。因此下单时保存名称、规格、成交价等业务快照,并保留商品 ID 供追溯;快照通常不随主数据更新。它记录了不同时间语义的事实,不应作为普通缓存参与同步修复。哪些字段冻结、哪些展示最新值,要在业务模型中明确。
3. 冗余字段异步更新失败时怎样发现并修复?
参考答案: 先定义权威字段、冗余字段和允许滞后的时间,再把业务修改与待发送事件放进同一个本地事务,避免“数据库成功但消息没有发出”。消费者按事件 ID 幂等处理,用业务版本防止旧消息覆盖新值,并记录重试次数、失败原因及死信。监控事件积压、最老未处理时间和版本落后量;定期按主键范围对账,发现差异后从权威源重建冗余字段。对账和修复也要带版本校验,避免覆盖并发新写;还应处理删除事件。历史快照则按其冻结语义检查,不能拿当前主数据强行覆盖。
自测
- 合上笔记,用 60 秒复述一句话结论、一个例子和一个边界。
- 完成第 3 个追问,写出你会核对的 SQL、指标或故障证据。
延伸阅读
参考资料
以 MySQL 8.4 为版本基准;官方资料核对日期:2026-09-27。