Skip to content

网约车 / 出行调度 架构模板

代表产品:Uber、滴滴、Lyft、Grab 一句话定位:实时掌握海量司机和乘客的位置,在几秒内把最合适的车和人匹配上,并全程跟踪行程。


1. 一句话定位

网约车系统 = 一张不断变化的「活地图」 + 一个秒级撮合供需的「实时匹配引擎」

它和普通系统最不一样的地方:核心数据是「位置」,而位置每几秒就变一次、几秒后就过时。 整套架构都在回答两个问题:「我附近现在有谁?」和「这个人和那辆车,怎么尽快配上?」——这需要地理空间索引、海量实时位置流、和一个撮合引擎协同。

2. 业务本质:它在解决什么问题

它是一个双边市场:一边是有空闲运力的司机,一边是有出行需求的乘客。系统解决的是「在时空上,把闲置的车和等车的人高效匹配起来」,顺便解决信任(评价)、定价、安全。

钱从哪来:每单抽佣、动态溢价、订阅 / 会员、广告。匹配效率越高(空驶越少、等待越短),双边体验和平台收入越好。

3. 核心需求与约束

功能性需求:

  • [ ] 司机 / 乘客实时定位与上报
  • [ ] 叫车 → 就近匹配派单
  • [ ] 路径规划与到达时间(ETA)预估
  • [ ] 动态定价(高峰溢价)
  • [ ] 行程跟踪、支付、评价

非功能性需求 / 质量属性:

质量属性目标为什么对这类系统重要
匹配延迟秒级等太久乘客就取消、司机就跑单
位置写入吞吐极高每个司机每几秒上报一次 = 海量写
地理查询速度毫秒级「附近的车」要快速算出来
高峰扩展性弹性早晚高峰、雨天、散场,流量剧烈波动

关键约束(不可逾越的边界):

  • 🔴 位置数据高频、海量、易失:写入量巨大,但每条只活几秒,旧的没价值。
  • 🔴 地理查询是「特殊查询」:「方圆 3 公里内的车」既不是主键点查、也不是范围扫描,而是二维空间查询,普通索引干不了。
  • 🔴 强地域性:北京的供需和纽约毫不相干——这是个天然的分片维度
  • 🔴 供需实时剧烈波动:演唱会散场、暴雨,瞬间需求爆炸。

4. 架构全景图

   司机 App                乘客 App
   (每几秒报位置)           (叫车)
       │                      │
       ▼                      ▼
┌───────────────┐    ┌──────────────────┐
│ 位置接入服务    │    │   叫车请求           │
│ (超高频写,     │    └─────────┬────────┘
│  主要落内存)    │              ▼
└──────┬────────┘    ┌──────────────────────────┐
       ▼             │   匹配引擎                  │
┌───────────────┐   │ ① 用地理索引查「附近的车」   │
│ 地理空间索引    │◀──│ ② 按距离/ETA/评分挑最优     │
│ GeoHash/网格   │   │ ③ 派单、等司机接受           │
│ (按区域分片)    │   └────────────┬─────────────┘
└───────────────┘                ▼
       ┌──────────────────────────────────────┐
       │  行程服务(状态机)→ 定价 → 支付 → 评价   │
       │  全程位置跟踪、ETA 更新、推送            │
       └──────────────────────────────────────┘
       (轨迹异步写入时序存储,用于回放/分析)

灵魂部件是地理空间索引 + 匹配引擎:前者让「查附近」从「不可能」变成「毫秒」,后者在这张活地图上完成撮合。其余都是支撑这次撮合与之后的行程。

5. 组件职责

  • 位置接入服务:承接司机 / 乘客超高频的位置上报。为什么需要:写入量极大,且大部分位置只需放内存、无需逐条持久化(见决策 2)。
  • 地理空间索引:把二维经纬度编码成可快速检索的结构(GeoHash / 网格 / 四叉树),支持「附近的车」查询。为什么需要:这是网约车区别于一切普通系统的核心能力。
  • 匹配引擎:查附近候选 → 按距离 / ETA / 评分挑选 → 派单。为什么需要:撮合是平台创造价值的核心动作。
  • 行程服务(状态机):管理一单从「已派单 → 司机在路上 → 行程中 → 完成」的状态。为什么需要:行程是有严格状态流转的,且关乎计费。
  • 动态定价:用实时供需比调价。为什么需要:高峰时用价格杠杆平衡供需、削峰(见决策 4)。
  • 推送 / 长连接:实时把派单、位置、ETA 推给两端。为什么需要:出行是强实时交互。

6. 关键数据流

场景一:司机持续上报位置(超高频写)

1. 司机 App 每 4 秒上报一次 {司机ID, 经纬度} ──▶ 位置接入服务
2. 更新地理空间索引里该司机的位置(主要在内存)
3. (旁路)按采样把轨迹异步写入时序存储,用于回放、结算、分析
   ── 注意:不是每条位置都进数据库,那样会写爆且无意义

场景二:乘客叫车并匹配(系统的核心动作)

1. 乘客叫车 {上车点经纬度} ──▶ 匹配引擎
2. 用地理索引查「上车点附近的空闲司机」── 毫秒级拿到候选
3. 按 实际道路 ETA、司机评分、是否顺路 排序,挑最优
4. 派单给该司机 ──▶ 推送,等其接受(超时则派给下一个)
5. 接受后 ──▶ 行程服务建单,进入「司机在路上」状态,两端实时看位置

7. 数据模型与存储选择

核心实体:司机位置(高频更新、易失);行程(状态机);用户 / 司机资料;轨迹(时序)

数据存储类型为什么
实时司机位置内存级地理索引超高频更新、查询要快、旧数据可丢
行程订单关系型状态流转 + 计费,要事务、强一致
历史轨迹时序 / 对象存储海量追加、按时间回放、用于分析结算
用户 / 司机资料关系型结构化、要一致

