SEO Title: Instacart 面经 2026:在线交易系统 10 题深度解析与上岸策略
Instacart 面经 2026:在线交易系统 10 题深度解析与上岸策略
Instacart 面经 2026 是高频后端真题。
它把账户状态和调度引擎放在一起。
并且每一步都考察失败语义。
因此,这题很能区分工程能力。
此外,这份内容对应 2026 年最新口径。
并且,这是我们学员贡献的最新面经。
换句话说,你要按真实业务来答。
所以只会写 CRUD 远远不够。
2026 面试流程深度复盘:Instacart 面经 2026
首先,在 Instacart 面经 2026 里,开场是 L1。
CreateAccount 和 Deposit 会先出现。
面试官会追问成功和失败语义。
因此你要先定统一返回规则。
其次,Transfer 会立刻加入。
你要同时校验两个账户。
但是任一失败都要返回空。
同时成功时返回源账户余额。
随后,Instacart 面经 2026 会进入 L2。
topNSpenders 是核心扩展题。
具体来说,只统计扣款和转出。
因此 deposit 绝不能记入 spending。
与此同时,面试会升级到 L3。
SchedulePayment 和 cancelPayment 连着问。
同一时间执行顺序会被深挖。
所以你要先讲调度模型再写码。
最后,很多场次会补 L4。
MergeAccount 和 getBalance 一起出现。
这一步考察历史一致性。
总之,Instacart 面经 2026 节奏很快。
核心题目解析
在 Instacart 面经 2026 中,核心是状态机。
首先每个 API 前都先跑 processDuePayments。
然后再执行当前交易接口。
这样才能保证时间语义正确。
1) L1 账户与交易语义
CreateAccount(timestamp, accountId) -> boolean:若账户已存在,返回false。此外,成功时初始化余额与历史快照。Deposit(timestamp, accountId, amount) -> Optional<Integer>:账户不存在返回空。同时,amount<=0也返回空。Transfer(timestamp, source, target, amount) -> Optional<Integer>:双账户都要合法。但是余额不足也返回空。成功后返回源账户余额。
2) L2 spending 与 Top N
spending只记扣款与转出。与此同时,deposit不计入。topNSpenders(timestamp, N):先按 spending 降序。其次按 accountId 升序做并列打破。- 当
N频繁变化时,优先全量排序。反之可用堆做增量维护。
3) L3 延迟支付执行引擎
SchedulePayment的执行时刻是timestamp+delay。此外,预约时不校验余额。paymentId必须全局递增。格式固定为payment1...。cancelPayment仅在三条件都满足时成功。即 ID 存在、未执行、账户匹配。- 同一时间多笔 payment 时,按
paymentId升序稳定执行。并且执行优先级高于同时间其他交易。余额不足则跳过该笔。
flowchart TD
A[收到任意 API 调用] --> B[先执行 processDuePayments(timestamp)]
B --> C{是否有到期 payment}
C -- 否 --> D[执行当前 API]
C -- 是 --> E[按 executeTime 和 paymentId 出堆]
E --> F{已取消或已执行}
F -- 是 --> C
F -- 否 --> G{余额足够}
G -- 否 --> H[标记跳过并结束该 payment]
G -- 是 --> I[扣款 更新spending 写历史快照]
H --> C
I --> C
4) L4 合并与历史查询
MergeAccount(timestamp, account1, account2) -> boolean:先处理到期 payment。然后把余额与 spending 合并到account1。- 合并后删除
account2的交易能力。与此同时,待执行 payment 要迁移到account1。 getBalance(timestamp, accountId, atTime) -> Optional<Integer>:每次余额变更都落历史。并且用TreeMap+floorEntry思想取快照。- 关键点在于合并时立刻写入新快照。因此历史查询在时间线上可解释。
Python 参考代码(可直接讲思路)
from dataclasses import dataclass
from typing import Dict, List, Optional, Tuple
import heapq
import bisect
@dataclass
class Payment:
pid: int
source: str
amount: int
execute_time: int
canceled: bool = False
executed: bool = False
class BankSystem:
def __init__(self) -> None:
self.active = set()
self.balance: Dict[str, int] = {}
self.spending: Dict[str, int] = {}
self.history: Dict[str, List[Tuple[int, int]]] = {}
self.parent: Dict[str, str] = {} # merged account -> survivor
self.payments: Dict[str, Payment] = {}
self.heap: List[Tuple[int, int, str]] = [] # (execute_time, pid, paymentId)
self.next_pid = 1
def _resolve(self, acc: str) -> str:
while acc in self.parent:
acc = self.parent[acc]
return acc
def _append_history(self, ts: int, acc: str) -> None:
self.history.setdefault(acc, []).append((ts, self.balance[acc]))
def _balance_at(self, acc: str, at_time: int) -> Optional[int]:
arr = self.history.get(acc, [])
i = bisect.bisect_right(arr, (at_time, 10**18)) - 1
return None if i < 0 else arr[i][1]
def process_due_payments(self, ts: int) -> None:
while self.heap and self.heap[0][0] <= ts:
execute_time, _, payment_id = heapq.heappop(self.heap)
tx = self.payments[payment_id]
if tx.canceled or tx.executed:
continue
src = self._resolve(tx.source)
if src not in self.active:
tx.executed = True
continue
if self.balance[src] < tx.amount:
tx.executed = True # 余额不足即跳过
continue
self.balance[src] -= tx.amount
self.spending[src] += tx.amount
self._append_history(execute_time, src)
tx.executed = True
def create_account(self, ts: int, account_id: str) -> bool:
self.process_due_payments(ts)
if account_id in self.active or account_id in self.parent:
return False
self.active.add(account_id)
self.balance[account_id] = 0
self.spending[account_id] = 0
self.history[account_id] = [(ts, 0)]
return True
def deposit(self, ts: int, account_id: str, amount: int) -> Optional[int]:
self.process_due_payments(ts)
if amount <= 0 or account_id not in self.active:
return None
self.balance[account_id] += amount
self._append_history(ts, account_id)
return self.balance[account_id]
def transfer(self, ts: int, src: str, dst: str, amount: int) -> Optional[int]:
self.process_due_payments(ts)
if amount <= 0 or src == dst:
return None
if src not in self.active or dst not in self.active:
return None
if self.balance[src] < amount:
return None
self.balance[src] -= amount
self.balance[dst] += amount
self.spending[src] += amount
self._append_history(ts, src)
self._append_history(ts, dst)
return self.balance[src]
def top_n_spenders(self, ts: int, n: int) -> List[str]:
self.process_due_payments(ts)
if n <= 0:
return []
ranked = sorted(self.spending.items(), key=lambda x: (-x[1], x[0]))
return [acc for acc, _ in ranked[:n]]
def schedule_payment(self, ts: int, source: str, amount: int, delay: int) -> Optional[str]:
self.process_due_payments(ts)
if source not in self.active or amount <= 0 or delay < 0:
return None
payment_id = f"payment{self.next_pid}"
tx = Payment(self.next_pid, source, amount, ts + delay)
self.next_pid += 1
self.payments[payment_id] = tx
heapq.heappush(self.heap, (tx.execute_time, tx.pid, payment_id))
return payment_id
def cancel_payment(self, ts: int, account_id: str, payment_id: str) -> bool:
self.process_due_payments(ts)
tx = self.payments.get(payment_id)
if tx is None or tx.canceled or tx.executed:
return False
if self._resolve(tx.source) != account_id:
return False
tx.canceled = True
return True
def merge_account(self, ts: int, a1: str, a2: str) -> bool:
self.process_due_payments(ts)
if a1 == a2 or a1 not in self.active or a2 not in self.active:
return False
self.balance[a1] += self.balance[a2]
self.spending[a1] += self.spending[a2]
self._append_history(ts, a1)
self.active.remove(a2)
del self.balance[a2]
del self.spending[a2]
self.parent[a2] = a1
for tx in self.payments.values():
if not tx.executed and not tx.canceled and tx.source == a2:
tx.source = a1
return True
def get_balance(self, ts: int, account_id: str, at_time: int) -> Optional[int]:
self.process_due_payments(ts)
if at_time > ts or account_id not in self.active:
return None
return self._balance_at(account_id, at_time)
专家备考策略与高频考点:Instacart 面经 2026
Instacart 面经 2026 的 BQ 也很关键。
首先你要把技术点说成业务语言。
其次你要突出可观测性和回滚思路。
因此答案要有“规则 + 取舍 + 风险”。
核心考点:
- 失败分支统一语义。
- 同时间事件的稳定顺序。
- 调度结构为何选最小堆。
- Map<paymentId, tx> 为何能 O(1) 取消。
- 合并后统计口径如何保持一致。
- 历史快照为何必须每次变更都落盘。
STAR 应对策略:
- S:先定义交易系统场景与约束。
- T:明确你负责一致性与时序正确。
- A:按 L1 到 L4 逐层加能力。
- R:给出复杂度、边界样例与压测结果。
总结与行动号召(CTA)
总而言之,Instacart 面经 2026 的本质是状态一致性。
同时它也考察你设计可扩展执行引擎。
如果你按本文框架练三遍,提升会很快。
并且你在白板上会更有结构感。
现在就开始做两件事。
第一,手写一版最小堆调度引擎。
第二,补齐 merge 与历史查询测试。
需要实战指导,可点这里:联系我们的专家进行一对一面试辅导。
此外,可配合阅读:权威算法参考。