Netflix 数据科学家面试实录 2026:真实面经完整复盘
Netflix面试数据科学家面试VO面试真实面经算法题SystemDesign

Netflix 数据科学家面试实录 2026:真实面经完整复盘

Netflix面试第一人称完整复盘:涵盖算法Coding、系统设计、Behavioral面试。还原真实面试对话、高频题目与解题思路,附准备策略与注意事项,助你高效备战Netflix技术面试。

Sam · · 15 分钟阅读

公司:Netflix 岗位:数据科学家 (Data Scientist) 面试形式:Virtual Onsite 结果:Pass → Offer

大家好,我是 Sam。这次分享我参加 Netflix DS 面试的完整经历。Netflix 的 DS 面试和其他大厂有一个很明显的区别:他们不太考传统意义上的 ML 建模题(比如手写梯度下降),而是更看重编码能力、系统设计思维、以及对数据如何驱动业务的理解。整场 VO 下来一共四轮,涵盖 Coding、System Design、Data Modeling 和 Behavioral,每一轮都有深度。

Coding:经典题变体,关键在扎实和表达

Coding 部分整体不难,基本都是经典题的变形。面试官给我出了一道 Command Undo System 的设计题。

题目还原:Command Undo System

题目描述

设计一个支持 undo 操作的命令系统。要求:

  1. 执行命令(execute)
  2. 撤销最近一个命令(undo)
  3. 撤销最近一个带有特定 tag 的命令(undo_by_tag)
  4. 查询当前命令历史(get_history)

面试官:这个 undo system 的 tag 是怎么用的? :tag 是用来给命令分组的。比如一个用户可能执行了多个”保存”操作,每个”保存”都有一个 tag 叫 “save”。调用 undo_by_tag(“save”) 时,应该撤销最近一个带 “save” tag 的命令。 面试官:如果已经执行了普通的 undo,tag 索引需要更新吗? :需要。因为普通 undo 撤销的是栈顶的命令,如果这个命令有 tag,我们需要从对应的 tag 列表中移除它。

from collections import defaultdict
from dataclasses import dataclass, field
from typing import Optional
import time

@dataclass
class Command:
    """单个命令"""
    cmd_id: str
    action: str
    tag: str
    timestamp: float
    is_executed: bool = True

class CommandUndoSystem:
    """
    支持按 tag 回滚的命令系统。
    
    数据结构设计:
    - command_stack: 所有命令的执行顺序栈
    - tag_commands: tag -> 该 tag 下的命令列表(栈)
    
    时间复杂度:
    - execute: O(1)
    - undo: O(1)
    - undo_by_tag: O(1) 均摊
    - get_history: O(n)
    """
    
    def __init__(self):
        self.command_stack: list[Command] = []
        self.tag_commands: dict[str, list[Command]] = defaultdict(list)
    
    def execute(self, action: str, tag: str = "") -> str:
        """执行一个命令"""
        cmd_id = f"cmd_{len(self.command_stack) + 1}"
        cmd = Command(
            cmd_id=cmd_id,
            action=action,
            tag=tag,
            timestamp=time.time()
        )
        self.command_stack.append(cmd)
        if tag:
            self.tag_commands[tag].append(cmd)
        return cmd_id
    
    def undo(self) -> Optional[Command]:
        """撤销最近一个命令"""
        if not self.command_stack:
            return None
        
        cmd = self.command_stack.pop()
        cmd.is_executed = False
        
        if cmd.tag and cmd in self.tag_commands.get(cmd.tag, []):
            self.tag_commands[cmd.tag].pop()
        
        return cmd
    
    def undo_by_tag(self, tag: str) -> Optional[Command]:
        """撤销最近一个带指定 tag 的命令"""
        if not self.tag_commands.get(tag):
            return None
        
        # 找到最近一个带该 tag 的命令
        target_cmd = self.tag_commands[tag].pop()
        
        # 从主栈中移除
        self.command_stack = [
            c for c in self.command_stack if c.cmd_id != target_cmd.cmd_id
        ]
        target_cmd.is_executed = False
        
        return target_cmd
    
    def get_history(self) -> list[Command]:
        """获取已执行的命令历史"""
        return [c for c in self.command_stack if c.is_executed]