教学点:司机位置是典型的「易失高频数据」——它的价值只在「此刻」,几秒后就被新值覆盖。把它逐条强一致落库,是把宝贵的写入能力浪费在不需要持久化的数据上。

8. 关键架构决策与权衡 ⭐

决策 1:「附近的车」怎么查?(网约车的立身之本)⭐

  • 数据库 WHERE 算距离:对每辆车算一遍到上车点的距离再筛——全表扫描,车一多就崩。
  • 空间索引(GeoHash / 网格):把二维位置编码成可索引的结构,「附近」变成「查相邻的几个格子」。
  • 取向:必用空间索引。它把『二维邻近』这个特殊查询,变成了普通的、可索引的查找。

决策 2:位置数据,逐条落库还是主要放内存?⭐

  • 逐条持久化:每个司机每几秒一条,海量写入直接压垮数据库,而且这些数据几秒后就过时。
  • 主要放内存(地理索引)+ 采样持久化轨迹:实时位置在内存里更新,只按需采样保存轨迹。
  • 取向:实时位置放内存,轨迹采样异步落库。别为「不需要持久化的易失数据」付出持久化的代价。

决策 3:就近匹配,还是批量全局最优?

  • 就近贪心:来一单立刻派最近的车,简单、快。
  • 批量撮合:攒一小批请求和车一起算全局最优(减少总空驶)。
  • 取向:起步就近;规模大后引入批量撮合提升整体效率,代价是单次匹配要多等一两秒。

决策 4:高峰怎么扛?——用定价当「经济限流器」

  • 硬扛:供给不变、需求暴涨,大量人打不到车、体验崩。
  • 动态溢价:涨价抑制部分需求、吸引更多司机上线,自动让供需重新平衡。
  • 取向:动态定价本质是用经济杠杆做流量调节,是限流 / 削峰的高级形态。代价是用户体感差、需谨慎设计与合规。

决策 5:按地域分片。

  • 强地域性让分片变得无比自然:每个城市 / 区域是几乎独立的系统,跨区域无交互。这是「顺着业务的天然边界切」的范例,水平扩展极干净。

9. 规模化与瓶颈

  • 第一个瓶颈:位置写入洪峰。 → 破解:内存地理索引 + 按区域分片,写入压力天然被地域打散。
  • 第二个瓶颈:局部供需爆炸(机场 / 演唱会散场)。→ 破解:区域隔离不影响别处 + 排队 + 动态定价削峰。
  • 第三个瓶颈:海量长连接(每个在线用户一条)。→ 破解:长连接接入层水平扩、按区域就近接入。详见 实时通讯模板
  • 跨区域几乎独立:地理分片让全球扩展近似「复制一套到新区域」,这是它最舒服的地方。

10. 安全与合规要点

  • 🔴 位置隐私:实时位置是极敏感数据,要严格授权、最小化留存、传输加密;行程结束后弱化精度。
  • 人身安全:行程录音、一键报警、轨迹可追溯、紧急联系人,是这类产品的刚需而非附加。
  • 欺诈:刷单、虚假定位(GPS 作弊)、司乘合谋骗补——需要轨迹一致性校验与风控。
  • 支付安全:见 支付系统模板

11. 常见误区 / 反模式

  • 用数据库 WHERE 算距离做「附近查询」 → ✅ 空间索引(GeoHash / 网格)。
  • 每条位置都强一致落库 → ✅ 实时位置放内存,轨迹采样异步持久化。
  • 全局一个大池子撮合,无视地域 → ✅ 按区域分片,顺着天然边界切。
  • 高峰只想着加机器硬扛 → ✅ 排队 + 动态定价,用经济杠杆削峰。
  • 把位置当普通业务数据强一致处理 → ✅ 认清它「易失、高频、只活几秒」的本质。

12. 演进路线:MVP → 成长期 → 成熟期

阶段规模量级架构长什么样此时该操心什么
MVP单城市内存地理索引 + 就近贪心匹配 + 一个行程库先把「叫车 - 派单 - 完成」跑通
成长期多城市按地域分片、动态定价、ETA 优化、长连接接入层匹配效率、高峰扩展、位置写入
成熟期全球多区域批量全局撮合、机器学习 ETA / 定价、拼车、运力调度预测全局效率、容灾、安全合规、成本

13. 可复用要点

  • 💡 特殊的查询,需要专门的索引。 地理邻近用空间索引,正如全文检索用倒排索引——把特殊问题映射到能被索引的结构上。
  • 💡 易失的高频数据,不必持久化每一条。 先问「这数据多久就没用了」,再决定要不要、以及怎么存。
  • 💡 用经济杠杆(定价)调节流量,是限流 / 削峰的高级形态。 价格能同时压需求、拉供给,比单纯排队更聪明。
  • 💡 顺着业务的天然边界(地域)分片,扩展最干净。 跨片无交互时,水平扩展几乎等于「复制一套」。

🎯 随堂检验

🤔网约车查『我附近的车』,正确做法是?
  • A用数据库 WHERE 对每辆车算距离
  • B用地理空间索引(GeoHash / 网格)
  • C全表扫描后排序

参考原型与延伸阅读

本模板基于以下真实开源项目工程剖析整理。

🔧 开源原型(可直接读代码):

  • uber/h3 — Uber 开源的六边形分层地理空间索引(C 核心 + 多语言绑定),网约车定价与派单的空间索引基础。

📖 工程博客:


📌 一句话记住网约车:它不是「一个叫车 App」,而是「一张每秒都在变的活地图 + 一个在地图上秒级撮合供需的引擎」——所有设计都在回答『怎么又快又准地知道附近有谁、并把人和车配上』。