📌 一、背景:为什么需要路径缓存?
在美团骑手实时导航系统中,每天超过 5 亿次路径规划请求,如果每次都重新计算:
- ❌ 地图服务调用次数剧增(高德/自研)
- ❌ 响应延迟上升,影响 ETA 和调度时效
- ❌ 多个相似订单浪费计算资源
所以我们引入了路径缓存服务 PathCache,用于加速以下场景:
- 相似起终点订单(比如同小区商户 → 楼盘外卖点)
- 骑手换单前后的路径尝试
- 派单调度模拟(每个骑手尝试不同路径)
📌 二、路径缓存的基本结构
✅ 缓存 Key 设计
ts
cacheKey = `${startLat}_${startLng}_${endLat}_${endLng}_${timeSlice}`
加入时间片段(例如 15分钟粒度)避免老路况影响精度。
✅ 缓存 Value 结构(简化)
ts
interface PathCacheEntry {
route: RouteSegment[]; // 路线分段信息
score: number; // 规划打分
trafficLevel: string; // 当时的路况等级
createTime: number; // 路径生成时间戳
usedCount: number; // 被命中次数
etaSnapshot: number; // 当时 ETA 值
}
📌 三、淘汰策略设计核心:缓存不能太旧,也不能太少
我们评估过以下淘汰算法:
策略 | 特点 | 是否使用 |
---|---|---|
FIFO | 简单,但命中率低 | 否 ❌ |
LRU | 保活常用路径,适合骑手高频路线 | 是 ✅ |
LFU | 命中频率优先,但会留"老废数据" | 否 ❌ |
TTL+LRU | LRU为主,超期也淘汰 | 是 ✅ |
自定义分级 | 热路网保留更久,冷路快淘汰 | 是 ✅ |
📦 四、核心实现片段(Node.js 伪代码)
✅ 路径缓存读写:
js
const cacheMap = new Map()
function getPathFromCache(key) {
const entry = cacheMap.get(key)
if (!entry) return null
const now = Date.now()
if (now - entry.createTime > 10 * 60 * 1000) { // 超过10分钟失效
cacheMap.delete(key)
return null
}
entry.usedCount += 1
return entry
}
function savePathToCache(key, data) {
cacheMap.set(key, {
...data,
createTime: Date.now(),
usedCount: 1
})
}
✅ 淘汰策略:定时任务 + 限制容量
js
const MAX_CACHE_SIZE = 100000
setInterval(() => {
const entries = Array.from(cacheMap.entries())
// 超时 + 命中次数低 的组合淘汰策略
entries.sort((a, b) => {
return a[1].usedCount - b[1].usedCount
})
if (entries.length > MAX_CACHE_SIZE) {
const dropCount = entries.length - MAX_CACHE_SIZE
for (let i = 0; i < dropCount; i++) {
cacheMap.delete(entries[i][0])
}
}
}, 60000)
📈 五、缓存命中率优化方案(真实策略)
✅ 1. 起终点"相似化"
通过对经纬度 hash 到格子区域:
ts
function simplifyCoord(lat, lng) {
return `${Math.round(lat * 1000)},${Math.round(lng * 1000)}`
}
命中率提升约 23.1%
✅ 2. 多路缓存(小批量候选)
为了防止主缓存被挤爆,我们引入 冷热分级策略:
text
一级缓存:热门路径(热门商圈配送) → Redis
二级缓存:普通路径 → LRU 本地缓存
三级备份:异步写入本地 LevelDB
✅ 3. 同区域同向缓存迁移
举例:
A小区 → 商场B 的路径,可应用到 A小区 → 商场B 门口快递点,只需增补尾段
这种近似路径继承策略 ,帮助我们降低路径规划调用约 14.7%
📊 六、实战效果对比
指标 | 未优化 | 使用 PathCache(全策略) |
---|---|---|
平均路径耗时 | 123ms | 23ms |
地图服务 QPS 降低 | - | 减少 67% 请求 |
缓存命中率 | 18% | 64.5% |
错误路径命中率 | - | < 0.9%(有 ETA 反查兜底) |
📌 七、工程挑战总结
挑战 | 解决策略 |
---|---|
异常旧路径未失效 | TTL+并发清理 |
路况快速波动 | 加入时间片 hash |
高峰缓存击穿 | Redis 热点缓存 + fallback 兜底 |
多版本路径重复命中 | 增加 algo_version 区分 key |
✅ 总结
路径缓存系统是一个低成本、高收益的优化方案,但做好它不简单:
- 要理解"骑手路径行为"和"地理分布模式"
- 要能高效设计缓存 key、淘汰策略、实时清理
- 要和 ETA 模型打通,确保预测数据不过时
- 要可观测、可复盘、可 Debug(日志审计 + 兜底机制)