Pinterest 数据工程师面试实录 2026:Feed 推荐数据管道 + Spark + 视觉搜索
Pinterest Data Engineer 面试真实经历:SQL 窗口函数、Spark 大数据处理、实时 Pin 推荐数据管道、System Design 完整复盘。第一人称真实面经,含面试官对话与解题思路。
公司:Pinterest 岗位:Data Engineer 面试形式:Phone Screen + Virtual Onsite (4 轮) 结果:Pass → Offer
2026 年 8 月,我参加了 Pinterest 的 Data Engineer 面试。Pinterest 的面试风格非常注重 数据驱动产品优化——从 Feed 推荐算法的数据管道到视觉搜索系统的特征工程,每个环节都围绕着如何提升用户发现感兴趣内容的体验展开。
Pinterest 的数据团队规模很大,数据平台基于 Spark + BigQuery + Kafka,处理 PB 级的 Pin 浏览、点击和收藏数据。面试官非常关注数据管道的可扩展性和数据质量保障。
Phone Screen:SQL + 数据建模
电话面由一位 Senior DE 进行,45 分钟。
题目:Pin 内容分析
给定以下表结构:
pin_impressions: impression_id, user_id, pin_id, session_id, impression_time, positionpin_clicks: click_id, impression_id, user_id, pin_id, click_timepin_details: pin_id, creator_id, board_id, category, image_url, created_at请完成以下查询:
- 计算每个类别的 CTR(点击率)
- 分析 Feed 位置对点击率的影响
- 计算用户的 Pin 收藏留存率(看到后 7 天内收藏的比例)
我的解答:
-- 1. 每个类别的 CTR
WITH category_impressions AS (
SELECT
d.category,
COUNT(DISTINCT i.impression_id) AS total_impressions,
COUNT(DISTINCT c.click_id) AS total_clicks
FROM pin_details d
LEFT JOIN pin_impressions i ON d.pin_id = i.pin_id
LEFT JOIN pin_clicks c ON i.impression_id = c.impression_id
GROUP BY d.category
)
SELECT
category,
total_impressions,
total_clicks,
ROUND(total_clicks * 100.0 / NULLIF(total_impressions, 0), 2) AS ctr_pct
FROM category_impressions
ORDER BY ctr_pct DESC;
-- 2. Feed 位置对 CTR 的影响
WITH position_stats AS (
SELECT
i.position,
COUNT(DISTINCT i.impression_id) AS impressions,
COUNT(DISTINCT c.click_id) AS clicks,
ROUND(COUNT(DISTINCT c.click_id) * 100.0 / COUNT(DISTINCT i.impression_id), 2) AS ctr_pct
FROM pin_impressions i
LEFT JOIN pin_clicks c ON i.impression_id = c.impression_id
GROUP BY i.position
)
SELECT position, impressions, clicks, ctr_pct
FROM position_stats
ORDER BY position;
-- 3. 用户的 Pin 收藏留存率
WITH first_impressions AS (
SELECT
user_id,
pin_id,
MIN(impression_time) AS first_impression_time
FROM pin_impressions
GROUP BY user_id, pin_id
),
saved_pins AS (
SELECT DISTINCT
user_id,
pin_id,
MIN(s.save_time) AS first_save_time
FROM pin_saves s
GROUP BY user_id, pin_id
)
SELECT
COUNT(DISTINCT fi.user_id) AS total_users,
COUNT(DISTINCT sp.user_id) AS users_who_saved,
ROUND(COUNT(DISTINCT sp.user_id) * 100.0 / COUNT(DISTINCT fi.user_id), 2) AS save_rate_7d
FROM first_impressions fi
LEFT JOIN saved_pins sp
ON fi.user_id = sp.user_id
AND fi.pin_id = sp.pin_id
AND sp.first_save_time BETWEEN fi.first_impression_time
AND DATE_ADD(fi.first_impression_time, INTERVAL 7 DAY);
VO Round 1:Python + Spark 优化
这一轮由一位 Spark Platform 团队的 DE 进行,60 分钟。
题目:大规模 Pin 相似度计算
设计一个 Spark 程序,计算 Pin 之间的相似度,支持:
- 基于视觉特征的相似度计算
- 基于用户行为的协同过滤
- 支持增量更新
我的解答:
from pyspark.sql import SparkSession
from pyspark.sql.functions import *
from pyspark.sql.types import *
import numpy as np
spark = SparkSession.builder \
.appName("PinSimilarityComputation") \
.config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension") \
.getOrCreate()
# 加载 Pin 视觉特征
pin_features = spark.read.parquet("/data/pin_features/") # pin_id, feature_vector
# 加载用户行为
user_interactions = spark.read.parquet("/data/user_interactions/") # user_id, pin_id, interaction_type, timestamp
# 1. 视觉相似度计算 (Cosine Similarity)
def compute_visual_similarity():
"""计算视觉相似度矩阵"""
# 使用广播变量优化小表 join
broadcast_features = F.broadcast(pin_features)
# 自连接计算相似度
similarity = (pin_features.alias("a")
.join(broadcast_features.alias("b"), col("a.pin_id") < col("b.pin_id"))
.withColumn("similarity",
cos_similarity(col("a.feature_vector"), col("b.feature_vector"))
)
.filter(col("similarity") > 0.85) # 阈值过滤
.select("a.pin_id AS pin_id_1", "b.pin_id AS pin_id_2", "similarity"))
return similarity
# 2. 协同过滤
def compute_cf_similarity():
"""基于用户行为的协同过滤"""
# 构建用户-Pin 交互矩阵
user_pin_matrix = (user_interactions
.groupBy("user_id", "pin_id")
.agg(count("*").alias("interaction_count"))
.filter(col("interaction_count") >= 2)) # 至少交互 2 次
# 计算 Pin 共现矩阵
pin_cooccurrence = (user_pin_matrix.alias("a")
.join(user_pin_matrix.alias("b"), "user_id")
.filter(col("a.pin_id") < col("b.pin_id"))
.groupBy("a.pin_id", "b.pin_id")
.agg(sum(col("a.interaction_count") * col("b.interaction_count")).alias("cooccurrence_score")))
return pin_cooccurrence
# 3. 合并相似度并写入
visual_sim = compute_visual_similarity()
cf_sim = compute_cf_similarity()
# 合并两种相似度
combined_similarity = (visual_sim
.unionByName(cf_sim, allowMissingColumns=True)
.withColumn("visual_sim", coalesce(col("similarity"), lit(0)))
.withColumn("cf_sim", coalesce(col("cooccurrence_score"), lit(0)))
.withColumn("final_score",
col("visual_sim") * 0.6 + col("cf_sim") * 0.4) # 加权合并)
# 写入 Delta 表
combined_similarity.write \
.mode("overwrite") \
.option("mergeSchema", "true") \
.save("/data/pin_similarity/delta/")
面试官追问:
“如果 Pin 数量达到 10 亿级别,Spark 怎么处理?”
我回答:
- 分桶策略:对 Pin 特征进行分桶,只计算同桶内的相似度
- 近似最近邻 (ANN):使用 FAISS 或 ScaNN 进行近似相似度计算
- 增量更新:只对新 Pin 计算相似度,老 Pin 定期重新计算
# 使用 LSH (Locality Sensitive Hashing) 进行近似最近邻搜索
from pyspark.ml.feature import BucketedRandomProjectionLSH
lsh = BucketedRandomProjectionLSH(
inputCol="feature_vector",
outputCol="hashes",
numHashTables=10
)
model = lsh.fit(pin_features)
# 找到近似相似对
similar_pairs = model.approxSimilarityJoin(
pin_features, pin_features,
threshold=0.85,
distCol="jaccardDistance"
).select("datasetA.pin_id", "datasetB.pin_id", "jaccardDistance")
VO Round 2:SQL + 数据建模
这一轮由一位 Analytics 团队的 DE 进行,60 分钟。
题目:用户行为分析数据模型
设计一个数据模型,支持:
- 用户日常行为分析(浏览、点击、收藏、分享)
- Pin 内容效果分析
- Feed 推荐效果评估
我的设计:
-- 1. 事实表:用户行为
CREATE TABLE fact_user_events (
event_id STRING PRIMARY KEY,
user_id STRING,
pin_id STRING,
event_type STRING, -- view, click, save, share, comment
event_time TIMESTAMP,
session_id STRING,
feed_position INT,
device_type STRING,
platform STRING, -- web, ios, android
country STRING
)
PARTITION BY DATE(event_time);
-- 2. 维度表:Pin 内容
CREATE TABLE dim_pins (
pin_id STRING PRIMARY KEY,
creator_id STRING,
board_id STRING,
category STRING,
subcategory STRING,
has_video BOOLEAN,
created_at TIMESTAMP,
load_date DATE DEFAULT CURRENT_DATE()
)
PARTITION BY DATE(created_at);
-- 3. 维度表:用户
CREATE TABLE dim_users (
user_id STRING PRIMARY KEY,
signup_date DATE,
country STRING,
device_type STRING,
is_verified BOOLEAN,
load_date DATE DEFAULT CURRENT_DATE()
);
-- 4. 聚合视图:每日用户行为摘要
CREATE VIEW v_daily_user_activity AS
SELECT
DATE(ue.event_time) AS activity_date,
ue.user_id,
COUNT(CASE WHEN ue.event_type = 'view' THEN 1 END) AS views,
COUNT(CASE WHEN ue.event_type = 'click' THEN 1 END) AS clicks,
COUNT(CASE WHEN ue.event_type = 'save' THEN 1 END) AS saves,
COUNT(CASE WHEN ue.event_type = 'share' THEN 1 END) AS shares,
COUNT(DISTINCT ue.pin_id) AS unique_pins_viewed
FROM fact_user_events ue
GROUP BY DATE(ue.event_time), ue.user_id;
-- 5. Pin 效果分析视图
CREATE VIEW v_pin_performance AS
SELECT
p.pin_id,
p.category,
p.created_at,
COUNT(DISTINCT ue.event_id) AS total_events,
COUNT(DISTINCT CASE WHEN ue.event_type = 'view' THEN ue.event_id END) AS impressions,
COUNT(DISTINCT CASE WHEN ue.event_type = 'click' THEN ue.event_id END) AS clicks,
COUNT(DISTINCT CASE WHEN ue.event_type = 'save' THEN ue.event_id END) AS saves,
COUNT(DISTINCT CASE WHEN ue.event_type = 'share' THEN ue.event_id END) AS shares,
ROUND(COUNT(DISTINCT CASE WHEN ue.event_type = 'click' THEN ue.event_id END) * 100.0 /
NULLIF(COUNT(DISTINCT CASE WHEN ue.event_type = 'view' THEN ue.event_id END), 0), 2) AS ctr
FROM dim_pins p
LEFT JOIN fact_user_events ue ON p.pin_id = ue.pin_id
GROUP BY p.pin_id, p.category, p.created_at;
VO Round 3:System Design — 实时 Pin 推荐数据管道
这一轮由一位 Principal Engineer 进行,60 分钟。
题目:设计 Pinterest 的实时推荐数据管道
设计一个数据管道,支持:
- 实时收集用户行为数据
- 实时更新用户特征
- 支持实时推荐推理
- 端到端延迟 < 50ms
我的架构设计:
┌─────────────────────────────────────────────────────────────────┐
│ Pinterest Real-time Recommendation Pipeline │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Data Collection │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Pin Views │ │ Pin Clicks │ │ Pin Saves │ │ │
│ │ │ (Kafka) │ │ (Kafka) │ │ (Kafka) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Stream Processing │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Spark │ │ Feature │ │ User Profile │ │ │
│ │ │ Streaming │ │ Engineering │ │ Update │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Feature Store │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Redis │ │ BigQuery │ │ Pin Feature │ │ │
│ │ │ (Real-time) │ │ (Batch) │ │ Index │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Model Serving │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ TensorFlow │ │ FAISS │ │ A/B Test │ │ │
│ │ │ Serving │ │ (ANN Search) │ │ Framework │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
关键技术决策:
- Kafka:高吞吐事件收集,支持回溯和重放
- Spark Streaming:流式特征计算,与批处理统一 API
- Redis:实时特征存储,支持低延迟读取
- FAISS:大规模向量相似度搜索,支持亿级 Pin 检索
- TensorFlow Serving:模型推理服务,支持版本管理
VO Round 4:Behavioral
最后一轮由 Hiring Manager 进行,60 分钟。
典型问题
Q1: Why Pinterest?
我回答:Pinterest 是一个独特的发现平台——用户不是带着明确搜索目的,而是通过浏览 Feed 发现感兴趣的内容。这种”无意中发现”的场景对数据驱动的推荐系统提出了极高的要求,这正是吸引我的地方。
Q2: Describe a time you improved a data pipeline’s performance.
我分享了一个将 Spark 作业从 2 小时优化到 15 分钟的经历:
- Situation: Pin 相似度计算作业运行时间过长,影响推荐模型更新频率
- Task: 将作业运行时间从 2 小时缩短到 30 分钟以内
- Action:
- 使用广播变量优化小表 join
- 对特征向量进行分桶,减少 shuffle 数据量
- 启用 Spark AQE 动态优化
- 使用 Delta Lake 替代 Parquet,利用时间旅行特性
- Result: 运行时间缩短到 15 分钟,节省了 80% 的计算资源
面试总结
成功经验
- SQL 能力:窗口函数、CTE、分区表是基础
- Spark 优化:广播变量、分桶、AQE、Delta Lake
- System Design:实时推荐系统的数据管道设计
- 行为面试:准备性能优化和数据质量改进的故事
推荐阅读
- Spark 性能优化指南 — 广播变量、分桶、AQE
- 推荐系统数据管道 — 特征工程、模型训练
- Kafka 架构设计 — 分区、副本、消费者组
💡 需要面试辅导?
如果你对准备技术面试感到迷茫,或者想要个性化的面试指导和简历优化,欢迎联系 Interview Coach Pro 获取一对一辅导服务。
👉 联系我们 获取专属面试准备方案