面试官追问

面试官:undo_by_tag 的时间复杂度是多少? :当前实现中,从 command_stack 里移除一个元素的复杂度是 O(n),因为需要遍历列表。如果要优化到 O(1),可以用 doubly linked list 代替 list,每个 Command 存储 prev/next 指针。这样删除操作只需要修改前后指针。 面试官:好,那如果用 linked list,tag_commands 里的引用还能直接指向节点吗? :可以。tag_commands 里存的是节点的引用,不是索引。所以从 tag 栈弹出后,拿到的是节点引用,直接在 linked list 上做 remove 就是 O(1)。

拓扑排序变体

另一轮 coding 出了拓扑排序,但是加了约束。

import heapq
from collections import defaultdict, deque

def topological_sort_all(graph: dict[str, list[str]], 
                         all_nodes: list[str]) -> list[list[str]]:
    """
    输出所有可能的拓扑排序结果。
    
    使用 DFS + 回溯,比 Kahn 算法更适合枚举所有结果。
    
    时间复杂度: O(N! * V),N 是节点数
    空间复杂度: O(V)
    """
    adj = defaultdict(list)
    in_degree = defaultdict(int)
    
    for node in all_nodes:
        in_degree[node] = 0
    
    for node, neighbors in graph.items():
        for neighbor in neighbors:
            adj[node].append(neighbor)
            in_degree[neighbor] += 1
    
    results = []
    
    def dfs(path, remaining_in_degree):
        if len(path) == len(all_nodes):
            results.append(path[:])
            return
        
        for node in all_nodes:
            if node not in path and remaining_in_degree[node] == 0:
                # 选择该节点
                path.append(node)
                # 更新入度
                for neighbor in adj[node]:
                    remaining_in_degree[neighbor] -= 1
                
                dfs(path, remaining_in_degree)
                
                # 回溯
                path.pop()
                for neighbor in adj[node]:
                    remaining_in_degree[neighbor] += 1
    
    dfs([], dict(in_degree))
    return results

面试官:如果图很大,所有拓扑排序的结果可能非常多,有优化方法吗? :有几个方向:

  1. 限制输出数量:只返回前 K 个结果,用 BFS 按优先级扩展。
  2. 字典序要求:如果要求按字典序输出,可以用最小堆代替遍历所有节点。
  3. 流式输出:不一次性生成所有结果,用 generator 按需产出。

System Design:考察深度,而不是框架

System Design 这一轮我遇到的题目是设计一个推荐系统的后端服务。

架构设计:Netflix 推荐系统简化版

面试官:请你设计一个视频推荐系统。用户打开 Netflix 首页时,需要展示一排排推荐视频。 :好的。我先确认几个关键指标:

  • DAU 大概多少?假设 2 亿
  • 每个用户看到多少个推荐?假设 10 排 × 每排 15 个 = 150 个推荐
  • P99 延迟要求?假设 < 500ms
  • 推荐需要实时更新还是离线计算?

系统架构

                    ┌─────────────┐
                    │  Client App │
                    └──────┬──────┘
                           │ REST API

              ┌─────────────────────────┐
              │     API Service         │
              │  (聚合层 / BFF)          │
              └──┬──────────┬──────────┬┘
                 │          │          │
                 ▼          ▼          ▼
        ┌────────────┐ ┌─────────┐ ┌──────────────┐
        │ Candidate  │ │ Ranking │ │ Personalized │
        │ Generation │ │ Service │ │ Cache (Redis)│
        └─────┬──────┘ └────┬────┘ └──────────────┘
              │              │
              ▼              ▼
     ┌──────────────────────────────────┐
     │       Feature Store              │
     │  (用户特征 + 内容特征 + 交互特征)   │
     └──────────────────────────────────┘


     ┌──────────────────────────────────┐
     │       Offline Pipeline           │
     │  Spark / Flink 模型训练 + 特征计算  │
     └──────────────────────────────────┘

