2023年小满春招基础架构研发岗笔试已经过去一段时间了,但后台一直有学弟学妹在问第三批的题目难度、考点分布和准备思路。老实说,基础架构这个方向在笔试阶段就能筛掉一大批人,因为它考的从来不是刷题量,而是你对“大规模系统是怎么运转的”这件事有没有底层认知。这篇就结合我参加第三批笔试的回忆和复盘,把整套题的结构、核心考点、答题思路和踩过的坑一次性说清楚。
先说这个笔试的定位。小满春招的基础架构研发岗,要的人是能去建设公司底层技术设施的角色,不是写业务 CRUD 的。所以笔试题目设计得很“系统”——单机算法题比例不高,重点全压在分布式、存储、网络、高可用这些硬核领域。第三批的整体感觉是:广度大、深度中等偏上、对工程直觉要求高。你单纯背八股文会做得很难受,但如果你真的自己搭过中间件、排查过线上故障,会发现很多题其实就是在考察你的“肌肉记忆”。
这篇文章适合三类人。一类是准备投递基础架构、中间件、云原生相关岗位的应届生,想提前知道笔试怎么考;第二类是工作一两年的后端开发,想系统梳理一下自己知识体系里的盲区;第三类纯粹是对“架构师到底在干嘛”好奇的同学,这篇文章能让你看到这类岗位的思维模式是什么样的。
1. 笔试整体布局与题型权重
1.1 试卷结构回顾
第三批笔试一共是三种题型:单选题、多选题、编程题。总时长120分钟,题量看上去不算特别大,但实际做下来时间非常紧张,尤其是编程题部分,我给自己留了50分钟,最后依然觉得不宽裕。
题型分布大致是这样的:
- 单选题 20道,每道1.5分,总分30分
- 多选题 10道,每道2分,总分20分
- 编程题 2道,总分50分
这个分值分配很能说明问题:五十分的编程题才是真正的分水岭。前面选择题做得再顺,撑死了也就是个及格线,编程题写不出来基本就告别通过了。所以你们复习的时候一定不要把精力全部押在背诵知识点上,代码能力是要天天练的。
从考点覆盖来看,我事后做了归类,大致可以分成五个方向:
| 考点方向 | 大致占比 | 典型内容 |
|---|---|---|
| 分布式系统与一致性 | 30% | Raft、Paxos、分布式事务、共识算法 |
| 存储引擎与数据库 | 25% | LSM-Tree、B+Tree、缓存一致性、分库分表 |
| 网络与高并发 | 20% | TCP/IP、HTTP/2、连接池、拥塞控制 |
| 高可用与容灾 | 15% | 故障转移、熔断降级、多活架构 |
| 操作系统与底层 | 10% | 零拷贝、IO多路复用、内存管理 |
这五个方向基本就是基础架构岗位笔试的“五大门派”了,后续批次和年份大概率也会围绕这些来出题。你们可以对照着查漏补缺。
1.2 为什么笔试要这么设计
我后来跟小满的学长聊起这批笔试,他说得很直白:基础架构岗要的不是“知道”的人,而是“遇过事儿”的人。选择题考的是知识广度,编程题考的是系统设计落地能力,两者结合才能看出一个人有没有真正的架构sense。
举个例子,选择题里有一道关于CAP理论的题,表面问的是“分区容错性不可妥协时选哪两项”,但实际上题目埋了一个坑——它描述的场景是“一个跨机房部署的数据库集群,双机房网络中断后,如何设计写入策略”。这已经不是单纯背CAP能答对的题了,它考的是你能否把CAP理论落到真实拓扑中去推导。
再比如说多选里那道关于分布式事务的题,选项里有2PC、TCC、本地消息表、Saga。如果只是背过概念,看到TCC和Saga都会选;但题干限定了“要求强一致性,且对吞吐量不敏感”,这时候TCC和2PC才是更优解,Saga由于是最终一致性就不太合适了。这种细微的取舍判断,才是笔试真正想看到的。
2. 核心题目详解与解题思路
2.1 分布式共识算法:从Raft选举到日志复制
第三批笔试涉及分布式共识的题目不少,单选、多选和编程题都有影子。其中一道单选题我记得很清楚,考的是Raft的选举流程。
题目大概是:某个Raft集群有5个节点,当前任期内的Leader宕机了,其中一个Follower的选举超时时间先触发,它发起选举,请问它需要获得几张投票才能成为新Leader?
这题如果只记结论“过半票数就能当选”,很容易算成3票(5个节点,过半是3)。但题目里有个关键的干扰项:这个发起选举的节点会不会给自己投票?答案是“会”。Raft协议里,节点发起选举时第一张票就是投给自己的,所以它实际只需要再获取2张其他节点的票即可。
这类题目在笔试里出现的频率特别高,因为Raft是很多中间件的共识底座,比如 etcd、Consul、Kafka 的某个版本元数据管理,都是基于它。你在复习的时候,不仅要记住“过半原则”,还要能把整个选举流程的细节理清楚。
还有一道多选题,考的是Raft中日志复制的一致性保证。选项里提到“Leader只能追加日志,不能覆盖已有日志”“日志条目一旦提交就不能被删除”“不同节点的日志可以存在分歧但已提交的日志一定一致”“Follower的日志必须和Leader完全一致”。正确答案是前三个。这道题很多人会漏选“不同节点日志可以存在分歧”这个选项,觉得集群日志就是应该完全一致的——但这恰恰是Raft的设计精髓:它允许未提交日志存在分歧,只要保证已提交日志一致即可。
这里顺带说一个我后来自己写Raft实现时踩过的坑:日志复制有个隐性的规则,就是新Leader在提交自己任期内的日志时,必须至少携带一条自己任期内的日志条目,否则不能直接提交上一个任期的日志。原因是为了防止“已提交日志被覆盖”的边界情况。笔试虽然没直接考到这个深度,但这个理解能帮你在做日志相关的选择题时准确判断“可不可以提交”这类选项。
2.2 存储引擎考点:LSM-Tree与B+Tree的全方位对比
选择题里关于存储引擎的题目大概出了三道,全是围绕着LSM-Tree和B+Tree打转。我印象最深的是一道多选,问“以下哪些是LSM-Tree相比于B+Tree的优势”。
我当时选了“写放大为顺序写”“更适合写多读少的场景”“无需像B+Tree一样维护严格有序的页结构”。这几个都是LSM-Tree的典型优势。但选项里有一个干扰项是“读放大更低”,这是典型的错误选项——LSM-Tree的读放大问题比B+Tree严重得多,需要Bloom Filter、层级合并等手段来补偿。
这类题表面在考“两种结构谁好谁坏”,实际上是在考你是否理解它们各自的设计取舍。B+Tree擅长范围查询和点查,因为数据在页内是有序的,但随机写会带来严重的磁盘寻道开销;LSM-Tree通过内存表+磁盘不可变文件的组织方式,把随机写变成了顺序写,写性能自然就上去了,代价就是读路径可能跨多个层级文件查找。
我印象里还有一道单选,问的是“LSM-Tree的MemTable达到阈值后,会转换成什么结构写入磁盘”。答案是“SSTable”。这个基础题反而被好多人答错了,可能是因为大家把注意力都放在了合并策略上,忽略了最基础的数据结构流转。建议复习的时候把“写路径:写内存 → 写WAL → 刷盘成SSTable → 后台合并”和“读路径:查内存 → 查Bloom Filter → 逐层查找SSTable”这两条链路死死刻在脑子里。
2.3 分布式事务:2PC、TCC、Saga的适用边界
分布式事务题在笔试里出现了两次,一次单选一次多选。单选那道题描述了一个跨库转账场景,要求“不能出现资金中间态暴露给用户”,问应该选哪种方案。
这道题的答案是2PC,因为2PC是同步阻塞协议,在提交前阶段锁住资源,外部看不到中间状态。TCC虽然也能保证最终一致性,但confirm和cancel之间是有窗口期的,严格来讲中间态存在。Saga更不用说了,它本身是最终一致性模型,通过补偿来回滚,中间态必然可见。
这里想多说一句:很多人在复习分布式事务时,喜欢死记“2PC强一致、TCC最终一致、Saga最终一致、本地消息表最终一致”,这个口诀本身没错,但一定要结合场景理解。比如TCC本质上是把业务操作拆成Try、Confirm、Cancel三个阶段,Try阶段做资源预留,Confirm阶段真正执行,Cancel阶段做补偿。它的优势是灵活,能解决2PC长时间锁资源的问题,但代价是需要你在业务层实现三段逻辑,开发成本极高,所以面试笔试里一般会把它放在“追求高性能但允许短暂不一致”的场景里。
多选那道题更难一点,题干给了一个跨多个微服务的下单场景,要求说哪些方案能保证数据最终一致。选项里有本地消息表、MQ事务消息、TCC、最大努力通知。正确答案是前面三个。最大努力通知本质上是“不管成不成功只通知一次”,它不带有任何事务性保证,所以不能算。这道题错的人很多,很多人在“MQ事务消息”这个选项上犹豫了——其实MQ事务消息就是利用半消息机制,先发一个broker不可见的消息,本地事务成功了再确认发送,本质上是本地消息表的升级版,当然属于最终一致性方案。
2.4 高并发网络:TCP队列与HTTP/2多路复用
网络方向的题目看起来是在考协议本身,其实是在考“高并发场景下协议怎么表现”。有一道印象很深的单选题,问的是TCP三次握手中,如果服务器端accept队列满了,新连接会发生什么。
答案是“客户端依然完成三次握手连接建立,但服务端不会accept,连接处于established状态,最终客户端超时重传或断开”。这道题考的是半连接队列和全连接队列的区别。我见过很多人在复习时只关注三次握手的流程,却忽略了内核有两个队列在分别管理握手的不同阶段。实际线上排查问题的时候,如果发现大量连接处于ESTABLISHED但应用层没反应,优先就要检查全连接队列有没有打满。
关于HTTP/2也有一道题,问的是“HTTP/2多路复用解决了HTTP/1.1的什么问题”。这题算是送分的,答案是“队头阻塞”。但后面跟了一道多选,问“HTTP/2的多路复用为什么还会有队头阻塞”,这就很有意思了。答案是TCP层的丢包重传:HTTP/2在一条TCP连接上跑多个流,TCP丢包会导致所有流一起等待重传。这个就是HTTP/3改用QUIC/UDP的核心动机之一。笔试能考到这个层次,说明出题人真的希望你有全景视野。
2.5 高可用设计:故障转移与容灾指标
高可用这部分考得比我想象中要细。有一道多选,问的是“在设计一个跨可用区部署的有状态服务时,以下哪些做法有利于故障恢复”。
选项里正确项包括“数据多副本跨AZ同步写入”“客户端连接通过服务发现自动摘除故障节点”“状态数据定期备份且备份文件持久化到独立存储”。错误项是“故障切换时依赖人工介入判断”。我在实际工作当中非常认同“自动化故障转移”这个原则——人肉运维在故障发生时最容易因为紧张误操作,设计系统时就应该把“人工介入”当成一种降级手段而不是主路径。
还有一道计算题,问的是“某服务的可用性目标是99.99%,每个月(按30天计)允许的不可用时间是多少”。答案是4.32分钟。这题虽然简单,但很好的提醒了一个基础架构岗位的基本素养——你要对自己维护的系统SLA有直观量化的感觉。如果不记得这个常数,临时算:每月总分钟数是43200分钟,乘以万分之一的不可用比例,就是4.32分钟。
3. 实操过程与核心环节实现
3.1 我在答题现场的时间分配策略
这部分讲讲我自己在笔试现场的实际操作流程,也分享一些应对这种高压力笔试的时间管理方法。
拿到试卷后,我没有立刻开始做题,而是花了两分钟整体浏览了一下题目分布。这是我一直以来的习惯:先看整体再动手,避免把时间耗在某个分值低但极难的题目上。浏览完发现编程题比较难,我立即决定调整战术——单选题要控制在25分钟以内,多选题控制在20分钟以内,把剩余时间全部砸给编程题。
实际做下来,单选题比我预想中要难一些,花了大概28分钟,有两道题是蒙的。多选题更惨,10道题里我有3道不能完全确定,只能把明显正确的选项选上,拿不准的选不选当时很纠结。多选对我来说一直是噩梦,少选要扣分,多选可能零分,所以我给自己定了个原则:完全不确定的选项不选,保住基础分。
编程题环节,我预留了50多分钟。第一道编程题相对简单,属于“LRU缓存”变体,20分钟写完并自测通过。第二道编程题是一道系统设计+代码实现题,我留了30多分钟,最后还是差点没写完,最后靠注释表达完整体现思路,拿了大部分分数。
3.2 编程题实战:从LRU到高并发缓存设计
编程题第一道是LRU缓存的变体。LRU本身是高频考点,网上全是标准答案,但这次题目做了个小小的改动——在get和put操作之外,额外要求实现一个incr方法,对缓存中某个key的值做原子自增。这其实不算难,核心数据结构和标准LRU一样:哈希表+双向链表。
关键实现点是这样的:
- 哈希表负责O(1)找到节点,双向链表负责记录访问顺序
- get时命中节点要移动到链表头部
- put时若容量已满,先淘汰链表尾部节点
- incr相当于先get再更新值,但注意要调整节点位置到头部
这个题考察的除了LRU本身,还有你在原有结构上扩展新功能的代码组织能力。建议你们练习时不要只背标准LRU,要自己尝试给LRU添加过期时间、批量删除等扩展功能,笔试时面对变体题就不会慌。
第二道编程题就系统设计味道比较浓了。题目要求设计一个支持多机部署的分布式计数器,要求:数据不能丢、支持高并发原子自增、允许最终一致。核心考点是两个:一是自增操作怎么做到原子化,二是数据同步怎么做。
我的设计思路是:数据分片存到多个节点上,每个key固定映射到一个分片(哈希取模),每个分片内是单机内存计数器,写操作先落在本机内存,同时异步写WAL日志保证持久化。跨分片统计时,比如求总数,就需要汇总每个分片的值。由于允许最终一致,跨分片同步可以用定期批量同步的方式。这个题其实不难,但你要在限定时间内写出来,需要你平时就有一定的架构思维。
3.3 多选题的稳准策略:不确定就不选
多选题的计分规则是:全部选对得满分,少选得部分分,多选、错选不得分。这意味着选择题真正的策略是“稳”,不是“狠”。
我给自己定了一个决策规则:对某个选项的正确性判断低于80%,就不选。这个策略在部分正确选项上会损失一些分数,但足够保证卷面分数不会出现断崖式下跌。我见过太多实力不错的人因为多选题贪多,导致整道题零分,最后总分上不去。
多选题还有一个实用技巧:注意选项内部的逻辑关系。如果两个选项描述的是完全矛盾的设计方案,比如“故障转移必须人工介入”和“故障转移应该自动化完成”,那几乎不可能同时正确。这类题其实是出题人在考察你是否具备“逻辑互斥排除”的能力。我在做高可用那道多选时就用到了这个方法,人工介入那条我直接排除了,省了不少纠结。
4. 常见问题与排查技巧实录
4.1 那些年我们一起踩过的笔试坑
考完和几个一起笔试的同学对答案,发现大家的失分点集中在几个地方。我整理成一个避坑清单,你们后续准备的时候可以直接对照。
第一个坑,也是最大的坑:分布式共识和一致性的概念混淆。很多人看到“共识”两个字就想到“一致性”,觉得一回事。实际上Raft、Paxos解决的是“多个节点就某个值达成一致”的问题,而线性一致性、顺序一致性是描述“客户端观察到的数据读写顺序”的语义。这次笔试有一道描述“客户端从不同副本读数据,读到旧值”的选择题,答案跟一致性模型强相关,跟共识算法没太大关系。如果概念混在一起,这道题很容易选错。
第二个坑,是读写放大的方向搞反。选择题里关于LSM-Tree的题,好多人把“写放大更低”当成它的优点。实际上LSM-Tree因为要有后台合并操作,写放大反而是它的痛点之一,它只是把随机写变成顺序写来换取性能。所谓的“LSM写性能好”是相对B+Tree而言的,但它的写放大(一个数据在合并过程中被多次重写)是实际应用中一直在优化的方向。
第三个坑,是HTTP/2和HTTP/3的区别。题目问“HTTP/3为什么能解决队头阻塞”,知道正确答案是“基于UDP,不受TCP丢包重传影响”的人挺多,但有个选项是“HTTP/3的每个流都建立了独立的TCP连接”迷惑性很强。实际上HTTP/3是基于QUIC协议的,而QUIC运行在UDP之上,一条连接里可以复用多个流,丢包时只影响丢包的那个流。如果你只是背了“HTTP/3=UDP”这个结论而不理解底层机制,很容易被这个选项带走。
第四个坑,在编程题里特别常见:LRU的边界条件。比如容量为0时怎么处理?key已经存在时put要不要算作一次访问?缓存满时先淘汰再插入还是插入失败报错?很多人代码主流程写对了,边界没考虑全,只能过部分测试用例。我的建议是,写完后先不要提交,自己在脑内跑这么几个用例:空缓存put、缓存满后put新key、put已存在的key、get不存在的key、容量为1的缓存。这几个用例走通了,基本能覆盖80%以上的扣分点。
4.2 编程题常见报错与调试复盘
我这次笔试第一道编程题其实也踩了个小坑。LRU变体题在实现incr时,我一开始只更新了节点的value,忘了把节点移动到链表头部。结果自测的时候发现,执行incr之后再调用get,返回的值是对的,但是访问顺序不对,后面put新key时淘汰的是错误的节点。
这个bug的排查过程很有意思:表面现象是“淘汰节点不对”,但看代码逻辑,哈希表和链表都有维护,就是value更新后没调整顺序。修正方法就是让incr逻辑等价于一次get再更新值——先命中并移动节点,再更新value。这个思路同样适用于标准LRU的put流程:先尝试get,命中就更新值并移动位置,未命中就插入新节点到头部。
还有一次是第二道分布式计数器的代码,我在初始化分片映射的时候,用了个静态常量列表来模拟一致性哈希环,写死了3个节点。自测没问题,但想着如果扩展节点数的话逻辑就要重构了。最后我在注释里写清楚扩展方案,然后提交了。笔试这种场景下,代码能跑通加上注释体现扩展思路,通常能拿个不错的分数。
4.3 事后复盘与知识补漏
笔试结束后的下一步,一定是复盘。我习惯把做错的、拿不准的题目全部整理到一张表里,每道题写明考点、错误原因、正确思路、关联知识点,然后一个一个排查自己的知识盲区。
我拿这次笔试举例,事后整理出来几个重点补救方向:
- 对HTTP/2队头阻塞和HTTP/3的底层机制理解不够
- 对LSM-Tree合并策略的细节不够熟悉
- 多选题的答题策略太激进,容易导致整题零分
- 编程题的边界条件测试思路不够系统
你们如果不准备笔试,也可以用这个思路来总结自己岗位的基础能力底子。把错误当成扫描仪,一点一点把知识盲区扫出来,然后集中补掉,这才是笔试复盘最大的价值。
5. 备战建议与资源路线
5.1 知识体系怎么搭
如果你距离笔试还有一两个月,时间够用,我建议按“底层原理 → 框架实践 → 真题练习”的路径来复习。
底层原理部分,你需要掌握这几块:操作系统(进程线程、内存管理、IO多路复用、零拷贝)、网络(TCP/IP、HTTP、连接池原理)、存储(B+Tree、LSM-Tree、WAL、缓存)、分布式理论(CAP、BASE、一致性协议、分布式事务方案)。每一块不要只停留在一个名词解释的层面,要能画出完整的数据流图。你画不出来,说明还没理解透。
框架实践部分,根据往年岗位要求来定。基础架构岗,重点推荐看etcd(Raft落地)、Redis Cluster(分布式分片)、MySQL InnoDB(B+Tree落地)、RocksDB(LSM落地)、Kafka(高吞吐消息系统+副本同步)。不用每个都深入源码,但核心机制要能说清楚,比如你对Redis Cluster要理解“槽位分配、主从切换、gossip通信”这三件事。
真题练习部分,可以在牛客、力扣的题库里搜其他公司的基础架构笔试题。做的时候不是看答案,而是要自己写出来,尤其要多选题,不能扫一眼觉得“差不多知道”就过,一定要落到笔头或者文档上,写出选项判断的理由。
5.2 编程题如何针对性训练
编程题是硬功夫,没什么捷径,但可以有针对性的练。基础架构方向的高频题型,我总结下来就是这几类:
- LRU/LFU缓存、带过期时间的缓存
- 分布式计数、限流算法(令牌桶、漏桶)的实现
- 时间轮调度器、延迟队列
- 一致性哈希的代码实现与虚拟节点扩展
- 并发编程相关的题目,比如多线程安全的LRU、生产者消费者队列
这些题目有一个共同点:特别关注并发安全、边界条件和复杂度。写的时候,不要只追求“能跑”,要有意识地去思考:这个数据结构线程安全吗?如果多线程并发put怎么办?如果内存不足怎么办?这种思考习惯不仅对笔试有用,对实际工作更是受用终生。
5.3 模拟演练要有现场感
这里我还想强调一下“模拟演练”的重要性。笔试和平时刷题不一样,它有时间压力,有计分压力,还有打字环境的不确定性。我建议在笔试前至少做三次完整的模拟:设定和真实考试一样的时间,找一个和考试环境相近的安静的独立空间,用编辑器而不是IDE来写代码,全程不查资料。
第一次模拟的目的是摸清自己的水平,不需要刻意控时间,能做完就算赢;第二次模拟目的是优化做题优先级,找到适合自己的时间分配;第三次模拟目的是形成“肌肉记忆”,比如先做选择题还是先做编程题,遇到不会的题是跳过还是死磕,都要在模拟里确定自己的答案。
我自己的感觉是,第一次模拟完特别崩溃,觉得好多知识点都没吃透;第二次开始能控制节奏了;到第三次模拟时,已经能很稳地分配时间并保证正确率了。到了真实笔试,心态也会平稳很多。
6. 一些心里话,送给正在准备的人
写到这里,我觉得最有价值的不是把题目答案给你们列出来,而是想传递一个观念:基础架构方向的学习没有速成的方法,它的核心是对物理世界资源的理解和抽象。
你在学Raft的时候,不要只背“过半投票”,要去想为什么需要过半——因为要保证任意两个多数派之间有交集,所以不会出现两个Leader同时提交不同日志的情况。你在学LSM-Tree的时候,不要只背“顺序写快”,要理解机械硬盘的顺序写比随机写快几个数量级,而同一个思想在Kafka里又出现了一遍。这种“底层逻辑在不同系统中反复出现”的认识,才是基础架构笔试真正想筛选出来的东西。
我也知道,备战过程一定会遇到瓶颈期,就是那种东西越学越多、脑子越来越乱的时候。我在准备笔试的时候也有过这样的阶段,后来发现解决的方法很简单:停下来,画图。把某个系统的读写路径画清楚了,把一条请求从客户端到数据库的全链路画清楚,很多混乱的感觉自然就消散了。
最后再分享一个小技巧。笔试前一周,我复习的重点不是去学新东西,而是把之前整理的错题本、易混概念表反复浏览,尤其是那些“A和B有什么区别”这种对照型的问题,比如B+Tree和LSM、2PC和Saga、Raft和Paxos、HTTP2和HTTP3。把这类对照型知识刻进脑子里,考场上遇到类似的选项就能立刻反应出来,这比临时抱佛脚学新知识有效得多。