文 / STEM Career 编辑部
初级软件岗位被问到系统设计时,常见困难是知道很多组件名称,却讲不清一次请求怎样完成。先从一个小系统建立完整推理,比把缓存、消息队列和微服务全部画上去更有用。
本文使用原创的校园咨询预约案例,从需求、数据、写入冲突讲到失败恢复。它是练习题,不代表加拿大或其他市场所有初级岗位都会考系统设计;准备范围应以职位说明、招聘方通知和当前环节为准。
先确认你要设计多大的问题
面试官说“做一个预约系统”,可能希望讨论产品流程、接口设计或多人同时预约。先问用户是谁、时段怎样提供、一次预约占几个人、是否涉及付款,再确认本轮重点。每个问题都要影响方案,不能只是背一份澄清清单。
本例约定:管理员预先建立固定时段,每个时段只能容纳一名学生;学生可以查看、预约和取消自己的预约;暂不处理支付、候补、跨时段预约和多人容量。身份验证由已有系统提供,设计仍需说明怎样使用身份。
这一范围刻意保持很小。如果后来增加多人容量,就需要重新讨论容量扣减和并发策略,而不能继续声称唯一一个时段标识足以表达全部规则。定义改变时,数据模型和验证也要改变。
用一条用户旅程找出核心职责
学生先看可用时段,选择后提交,系统保存成功预约,再显示确认。学生随后可以查看本人记录,或取消释放时段。管理员管理时段开放状态,不应通过普通学生接口修改别人的预约。
把旅程拆成读取、写入、权限、通知四类职责。最关键的业务承诺是同一个时段不能被两个学生同时占用。页面显示得快不快、邮件好不好看都重要,但不能替代这个承诺。
面试开场可以这样说:“我先保证单容量时段不会重复占用,以及取消后能重新预约。接下来再讨论读取体验和通知失败。”对方能看见你的优先级,也容易指出需要深入的部分。
先画最小的数据模型
本例可以用时段表、当前占用表和操作记录表。时段表保存时段标识、开始与结束时间、开放状态;当前占用表保存时段标识、学生标识、预约标识;操作记录用于追踪创建、取消及请求结果。
当前占用表中的时段标识应当非空且唯一,并引用有效时段。这表达的是当前占用,而不是把所有历史预约都放在同一个唯一约束下。取消时释放当前占用,历史事件保留在另一处。否则保留一条已取消记录,可能错误地阻止下一次预约。
PostgreSQL约束文档可用于核对唯一、非空和外键的语义。本文只借这些基本工具表达教学规则;实际数据库版本、隔离级别与事务实现还需要单独验证。
接口先描述行为再讨论地址
读取时段接口返回日期范围内开放情况;创建接口接受时段标识与请求标识;取消接口针对当前用户的预约。用户标识由服务端认证上下文取得,不能直接相信请求正文里任意填写的身份。
成功响应应包含预约标识和明确状态。时段已被占用、时段关闭、身份无权限、输入格式错误,需要不同的可理解结果。技术细节可以不同,但客户端不能把每一种失败都显示为“再试一次”。
取消也要定义重复调用:对于同一条本人已取消记录,可以返回已取消的结果;对于不属于本人的记录,不能泄露不必要的信息。先讲行为,再选择具体状态码和接口命名,答案会更连贯。
两个人同时点预约会发生什么
假设学生甲、乙都在页面看见时段可用。甲先提交,乙紧接着提交。如果代码只是先查询没有预约,再分别插入,两次查询都可能看见空位。页面按钮变灰,也无法防止另一个客户端同时提交。
在本例模型下,数据库的当前占用唯一约束提供最后的冲突边界。两个写入不能都成功成为同一时段的当前占用。服务端要处理约束冲突,把它转为“时段刚被占用”的结果,而不是任由内部错误直接暴露。
注意这段解释没有声称任何先查后写都错误。查询可以改善体验,却不能独自承担并发正确性。面试中要说清哪个步骤只是提示,哪个机制真正保护业务规则。
开放状态也要放进一致性范围
另一个容易漏掉的竞争是管理员关闭时段与学生创建预约同时发生。若开放检查和占用写入彼此独立,可能在关闭后仍出现新预约。因此需要约定关闭的业务含义,并用相应事务与锁定策略实现。
教学讨论可以先选择:创建预约时锁定相应时段,检查开放状态,再写入占用并提交;关闭操作遵守相同的锁定规则。具体语法和失败重试依数据库而定,不应只画一个“事务”方框就认为所有情况已解决。
如果业务允许已有预约保留而只阻止新预约,也应写清。取消、关闭和删除不是同一个动作。删除被引用的时段会涉及历史记录与外键行为,本例不把删除作为普通管理动作。
网络超时不等于写入没有发生
学生点击提交后,数据库已经保存,但网络在响应返回前断开。客户端如果换一个新请求再次提交,服务端可能无法判断它是不是同一次业务操作。单容量约束避免两个占用,却不能自动给用户返回第一次的预约结果。
可以为一次创建意图生成请求标识,并在服务端记录该标识、用户、请求内容与结果。相同用户使用相同标识重试时,返回已有结果;若标识相同但时段不同,则拒绝这种不一致。请求标识需要按明确范围唯一。
预约写入和请求结果记录应放在同一事务范围考虑,否则可能出现预约成功却没有留下可重试结果的缝隙。记录保留多久、如何处理进行中的请求,也属于方案的限制,初步回答可以列为需进一步确定的问题。
邮件通知与预约结果分开
发送确认邮件可能失败。如果数据库提交成功而邮件失败,不能直接告诉学生“预约失败”并让其再约。页面和查询接口应以已保存的预约状态为准,再单独处理通知状态。
一个后续改进是可靠记录待发送通知,再由后台重试。你需要解释任务如何被记录、重试如何避免重复副作用,而不是仅说“加一个消息队列”。用户查看预约时仍应能得到真实结果。
本例最小版本可以先保留站内确认并记录通知失败,明确没有实现自动重试。演示范围写清,就比画出尚未实现的基础设施更可信。面试中也可以说明下一步优先补哪一条可靠性链路。
读取可以稍旧 写入必须重新判断
可用列表在用户打开后就可能过时。即使不使用缓存,另一个人也可能在下一秒抢先预约。界面应允许创建时返回冲突,再提示刷新或选择其他时段,不能承诺看见空位就一定成功。
若引入缓存,说明它用于哪些读取、可容忍多旧、怎样失效,以及哪些决定必须回到真实存储。不要用缓存里的“可用”直接作为成功依据。本例没有规模证据,不需要为了显得高级而默认分布式缓存。
容量讨论也应先给假设,例如活跃用户、一次开放的时段数、集中提交时刻。假设是设计输入,不是已经测得的性能。没有负载实验,就不能将课堂估算写成系统实际承载量。
时间与权限别留到最后
开始和结束时间需要统一解释,包括存储时区、展示时区、夏令时与过期边界。练习可以先约定统一时区并只使用预建固定时段,再说明跨地区扩展需要检查哪些情况。
权限测试至少覆盖学生查看与取消本人预约、不能取消别人预约、管理员关闭时段的边界。即使系统在本地演示,也不应该让任意用户通过更换标识操作他人记录。
日志记录请求标识、业务结果和必要的错误信息,避免把私人身份资料完整写入。日志用于调查,并不自动证明业务正确;还要将观察与数据库状态、请求结果放在一起核对。
把测试写成能推翻方案的场景
先写正常预约和取消,再写同一时段同时提交、同一请求超时重试、重复取消、关闭时段后提交、使用别人的预约标识。每个场景给出预期,而不是只写“测试通过”。
| 情景 | 应观察的结果 |
|---|---|
| 两人竞争同一个单容量时段 | 最多一个当前占用,另一请求明确冲突 |
| 相同请求标识与相同内容重试 | 返回同一次预约结果 |
| 相同标识换了内容 | 不能悄悄执行另一项预约 |
| 邮件发送失败 | 已成功预约仍可查询,通知单独处理 |
| 本人取消后另一人预约 | 历史保留,当前占用可被重新建立 |
如果只在单线程里连续调用,不能称为已经验证并发。练习可以先推演竞争,再做实际并发试验,并记录环境与限制。不要把设计推理、本地验证和生产可靠性混成同一种证据。
用一张图讲清取舍
画学生端、预约服务、数据库三部分就可以开始,再用箭头标出读取与创建。沿一次创建请求说明身份、时段检查、冲突保护、保存和返回;通知放在单独分支,避免与预约承诺混在一起。
对方问“为什么不用更多服务”时,回应当前规模与维护约束,并指出什么时候现有设计会遇到瓶颈。能够说明暂时不引入某个组件的理由,也是设计能力,而不是知识不足的自动证据。
可参考Microsoft技术面试指导理解技术沟通的准备方向,但具体环节依招聘通知。系统设计练习需要的是连续推理,不是复制某家企业的固定架构答案。
用一个规则变化检验设计
现在将每时段容量从一人改成两人。先不要直接删除唯一约束,再让客户端自己数人数。需要重新定义容量表示、同时扣减的原子性、取消释放与重试结果。原先的一时段一占用模型,不能不加修改就声称适用。
可以先在纸上推演已有一个预约时,两人同时争取最后一位的过程。你希望最多新增一份占用,另一请求得到容量不足;随后推演其中一个请求响应丢失后重试,确认不会再次扣减。具体实现需要按所选存储验证。
这项变化用于检查你是否理解原方案为什么成立。能够指出哪些规则与结构要变,比在原图旁边再加一个服务器更能说明设计推理。
带一份自己的设计来讨论准备重点
先独立画图并录一段讲解,检查自己能否解释同一时段竞争与超时重试。这两个问题如果说不清,继续增加组件不会解决根本困难。若能讲清,再加入容量变化或候补规则,看模型如何调整。
点击本页按钮添加蒸汽顾问微信,可以说明目标岗位、收到的面试说明,以及最难回答的追问。带上自己的图和一次真实练习记录,方便讨论需要补数据库基础、设计思路还是现场表达。
蒸汽技术面试与求职辅导可围绕目标岗位安排练习与反馈。尚未确认的考核形式不必提前猜成大型架构面试,也不需要把初级练习包装成已经运行的商业系统。
常见问题(FAQ)
初级岗位都考系统设计吗?
并非统一要求,应核对岗位与招聘通知。
页面按钮变灰能避免重复预约吗?
不能独自保护并发写入,需要服务端与存储的正确性机制。
邮件失败表示预约没有成功吗?
要以保存的预约状态判断,通知结果单独处理。
- Constraints· PostgreSQL Global Development Group
- Technical interviews· Microsoft Careers
添加顾问微信,说明目标岗位与面试要求,带一张自己的设计图,讨论需求、数据关系与现场表达。
