题目重点:G8 SD 题目给 Listings/Reservations/Pricing 三表,房东选时间范围看已订晚数与均价,房源超百套页面慢;按口径和预聚合优化。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练版按 hostId 与 [start,end) 日期查询,已订晚数只计最终确认且未取消的 reservation 在窗口内的夜晚数。
- 平均价格训练版定义为已订夜晚的实际成交单价加权平均;若题目要当前挂牌价均值或按 reservation 简单平均,应另换分子/分母。
- 价格和预订可变更,预聚合必须可回补/重算,不能把页面缓存当账务真相。
解题路线
- 先以 EXPLAIN 和 tracing 查慢点:逐 listing 的 N+1 查询、跨日期 join 膨胀、缺 (hostId,listingId,date) 索引或过大返回体。
- 定义日粒度事实:listingId/date 的 bookedNightCount 与 realizedPriceSum,取消/改期通过增量事件修正;按 host/listing/date 做物化聚合并保留重算源事件。
- API 先取得 host 的 listing IDs,再按日期范围从聚合表做一次 grouped query,返回总晚数与 sum(price)/count 的均价;分页展示每房源明细时用稳定 listingId cursor。
- 对近期频繁访问的窗口可短缓存,但定义版本/更新时间和取消后的失效;历史大窗口可异步查询,避免在线全表扫描。
- 监控页面 p95/p99、聚合滞后、源表与聚合核对误差、缓存命中和分页完整性。
手动走一遍
训练例:房源 A 两个确认夜晚各 100/200,B 一个确认夜晚 300,则房东窗口已订晚数 3、按夜加权均价 (100+200+300)/3=200。若一个预订后来取消,要从聚合中撤销其夜晚/金额;不能只改 Reservations 不改读模型。
规模与取舍
原始逐房源查询近似 O(L×查询成本),聚合后按 host/date 索引扫描 O(窗口内聚合行数);日聚合存储约房源数×活跃日期数,更新成本随预订夜晚数。题目没有负载数,不能给固定缓存 TTL。
易错点与追问
容易失分的边界
- 没定义均价分母就优化 SQL。
- 每房源单独查三表产生 N+1。
- 用当前挂牌价格回填过去成交均价。
- 取消/改期没修正预聚合。
- 用 offset 分页导致持续新增房源时重复/漏项。
继续深挖
- 若要按房源类型或城市切分如何建索引?
- 长行程跨月改期怎样更新聚合?
- 聚合滞后时 UI 如何显示?
- 若结果必须实时精确而不能异步怎么办?