面试官:Candidate Generation 和 Ranking 的 trade-off 是什么? :Candidate Generation 负责从海量内容中快速筛出几百个候选,速度优先,精度可以稍低。常用方法包括协同过滤、Embedding-based retrieval。Ranking 负责精细排序,对候选做更复杂的特征计算和模型打分,精度优先,但输入量已经缩减到几百,所以可以用更重的模型。

Data Modeling:从结构到理解

Data Modeling 这一轮,面试官让我设计一个用户行为追踪的数据库 schema。

-- 用户行为事件表
CREATE TABLE user_events (
    event_id BIGINT PRIMARY KEY,
    user_id BIGINT NOT NULL,
    event_type VARCHAR(50) NOT NULL,  -- play, pause, resume, rate, etc.
    content_id BIGINT NOT NULL,       -- 视频/电影 ID
    event_ts TIMESTAMP NOT NULL,
    device_type VARCHAR(20),
    session_id VARCHAR(64),
    watch_duration_ms INT,            -- 观看时长
    completion_pct DECIMAL(5,2),      -- 完成百分比
    INDEX idx_user_time (user_id, event_ts),
    INDEX idx_content (content_id),
    INDEX idx_event_type (event_type, event_ts)
) PARTITION BY RANGE (UNIX_TIMESTAMP(event_ts)) (
    PARTITION p202505 VALUES LESS THAN (UNIX_TIMESTAMP('2025-06-01')),
    PARTITION p202506 VALUES LESS THAN (UNIX_TIMESTAMP('2025-07-01'))
);

-- 用户画像表(离线聚合)
CREATE TABLE user_profiles (
    user_id BIGINT PRIMARY KEY,
    total_watched_hours DECIMAL(10,2),
    favorite_genres JSON,             -- {"drama": 0.8, "comedy": 0.3}
    avg_completion_rate DECIMAL(5,4),
    last_active_ts TIMESTAMP,
    updated_at TIMESTAMP
);

面试官:为什么用 JSON 存 favorite_genres 而不是单独的表? :因为 genre 偏好是一个稀疏向量,每个用户的活跃 genre 数量不同。用 JSON 可以灵活存储,不需要预定义列。同时查询时可以直接在应用层解析,避免 JOIN 开销。但如果需要按 genre 做聚合查询,可能需要额外建立 genre 倒排索引。

Behavioral:看起来简单,其实最有决定性

Behavioral 有两轮,面试官都是 senior 级别。问题很常规,但考察点很明确:你是否和团队匹配。

面试官:Tell me about a time you dealt with ambiguous requirements. :(用 STAR 框架讲述了一个项目经历,强调如何主动定义问题、收集 stakeholder 反馈、快速迭代方案) 面试官:你当时是怎么决定优先级的? :我用了一个简单的 impact/effort 矩阵,把需求分成了四个象限。和 PM 对齐后,先做高影响低投入的 quick win,建立信心后再投入大项目。

面试总结

成功经验

  1. 编码要扎实:Netflix 的 coding 不考偏题,但要求代码质量高、边界情况覆盖全。
  2. System Design 要深入:不要只画架构图,要深入到具体的 trade-off 和实现细节。
  3. Behavioral 要主动:不要被动回答,要主动展示决策过程和影响力。
  4. Data Modeling 要有业务思维:不仅考虑 schema 设计,还要考虑数据如何流动、如何被消费。

面试注意事项

时间管理:每轮 45-60 分钟,coding 35 分钟 + 讨论 10 分钟。System Design 5 分钟需求分析 + 10 分钟架构 + 25 分钟深入。

技术深度:Netflix 的面试官非常看重你对技术的深入理解。不要只说”用什么”,要说”为什么用”和”为什么不选别的”。


推荐阅读


💡 需要面试辅导?

如果你对准备技术面试感到迷茫,或者想要个性化的面试指导和简历优化,欢迎联系 Interview Coach Pro 获取一对一辅导服务。

👉 联系我们 获取专属面试准备方案


准备好拿下下一次面试了吗?

获取针对你的目标岗位和公司的个性化辅导方案。

联系我们