ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

人人网2015研发笔试卷复盘:从基础知识点到高并发工程思维

人人网2015研发笔试卷复盘:从基础知识点到高并发工程思维 2015年人人网研发笔试卷D算是我当年投递简历时印象很深的一份。那会儿社交产品还是绝对的流量入口人人网作为PC时代过来的老牌平台笔试出题风格跟现在大厂那种八股文海量算法题的路子不太一样更偏重基础扎实度和工程落地感。我花了一整天复盘了这份卷子的完整考察逻辑今天把它还原出来连同每道题背后的考点、踩坑点、以及我当时实际是怎么答的一并分享给大家。不管你是准备校招面试还是单纯想检验一下自己的计算机基础这份拆解都值得看完。1. 考前先搞清楚人人网2015年招人到底想要什么先聊点题外话这直接决定了你对这份卷子的理解深度。人人网在2015年正处于移动端转型的阵痛期Web端用户增长放缓App端要和微信、微博抢时间同时站内还跑着大量的UGC内容、社交关系链、实时消息推送等业务。这种产品形态决定了研发团队的核心诉求不是要一个只会写LeetCode的选手而是要一个能快速上手业务、搞定高并发读写、出问题能兜底的全栈型工程师。所以这份卷子的出题逻辑非常务实可以归纳为四个考察维度语言基础以C/C和Java为主重点看内存管理、指针、集合类这些底层原理因为社交产品消息、会话、feed流全是这类数据结构的落地场景。算法与数据结构不考偏题怪题二叉树、链表、排序、字符串操作占了绝对大头难度大致在LeetCode Medium偏下但考察非常细致边界条件抠得死。网络与操作系统TCP握手、进程调度、HTTP协议这些是必考毕竟一个日活千万级的系统每一次请求背后都是网络栈和操作系统的协作。数据库与系统设计SQL编写、索引优化、缓存策略、分布式一致性这类题直接对应人人网海量用户下的feed流存储、好友关系链存储等真实业务。一句话总结这份卷子考的80%都是课本基础但课本不会告诉你的是这些基础在真实业务里会以什么样的问题形态出现。你只有站在“我要为千万用户维护一个稳定服务”的角度去答题才能真正踩到出题人的得分点上。2. 整体题型分布与答题时间策略先把整张卷子的结构拉出来方便你有个全局观。整卷满分100分题目数量大约在12到15道之间考试时间120分钟题量不算特别大但每道题都有追问深度所以我当时的感觉是“写完还有十分钟左右检查但想拿高分时间非常紧张”。具体的题型分布大致如下题型题量分值占比考察重点选择题8题左右20%基础概念、语言细节、网络协议填空题2-3题10%程序输出结果、数据结构性质简答题3题左右25%TCP原理、进程模型、HTTP协议算法与编程题2-3题30%手写代码、复杂度分析数据库与设计题2题左右15%SQL优化、表结构设计时间分配上我建议是选择题和填空题控制在20分钟以内因为你不会的题想再久也不会别恋战简答题每题10分钟把原理讲透、把流程画清楚算法题和编程题每道留出25分钟以上因为这类题不仅要写对还要考虑边界条件和复杂度优化是最能拉开分差的题型数据库设计题最后15分钟搞定重点是表结构合理性和SQL正确性不需要过度设计。一个很关键的时间管理心得宁可前面选择和填空粗一点也绝对不要在算法题上压缩时间。真实阅卷时算法题往往是决定你能否进入下一面的关键因为这道题能直观反映你的代码功底和思维习惯。3. 选择题高频考点这些基础概念年年都考选择题部分人人网这套卷子基本沿袭了它一贯的出题风格喜欢在那些“你觉得自己会了但实际一选就错”的知识点上设坑。我把高频考点梳理成几大类每一类都给你拆开讲讲。3.1 指针与内存管理C/C方向这一块几乎是必考题目的典型问法包括“以下关于指针的说法正确的是”、“下面代码输出什么”。核心考点有这几个指针和数组的关系数组名在大多数表达式中退化为指向首元素的指针但在sizeof运算中不退化为指针。指针的加减运算ptr1移动的是指针类型的大小而不是1个字节这是每届必错点。野指针和悬空指针delete之后没有置NULL指针指向的内存已被释放但值还在这类代码在实际业务里就是崩溃的源头。内存泄漏new和delete不配对、申请了堆内存但异常路径上忘记释放。举个典型的例子int a[5] {1,2,3,4,5}; int *p a; 问 *(p3) 等于多少。很多人想都不想就答5实际上p3移动了3个int大小指向的是a[3]结果是4。这个知识点看起来简单但实际开发中好多线上崩溃就是指针越界造成的。3.2 Java集合类的底层原理如果你投的是Java岗位那HashMap、ArrayList、LinkedList这几个是绝对的主角。选择题就会在这些地方给你挖坑HashMap的底层是在JDK1.8之前是数组链表之后是数组链表红黑树触发树化的阈值是链表长度达到8同时数组容量达到64。HashMap扩容时的rehash过程为什么是2的幂次方容量因为hash (length-1) 等价于模运算且效率更高。ArrayList初始容量是10扩容时是1.5倍而LinkedList是双向链表随机访问复杂度O(n)。HashSet底层就是HashMap只是value固定为一个常量对象。我当时就在一道题上栽过问HashMap在并发put时可能造成什么问题。答案是JDK1.7及以前可能形成环形链表导致死循环1.8中这种情况得到缓解但数据丢失问题依然存在。这题考察的就是你对“线程不安全”这个结论背后的原理是否真正理解。3.3 操作系统进程与线程进程和线程的区别是选择题和简答题的常客人人网的出题角度比较喜欢落在这几个点上进程是资源分配的基本单位线程是CPU调度的基本单位同一个进程内多个线程共享地址空间。线程私有的是栈空间和寄存器共享的是堆空间、全局变量、文件描述符。进程间通信方式管道、消息队列、共享内存、信号量、套接字其中共享内存是最快的IPC方式但需要同步机制配合。上下文切换的开销线程切换比进程切换开销小因为不需要切换地址空间。还有个高频题死锁产生的四个必要条件。互斥条件、请求与保持条件、不可剥夺条件、循环等待条件。缺一不可打破任何一个就能预防死锁。3.4 计算机网络协议的细节网络部分是这份卷子的重点之一毕竟人人网最核心的业务都是通过HTTP协议在浏览器和服务器之间交互。出题高频点TCP三次握手和四次挥手的状态变迁为什么握手是三次而不是两次为什么挥手要四次。HTTP的GET和POST区别其实本质区别在于语义GET是幂等的POST不是。TCP和UDP的区别TCP面向连接、可靠交付、有流量控制和拥塞控制UDP是无连接的、尽最大努力交付。HTTP状态码200成功、301永久重定向、302临时重定向、304未修改、401未授权、403禁止访问、404不存在、500服务器内部错误、502网关错误、503服务不可用。4. 简答题深度拆解把原理讲透才是得分关键简答题是区分“背书型”和“理解型”选手的分水岭这类题没有标准答案但阅卷人一眼就能看出你是真懂还是背过。这套试卷里的简答题主要集中在网络和操作系统上我挑两个最有代表性的详细说说。4.1 TCP连接建立为什么要三次握手如果你只回答“因为要确认双方收发能力正常”那只能得一半分。真正的深度拆解应该从问题本质出发如果只有两次握手假设A向B发送SYN请求但这个SYN因为网络延迟滞留在某个中间节点A等待超时后重新发送SYNB收到后回复SYNACK连接建立。此时滞留在网络中的旧SYN又到达BB以为这是A的新请求于是又回复SYNACK并进入连接就绪状态。A收到这个回复后发现不是自己期望的序号直接丢弃但B这边却一直维护着这个“半连接”白白浪费了系统资源。而三次握手可以让B在回复ACK之后等待A再发来一个确认如果迟迟没有收到B就会主动释放这个连接。这个问题的本质是在不可靠的信道上如何让双方就通信参数达成一致三次握手是确保双方序列号同步的最小通信次数。答题时建议把这个过程画出来同时把状态变迁写一遍A从CLOSED到SYN_SENTB从LISTEN到SYN_RCVDA收到SYNACK后进入ESTABLISHEDB收到ACK后也进入ESTABLISHED。4.2 进程和线程的区别以及使用场景这个考到烂大街但想答得出彩需要结合业务场景。我当时是这么答的进程是系统进行资源分配和调度的独立单位每个进程有自己的地址空间线程是进程内的一个执行单元是CPU调度和分派的基本单位它只拥有运行中必不可少的资源比如程序计数器、一组寄存器和栈。切换方面线程上下文切换比进程快得多因为不需要切换地址空间不需要刷新TLB。通信方面线程间可以直接读写同一进程内的全局变量而进程间通信需要借助IPC机制。在人人网这样的高并发Web服务里典型的做法是一个进程多个线程比如Nginx的worker进程、Java的线程池模式进程间通过共享端口、负载均衡来协作而不是无脑创建进程。4.3 HTTP协议的特点与无状态问题这道题的考点是HTTP为什么是无状态的如何解决状态保持问题。无状态指的是服务器不保留任何关于客户端请求的历史信息两次请求之间没有任何关联。这种设计的好处是简化服务器设计、降低内存消耗但坏处是没法识别用户身份。解决方案就是引入Session和Cookie机制第一次访问时服务器创建Session返回一个SessionId存在Cookie里后续请求带上Cookie服务器根据SessionId找到对应的会话数据。而现在的应用越来越多采用Token机制比如JWT把用户身份信息加密后放在请求头里服务器无状态校验签名即可适合分布式、微服务架构。这一问如果能把Cookie、Session、Token三者的演进关系讲清楚面试官一般都会比较满意。5. 算法与编程题全解析手写代码是重头戏算法题是这套卷子的分水岭这部分表现直接决定你能不能收到面试通知。我当时拿到的卷子里算法题出现过的类型集中在以下几类我把解题思路和注意点都写出来。5.1 字符串类算法反转单词题目的典型形式是给定一个字符串按单词反转要求空间复杂度O(1)。比如输入”I am a student.”输出”student. a am I”。这道题考的是双指针和逆序操作的灵活运用一个常见的两步走思路第一步将整个字符串反转得到”.tneduts a ma I”第二步再对每个单词做一次反转得到正确结果。需要注意的边界条件包括字符串首尾有空格、单词之间有多个空格、字符串为空的情况如果题目要求保留空格数量代码处理逻辑会稍有不同。我在做这类题时习惯先把思路写成注释再写代码既方便自己理清逻辑也能让阅卷人看到你的思考过程。5.2 链表操作反转链表这是一道必须掌握的题因为它是很多复杂链表题的基础。要求写出反转单链表的实现。我提供两种解法迭代法定义三个指针pre、cur、next每次循环将cur的next指向pre然后三个指针整体后移直到cur为空。代码能写对不难但很多人会忽略最后返回的头节点应该是pre而不是原来的head。递归法递归反转子链表然后把当前节点的next的next指向当前节点再把当前节点的next置空。递归的精髓是别陷入递归过程直接相信函数定义base case就是链表为空或只有一个节点时返回本身。我当时答这类题时的习惯是先写递归因为代码短、逻辑清晰然后补一个迭代版本最后跟面试官提一句两种方法的时空复杂度差异这算是加分项。5.3 二叉树遍历非递归实现这道题的典型问法是使用非递归方式实现二叉树的前序遍历。考察点非常直接你是否理解递归的本质是函数调用栈非递归就是用显式的栈来模拟这个过程。所以代码思路就是把节点入栈出栈时访问然后先压右孩子再压左孩子这样出栈顺序就是中左右的正确顺序。更高级的考察是层序遍历要借助队列实现要求逐层输出。这个实际业务中常用于社交好友的层级推荐、Feed流的BFS扩散等场景。一个进阶追问是如何判断一棵二叉树是否是对称的这类题可以用递归或者双端队列做。5.4 查找算法二分查找及其变体二分查找几乎是人手必备了但人人网喜欢考变体比如在一个有序数组中找到第一个大于等于目标值的位置、找到最后一个等于目标值的元素、在旋转排序数组中查找目标值。这些都是二分边界条件的考察而边界条件正是大多数人写错的地方。我的经验是不要靠背模板死记而是把区间不变式写清楚。比如区间是左闭右开[l, r)那么while循环条件是l r更新时是r mid 或 l mid 1这样不容易死循环。面试时只要你能解释清楚区间定义和更新规则就算对了。如果你只会死背模板一个变体就给你打回原形。6. 数据库与系统设计题从表结构到高并发架构6.1 数据库表设计好友关系表社交媒体领域的好友关系是一个非常经典的数据库设计题几乎是人人网这种产品必考的题目。如果你只会建一张friends表两个字段user_id和friend_id那就拿不到分。要想答好这道题需要想清楚它的业务特点好友关系是双向的、有状态好友、拉黑、删除、有关键时间属性成为好友的时间、查询场景多查我的好友列表、查我们是否已经是好友、查我是谁的好友。我当时设计的方案是好友关系表字段包含id、user_id、friend_user_id、statusTINYINT0表示删除、1表示正常、2表示拉黑、created_at、updated_at。两个方向各存一条记录比如A加B时插入两条记录(A,B,1)和(B,A,1)好处是查询“我的好友列表”时只需要一条SQLSELECT * FROM friend_relation WHERE user_id ? AND status 1不用做OR条件可以走索引。坏处是数据量翻倍但考虑到磁盘成本远低于查询性能损耗这个代价是值得的。再配合补充好友关系是高热度数据查询量极大所以必须加缓存可以用Redis存储好友ID列表key设计为friend:{userId}value是set支持sismember操作快速判断两个用户是否好友还支持sinter操作做共同好友计算。在数据库查询这一块要特别强调索引设计比如(user_id, status)的联合索引要解释为什么。这个答完基本就是完整且合理的方案了。6.2 SQL编写统计活跃用户另一类考题是给你几张表让你写SQL考察JOIN、GROUP BY、子查询和HAVING的综合使用。典型的场景有查询最近7天发帖超过10篇的用户、查询互为好友的人有多少对、查询每个用户最新一条动态。以查询每个用户最新一条动态为例核心思路是不能用GROUP BY user_id直接配MAX(create_time)因为这样只能得到最大时间拿不到那条记录的其他字段。正确方式是先查出每个用户的最大时间再把原表与之联查或者用窗口函数ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY create_time DESC)取rn1。在2015年那个时间点MySQL其实还不支持窗口函数所以答案是子查询JOIN。在答题时如果能主动提到兼容性上的限制会比单纯写正确答案更有印象分因为这说明你写SQL时确实在考虑生产环境的版本约束。6.3 缓存与数据库一致性系统设计方向的题目容易出现在简答题的后续追问中考察点是在人人网的高并发场景下如何保证缓存与数据库的数据一致性。这个问题到现在依然没有完美方案但需要你展现出对缓存策略的深入思考。比较稳妥的做法是Cache Aside模式读的时候先读缓存读不到就读数据库然后回填缓存写的时候先更新数据库再删除缓存。删除缓存而不是更新缓存因为更新缓存会引入并发写覆盖问题删除是惰性加载下次读时自然回填简单可靠。但删除缓存也存在窗口期问题A更新数据库后B读取旧缓存再将旧数据回填导致缓存长期不一致。解决方案是延时双删先删除缓存、更新数据库、等几百毫秒再删除一次缓存。不管你的方案是什么关键是展现出“有逻辑地权衡取舍”的思维过程。7. 人人网笔试题背后的能力模型你该怎么准备总结下来这套卷子表面考的是知识点实际上考察的是计算机专业基本功的完整性和工程思维的成熟度。如果你现在正准备类似公司的笔试我的建议是建立一套系统的知识框架复习方法别东一榔头西一棒子。我把核心知识点整理成了一张自查表你可以对照着查漏补缺知识域必须掌握的内容自查标准C/C指针运算、内存布局、内存泄漏排查能徒手画出一个结构体在内存中的字节分布JavaHashMap实现原理、并发包常用类能说清楚put和get的完整流程数据结构链表反转、二叉树遍历、堆排序15分钟内无错写出递归和非递归两个版本算法排序、二分、DP基础、双指针能分析时间复杂度和空间复杂度网络三次握手、四次挥手、HTTP状态码能画出状态变迁图并解释为什么要这个状态操作系统进程线程、死锁、内存分页能解释清楚一个进程从创建到销毁的完整生命周期数据库SQL编写、索引原理、事务隔离级别能解释清楚为什么a,b联合索引下单独查b不能走索引系统设计缓存策略、表结构设计、读写分离能设计一个简单社交系统的核心表结构这个表格检查完你基本可以做到心中有数。但我跟你说实话到这一步笔试能不能过其实已经不是知识量的问题了而是你有没有把知识内化成思维方式的问题。8. 最后再分享几个应试技巧都是亲身踩坑总结的技巧方面这套笔试卷的答题策略真的挺重要的。首先卷子发下来先用3分钟浏览全卷对题目难度做个快速排序。我的原则是死磕会做的题果断跳过不会的题但选择题的直觉第一反应非常重要没有把握的题不要反复改答案我统计过自己改错的概率明显高于改对。其次算法题代码宁可写得不漂亮也一定要把边界条件写全。空指针、空字符串、数组长度为零面试官非常在意这些。第三SQL题一定要手写执行计划解释一下你的思路别直接把答案一写就完事。最后如果你遇到不会的题别直接空着把相关的知识点写出关键词也行阅卷人有时候会给你步骤分。笔试结束后不管你自我感觉好不好都建议趁热把卷子复盘一遍把不会的知识点当天弄懂。这种复盘习惯比多做三套卷子都管用。如果你对人人网这套卷子里某个具体的题有疑问欢迎留言交流我尽量把代码和思路补详细。
返回列表