ARTICLE DETAIL

资讯详情

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

神策数据秋招笔试指南:数据链路、算法与SQL考点全解析

神策数据秋招笔试指南:数据链路、算法与SQL考点全解析 神策数据2023秋招技术岗第三批笔试是不少准备进数据方向的同学绕不开的一场考试。神策这家公司大家应该不陌生做用户行为分析、智能运营、CDP起家技术栈以Java/Go后端、ClickHouse、Kafka、Spark/Flink这套为主笔试的出题思路也紧紧围绕数据链路这四个字展开。我是2023年秋季投递的收到了第三批笔试通知当时身边很多人都在冲大厂我却对这家做数据产品的中型公司很有好感。这场笔试做完之后最大的感受是它不像大厂那样喜欢堆难题偏题而是更看重你能不能从数据采集到数据应用这条链路上把问题想通。这篇文章我就把整场笔试的题型结构、考点分布、具体题目解法、备考方案全部分享出来里面包含大量我事后复盘得出的经验希望能给后面投递神策的读者一个清晰的方向。先说明一下由于笔试内容涉及保密要求下面的题目和考点都是基于我个人的回忆和常见题型整理还原的不能保证和实际考题一字不差但考察的知识面和难度区间是完全可以参考的。1. 神策笔试背后的技术底牌为什么出题方向是这样投递神策之前我建议你先搞清楚这家公司的业务到底是什么否则笔试很容易抓不住重点。神策数据的核心产品是用户行为分析平台做的是从埋点采集用户行为数据到数据清洗、ETL、建模再到多维分析、漏斗转化、用户分群、智能运营这样一整套东西。所以它本质是一家既做前端SDK采集、又做后端大数据处理、还要做上层分析应用的公司。这个业务背景直接决定了笔试的出题方向。我的判断是神策技术岗笔试有两条主线数据链路主线从数据埋点、上报、采集到消息队列、离线/实时计算、数据仓库建模再到数据查询和应用分析。这条链路涉及的每个环节都可能成为笔试考点。基础编码主线算法与数据结构、Java/Go编程基础、数据库SQL、操作系统和计算机网络的基本概念。为什么是这两条主线因为神策的产品形态决定了它的技术团队需要做一个看起来简单但实际很重的事情用户在你的网站或App上点了一下按钮这个行为数据要在几秒内完成上报、清洗、存储并且支持分析师在后面做各种维度的即时查询。这个场景对后端服务的高并发写入、数据的一致性、实时计算和离线计算的配合都有很高要求。所以如果你把神策的笔试当成一场普通的大厂笔试来准备只刷LeetCode的话方向就偏了。神策的算法题难度总体不算高我遇到的题目基本在力扣中等题难度之下更侧重字符串处理、模拟、哈希表、贪心这类常见题型很少出那种要花40分钟才能推导出状态转移方程的动态规划压轴题。相反SQL题和数据链路设计题的分量很重而且和公司的实际业务结合得非常紧密。提示准备神策笔试有个高效的切入方式——先去他们的官网或开发者文档把埋点数据从采集到展示的完整流程读一遍再想想每个环节用到了哪些技术和组件。我备考时就是先做了这个功课后面做数据链路设计题时思路清晰了很多。2. 整套试卷的结构拆解题量、时间分配和板块权重2023年秋招技术岗第三批的笔试我的整体感受是时间偏紧题目数量比想象中多。当时我收到的是线上笔试链接整个考试时长120分钟题目的分布和大致权重如下板块题型题量大致分值占比我的时间分配计算机基础选择题10道左右20%25分钟算法编程在线编程题2-3道30%55分钟数据库SQL编写SQL2道左右25%20分钟数据链路/系统设计简答/设计题1-2道25%20分钟这个板块结构是我复盘后整理的不同批次、不同岗位方向可能会有差异比如后端开发和数据开发岗的侧重点就不同但整体框架应该是接近的。这里我想提醒几个重要的细节第一选择题覆盖的范围非常广Java基础、JVM内存分区、并发编程、操作系统死锁、网络TCP三次握手、HTTP状态码都可能遇到。我印象比较深的一道题是关于ConcurrentHashMap在JDK 8中如何保证线程安全性的考的是CAS加synchronized锁桶机制。这类题不难但需要你对Java并发有基础性的了解光靠背八股不行要真正理解原理。第二算法题和SQL题没有自动判题的即时反馈。我用的是牛客网在线笔试系统提交后不会立刻显示通过率需要等考试结束后统一判定。所以在编程题上不要追求一遍完美提交而是尽量保证代码逻辑完整、边界条件处理到位能写的注释写清楚。我当时有两道算法题第一道刚写完就发现了一个边界漏判的地方赶紧补上但因为没有反馈心里其实一直没底。第三时间分配非常关键。120分钟看起来不少但如果选择题磨太久后面两道算法题加SQL题很容易时间不够。我的建议是选择题单题不超过2分钟拿不准的直接标记跳过算法题一道最多25分钟超过这个时间还没有完整思路果断换下一道SQL题一般15分钟内能搞定因为考点相对固定。这样算下来整体时间会留出10分钟左右的缓冲可以用来检查代码是否有低级笔误。从分值占比来看真正拉开差距的是算法题和数据链路设计题。选择题大家水平接近SQL题只要认真准备过基本都能写出来算法题和数据链路题才是区分度所在。3. 基础题中的高频考点选择题最容易丢分的三个位置选择题虽然分值不高但胜在题量大错得多了同样伤不起。根据我的回忆和复盘神策的选择题考点有比较明显的偏好下面这三个位置是丢分重灾区。3.1 Java并发与集合框架的底层机制神策的后端大量使用Java所以并发编程是选择题的常客。考过的方向包括HashMap在JDK 7和JDK 8之间的区别。JDK 8中引入红黑树链表长度达到8且数组容量大于64时转为红黑树这个阈值条件很多人背了但容易忽略后者。ConcurrentHashMap保证线程安全的机制。JDK 8放弃JDK 7的Segment分段锁改用CAS配合synchronized锁住数组的每个桶节点锁粒度更细并发度更高。volatile关键字的两层语义保证可见性和禁止指令重排但不保证原子性。备考建议不要只背结论要去理解为什么这样设计。比如ConcurrentHashMap为什么在扩容时依然能支持并发读因为在扩容过程中读操作会通过ForwardingNode转发到新数组这个机制理解了题目换个问法也能答出来。3.2 操作系统和计算机网络的基础概念操作系统和网络的知识点相对零散但题目比较直接不会绕弯子。印象里有几道进程和线程的区别进程是资源分配的最小单位线程是CPU调度的最小单位同一进程的多个线程共享进程的地址空间和资源。死锁的四个必要条件互斥、占有并等待、不可剥夺、循环等待。考的时候一般是给一个场景问破坏了哪个条件。TCP三次握手和四次挥手服务端在收到FIN后进入CLOSE_WAIT状态四次挥手中的TIME_WAIT出现在主动关闭连接的一方持续时间为2MSL。为什么需要TIME_WAIT因为要保证最晚到达的报文段在网络中消失避免端口复用时新旧连接混淆。注意这些基础概念不难但容易在细节上出错。比如TIME_WAIT是出现在主动关闭方还是被动关闭方这是选择题里很常见的坑我见过不少人在这个选项上翻车。3.3 数据库事务隔离级别与索引失效场景数据库部分主要考MySQL的InnoDB引擎。高频考点有事务隔离级别读未提交、读已提交、可重复读、串行化。MySQL默认的RR可重复读级别通过MVCC解决了不可重复读问题但还是存在幻读问题需要依赖间隙锁来进一步解决。索引失效的典型场景对索引列使用函数、隐式类型转换、OR条件中有非索引列、左前缀模糊查询等。聚簇索引和二级索引的回表操作InnoDB中主键索引的叶子节点保存整行数据二级索引的叶子节点保存主键值查询时先查二级索引找到主键再回表查完整数据。其实神策的SQL笔试重点不是考这些理论而是直接让你写SQL但选择题里的数据库理论题也不能忽视毕竟它是和SQL题分开独立考察的部分。4. 算法编程题的实战拆解从题干分析到完整代码算法题是整场笔试里让人心里最没底的部分。神策的题目风格偏实用题干会包装成一个实际业务场景但本质还是经典的算法题。我遇到的三道题我把它还原成可练习的题型4.1 字符串压缩并输出压缩次数这道题的原型应该很多人都见过给定一个字符串把连续出现的相同字符压缩成字符出现次数的形式。比如输入aabcccccaaa输出a2b1c5a3。如果压缩后的字符串长度不小于原字符串返回原字符串。这个题的考察点在于字符串遍历和边界处理。核心逻辑就是一次遍历记录当前字符和出现次数遇到不同字符时把上一次的计数拼接到StringBuilder里。public String compressString(String S) { if (S null || S.length() 2) { return S; } StringBuilder sb new StringBuilder(); char cur S.charAt(0); int count 1; for (int i 1; i S.length(); i) { if (S.charAt(i) cur) { count; } else { sb.append(cur).append(count); cur S.charAt(i); count 1; } } sb.append(cur).append(count); String res sb.toString(); return res.length() S.length() ? S : res; }这道题有个容易忽略的点题目要求如果压缩后字符串长度不小于原字符串返回原字符串很多人写完压缩逻辑就忘了这个判断。我当时的做法是先算一下压缩后可能的长度小于原长度才走压缩流程否则直接返回原字符串这样可以避免无意义的拼接。这里推荐直接用上面代码的方式最后统一判断更不容易出错。4.2 数组中的多数元素这道题完整还原出来其实就是LeetCode 169给定一个大小为n的数组找到其中的多数元素多数元素是指在数组中出现次数大于n/2的元素。我印象里题目包装成了统计各渠道来源的用户数找出占比超过一半的渠道之类的场景。最优解是摩尔投票法时间复杂度O(n)空间复杂度O(1)。核心思路是不同元素互相抵消最后剩下的就是候选多数元素。public int majorityElement(int[] nums) { int candidate nums[0]; int count 1; for (int i 1; i nums.length; i) { if (nums[i] candidate) { count; } else { count--; if (count 0) { candidate nums[i]; count 1; } } } return candidate; }为什么这道题放在神策笔试里值得重视因为它后面有个延伸问题如果只是找出现次数超过1/3的元素怎么办那就需要用两个候选人的摩尔投票变种。我在准备阶段把这两种情况都练了如果笔试时有明确的延伸考查完全能接得住。4.3 任务调度的最短时间这道题对标的是LeetCode 621任务调度器。给定一个字符数组tasks表示CPU需要执行的任务列表每个字母代表一种不同种类的任务任务可以按任意顺序执行每个任务执行时间为1个单位在任意两个相同种类的任务之间必须有长度为n的冷却时间计算完成所有任务所需要的最短时间。这道题的正确思路是贪心加数学推导。先统计每个任务出现的次数找到最大次数maxExec和具有最大次数的任务数量maxCount答案是(maxExec - 1) * (n 1) maxCount但要注意这个结果不能小于任务总数所以最终结果要取max(公式结果, tasks.length)。public int leastInterval(char[] tasks, int n) { int[] freq new int[26]; for (char c : tasks) { freq[c - A]; } int maxExec 0; for (int f : freq) { maxExec Math.max(maxExec, f); } int maxCount 0; for (int f : freq) { if (f maxExec) { maxCount; } } int res (maxExec - 1) * (n 1) maxCount; return Math.max(res, tasks.length); }这道题如果没做过原题考场上临时推导会花不少时间。我当时属于做过类似题型的看到题干马上就能联系到任务调度器模型前后大概12分钟就写完了。这也说明了刷题的重要性——不是背代码而是看到场景能快速映射到已知模型。提示神策的算法题整体难度不会很离谱建议备考时把LeetCode中哈希表字符串贪心单调栈这几个标签下的中等题刷透。尤其要练字符串类题目笔试很容易出这类场景包装题。5. SQL题神策这类数据公司的高频送分题与踩坑点做数据分析和用户行为数据产品的公司SQL的考察是躲不掉的。神策的SQL题不考特别偏门的花活但很看重窗口函数和留存计算这类实际业务中高频使用的技能。我笔试时遇到的两道SQL题一道是统计留存率一道是计算用户首单时间。5.1 次日留存率计算从建表到窗口函数完整写法题干大概是有一张用户登录行为表user_login包含user_id、login_date两个字段要求计算每天的次日留存率。次日留存率的定义是在某天登录过的用户在第二天依然登录的比例。我当时的答题思路是先按天和用户去重再用DATE_ADD或者DATEDIFF关联前一天的登录用户最后通过连表统计。SELECT a.login_date, ROUND( COUNT(DISTINCT b.user_id) / COUNT(DISTINCT a.user_id), 2 ) AS retention_rate FROM ( SELECT user_id, login_date FROM user_login GROUP BY user_id, login_date ) a LEFT JOIN ( SELECT user_id, login_date FROM user_login GROUP BY user_id, login_date ) b ON a.user_id b.user_id AND b.login_date DATE_ADD(a.login_date, INTERVAL 1 DAY) GROUP BY a.login_date ORDER BY a.login_date;这里有几个关键点为什么要先做GROUP BY去重因为原始登录表可能一天有多次登录记录如果不先去重留存率会算错。这是一个实际业务中非常常见的坑。为什么用LEFT JOIN因为要保证第一天的用户即使第二天没有登录也能被统计到这样才能正确计算分母。分母是a表中的用户数分子是关联到b表的用户数两个都用了COUNT(DISTINCT)来确保去重。如果笔试允许使用窗口函数还可以用COUNT(DISTINCT)配合窗口函数来实现更复杂的留存计算但基础版的留存率用上面这个写法已经完全够用。从我做题的经验来看SQL题追求的不是炫技而是逻辑正确加代码规范能在限定时间内写出来才是最实际的。5.2 计算每个用户的首单时间ROW_NUMBER的经典用法这道题的场景是有一张订单表orders包含user_id、order_id、pay_time、order_amount等字段要求找出每个用户的第一笔有效订单的支付时间。用窗口函数是最直观的解法SELECT user_id, pay_time AS first_pay_time FROM ( SELECT user_id, pay_time, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY pay_time ASC) AS rn FROM orders WHERE order_status paid ) t WHERE rn 1;这道题的考察重点很明确窗口函数ROW_NUMBER()的PARTITION BY和ORDER BY用法。不少人会在这个基础上犯一个错误——在WHERE子句里直接使用窗口函数但SQL的执行顺序中WHERE是在SELECT之前执行的窗口函数在WHERE之后才生效所以必须先查出来再套一层子查询过滤。还有一个变体是求每个用户的最近一次消费金额、消费次数等只要掌握了ROW_NUMBER、RANK、DENSE_RANK的区别基本都能应对。RANK在遇到并列时会跳过名次DENSE_RANK不会跳过这两个的差异也是笔试中常考的细节点。5.3 埋点事件表中的多维统计场景除了上面两道具体题目我还想说说另外一类很值得准备的SQL题型——埋点事件表的统计。神策的业务核心就是埋点事件所以笔试或面试中很可能出现类似下面的题目一张事件表event_table包含user_id、event_name、event_time、page_url、device_type、city等字段。统计每天各渠道的活跃用户数SELECT DATE(event_time) AS dt, channel, COUNT(DISTINCT user_id) AS active_users FROM event_table GROUP BY DATE(event_time), channel ORDER BY dt, channel;再比如统计漏斗转化从页面A到页面B再到下单的转化率。这需要用多条条件聚合或者连表来实现。对于这种题建议在备考阶段多练一些GROUP BY结合条件聚合的写法比如用SUM(CASE WHEN ... THEN 1 ELSE 0 END)来在一条SQL里统计多个埋点事件的触发人数。注意SQL题一定要在本地数据库或者线上SQL练习平台亲手敲一遍纯看答案是记不住的。我当时用了DataGrip连接本地MySQL把笔试常见的留存率、漏斗、分组TopN这几类题全部自己建表造数据跑了一遍这比看一百道题解析都管用。6. 数据链路设计题埋点数据从采集到展示的完整答题思路这应该是整场笔试里最有神策特色的一道题也是最容易让没有数据架构经验的人卡住的部分。我遇见的题目大致是假设你负责为神策设计一套用户行为数据采集系统支撑千万级日活用户每天收集的埋点数据量达到数十亿条请画出整体架构并说明各环节的关键设计。这道题考的不是你背过多少框架而是你有没有真正理解一条数据从客户端产生到最终在分析产品里被分析师看到中间经过了哪些环节、每个环节解决了什么问题。我用的是数据链路五段式的答题框架数据采集端SDK埋点、日志上报数据传输层Kafka消息队列削峰填谷数据存储与计算实时和离线两条链路数据建模层数据仓库分层数据查询服务层OLAP引擎对外提供分析能力6.1 数据采集端的核心矛盾可靠性与实时性的取舍客户端SDK负责采集用户行为事件包括点击、浏览、页面停留、App启动等然后打包上报。这个环节的核心矛盾是既要保证数据不丢又不能因为频繁上报拖垮用户设备和公司服务器。我的答题思路是SDK内置本地缓存队列事件先在本地合并和压缩按批次上报到数据接收服务。上报失败时采用指数退避策略重试超过重试次数先落本地磁盘等网络恢复后再补报。这里刻意提到本地缓存上限的设计——如果缓存满了优先丢弃最早且非关键的事件避免SDK无限占用用户设备存储空间。这一块虽然实际项目中不一定由笔试者来设计但能答出本地缓存、批量上报、失败重试这三个关键词已经能说明你对采集链路有基本认知。6.2 Kafka在链路中的作用为什么必须有消息队列这一层数据接收服务接收到事件后不直接写数据库而是先写入Kafka。这是整个架构中最关键的一层设计。削峰填谷业务高峰期的上报量可能是平时几十倍直接打数据库会压垮系统。Kafka作为缓冲层让后端消费服务按照自己的处理能力来拉取数据。解耦生产者和消费者数据采集和后续的ETL、实时计算、数据仓库导入互不影响任何一方出故障都不会导致整个链路瘫痪。多消费组支持离线数仓导入、实时计算引擎、实时监控报警可以各自消费Kafka中的同一份数据互不干扰。我在答题中重点强调了分区键的选择尽量把同一个用户的事件路由到同一个分区保证事件处理的时间顺序。如果同一个用户的行为事件分散在不同分区实时计算里的session分析就会出现乱序。6.3 实时与离线双链路Lambda架构的取舍在存储与计算层我建议答出实时和离线两条链路并存。离线链路负责全量数据的批处理通常使用Spark或Hive在大数据集群上运行产出各类汇总指标实时链路负责秒级甚至毫秒级的数据处理比如Flink流式计算负责实时PV/UV统计、实时漏斗分析等。这种双链路架构本质上就是Lambda架构。它的好处是离线数据准确但延迟高实时数据响应快但可能有微小误差两者互补。我当时的答法是把离线链路作为主数据通路实时链路作为辅助同时提到如果后续成本允许可以尝试Kappa架构——用Flink同时支撑实时和准实时两种场景统一计算框架。这个加分项不需要太深入但能体现你对架构演进的思考。6.4 ClickHouse为什么是数据分析场景的答案查询服务层用什么引擎神策这类对多维分析要求极高的产品答案大概率是ClickHouse。我在答题中写的是由于神策业务需要支持海量维度的即时筛选与聚合需要列式存储数据库ClickHouse的列式存储加上向量化执行引擎能极大提升聚合查询效率。这里我额外补充了一个冷门但重要的点宽表设计。在ClickHouse中行为数据常常会按用户维度或者会话维度打平成宽表一个用户一行记录包含该用户近期的关键行为特征。这样设计是因为ClickHouse在单表聚合上性能极强但多表JOIN能力相对薄弱宽表可以规避这个劣势。这个细节我当时写了因为确实想展示自己理解分析型场景的存储特性。如果想答得更有深度还可以补充数据接收写ClickHouse的时候要设计合理的分区和排序键一般用事件时间和用户ID作为复合排序键这样同一用户的时序数据在物理存储上尽量连续查询时磁盘扫描量更小。6.5 数据一致性保障一次上报带来的重复问题我还想特别强调一个点数据链路设计题中比架构更要命的是数据一致性。埋点上报是一个典型的分布式场景客户端可能因网络波动重复上报服务端可能因超时重试导致同一条事件被处理多次所以幂等机制是必须提到的。我在答题里给了一个方案每条埋点事件在客户端生成全局唯一的event_id服务端接收后把event_id和对应的消息偏移量写入Redis去重表消费端在处理Kafka消息时先查Redis确认没有处理过该event_id。如果重复直接丢弃。这样即使Kafka出现消费重复或者客户端重复上报也不会重复计算。这个点如果在笔试中能写出来含金量很高。因为大部分考生只关注架构组件很少有人会把数据质量和精确一次这种落地的工程问题放在设计里考虑而这恰恰是神策这类数据公司最看重的。7. 备考时间线与踩坑复盘那些不会写在面经里的细节最后这部分我按自己的实际备考过程做一个时间线和踩坑总结给准备投递神策的同学一个可以直接照搬的参考节奏。我当时的备考周期大概是三周大致安排如下时间段主要任务具体内容第一周刷算法基础题LeetCode热题100分类刷重点练字符串、哈希表、贪心、栈队列每天固定5道题周末复盘错题第二周补SQL和大数据组件概念每天2道SQL题留存率、漏斗、TopN看Kafka、ClickHouse、Spark/Flink核心原理读神策官方技术博客第三周模拟笔试与查漏补缺找往年的秋招笔试题做限时模拟把选择题涉及的Java并发、操作系统、网络基础快速过一遍这次备考和笔试过程中有一些让我印象深刻的坑写在下面第一个坑是过分依赖IDE而不练手写代码。平时刷题都在IDE里有自动补全和代码提示但牛客网笔试系统的编辑器只有非常基础的语法高亮没有自动补全。我在考前一周开始用手写板或者记事本练代码把常见算法题的模板反复默写才在笔试时适应了这种裸写代码的环境。第二个坑是SQL只背语法不建表造数据。我第一次做留存率SQL题时脑子里有概念但写出来不放心因为不知道DATE_ADD用得对不对、结果是否符合预期。后来我在本地MySQL里建了一张模拟登录表往里面插了50条模拟数据跑了一遍后终于对这类SQL的执行逻辑有了实感。这个习惯在后来的笔试中帮了大忙。第三个坑是时间分配上的失误。我第一次模拟笔试时在一道算法题上死磕了40分钟导致后面的SQL题和设计题时间不够整张试卷分崩离析。后来我给自己定了规矩编程题25分钟没有完整思路就跳过先把能拿分的题目全部拿到手。神策的笔试题目量不算少把会做的都做对比在一道难题上耗到天荒地老重要得多。第四个坑是忽略了对公司产品形态的调研。笔试前我只刷题对神策具体的产品线和技术博客看得太少。后来做完数据链路设计题才发现如果提前读过他们的埋点采集文档答这道题会顺手很多。好在我的知识储备比较全面加上对通用大数据架构的把握还是答了个大概。但对于后面要参加笔试的人我的建议是务必花两小时把神策官网的开发者文档和技术博客通读一遍尤其是数据接入数据模型查询引擎这几个部分。8. 最后一个实操经验笔试后的复盘比做新题更重要笔试一旦结束不管感觉好坏我建议你都花完整的时间做一次复盘。我当时做完第三批笔试后趁记忆还热在文档里把主观题和SQL题的作答思路全部回忆并记录下来。这个过程的价值在于你会发现自己哪些知识点是知道但写不出来哪些是写出来但不确认对这些模糊地带就是下一步要补的地方。复盘时我会重点看这几类问题选择题里模棱两可的去查官方文档或者权威博客把所有选项都搞明白而不是只记住正确答案。编程题里没有AC的重新在IDE里完整地实现一遍跑几个自测用例思考是否还有更优解。SQL题有疑问的在本地数据库里还原题目数据验证自己的SQL是否能跑出预期结果。数据链路设计题对比网上能找到的成熟方案思考自己的设计哪里有漏洞。这套复盘方法不光适用于神策的笔试对后续其他公司的技术面试同样有效。很多大厂的面试官喜欢深挖笔试中的薄弱点你如果提前把这些地方都补上了面试时反而能从劣势变优势。我个人当时在复盘后把数据链路的架构又画了一遍把埋点上报、Kafka、Flink、ClickHouse之间的数据流关系彻底理清这个知识框架在后面好几家公司的面试中都派上了用场。所以说笔试不只是筛选它本身就提供了一个很好的系统学习契机。好好利用它哪怕最后没有拿到神策的offer你在这个过程中的收获也不会辜负这三周的投入。
返回列表