美团本地生活架构

2010 年 3 月,王兴创办了美团。那时候Groupon 的团购模式正在美国爆火,王兴把团购引入中国,成为中国团购网站的开创者。

但团购只是起点。美团用十年时间,从一个团购网站演变成了中国最大的本地生活服务平台——涵盖外卖、到店、酒旅、买菜、单车、充电宝等多个业务。日订单峰值超过 6000 万单,骑手超过 100 万,商家超过 900 万

理解美团技术挑战的关键,在于它的本地生活服务特性

  • LBS(Location Based Service):所有业务都跟地理位置强相关,搜索结果、配送路线都要根据用户位置实时计算。
  • 供给侧分散:没有自营商品或司机,而是连接商家和用户,需要协调的是数十万商家和百万骑手。
  • 高峰低谷明显:午晚高峰(11-13点、17-19点)和平时流量差距可达 10 倍以上。
  • 多角色系统:用户、商家、骑手、BD(地推人员),每个角色的数据和业务逻辑完全不同。

架构演进时间线

时间阶段核心技术解决的核心问题
2010-2013团购起步PHP + MySQL + 单体快速验证商业模式
2013-2016外卖扩张期Java 微服务 + HBase + MQ团购到外卖,O2O 实时性要求
2016-2019配送优化期智能调度系统 + Redis GEO + Storm骑手调度、实时路径规划
2019-至今平台化期本地超脑 + 美团云 + 多端融合实时决策、供需匹配

第一阶段:团购时代的单体架构(2010-2013)

LBS 的基本问题

团购模式的技术核心是 LBS(基于位置的服务)——用户在某个位置,希望看到附近的团购优惠。

早期实现很简单:商家入驻时填写地址经纬度,用户搜索时按距离排序:

-- 查找附近商家的 SQL(简化版)
SELECT id, name, ST_Distance_Sphere(
    location, ST_GeomFromText('POINT(116.408 39.904)')) AS distance
FROM merchants
WHERE city = '北京'
  AND category = '餐饮'
HAVING distance < 5000  -- 5km 范围内
ORDER BY distance
LIMIT 20;

这个查询的问题:当商家数量增加到几十万时,MySQL 的空间索引在高性能要求下变得不够用。

第二阶段:外卖扩张与 O2O 实时化(2013-2016)

为什么团购架构撑不住外卖

2013 年美团上线外卖,外卖的实时性要求远高于团购:

  • 团购:用户搜索 → 商家列表 → 下单(延迟容忍度高)
  • 外卖:用户搜索 → 商家列表 → 下单 → 商家接单 → 骑手取餐 → 配送 → 用户收货(延迟要求高)

外卖的核心是实时配送,每一步都需要及时响应。

智能调度系统:骑手匹配

美团配送的调度系统(internally called "TMS"),是外卖系统中最复杂的部分之一。当用户下单后,系统需要在几秒钟内匹配最合适的骑手。

// 配送调度:骑手匹配算法
@Service
public class DispatchService {

    @Autowired private MerchantStore merchantStore;
    @Autowired private RiderService riderService;
    @Autowired private DistanceService distanceService;

    public DispatchDecision dispatch(Long orderId, Location deliveryAddress) {
        Merchant merchant = merchantStore.findByOrder(orderId);
        Location merchantLocation = merchant.getLocation();

        // 1. 找出附近可用的骑手(10km 范围内)
        List<Rider> nearbyRiders = riderService.findNearby(
            merchantLocation.getLat(),
            merchantLocation.getLng(),
            10000  // 10km
        );

        // 2. 过滤出正在等单的骑手(不是正在配送中的)
        nearbyRiders = nearbyRiders.stream()
            .filter(Rider::isIdle)
            .collect(Collectors.toList());

        if (nearbyRiders.isEmpty()) {
            return DispatchDecision.noRiderAvailable();
        }

        // 3. 并行计算每个骑手的配送成本
        List<RiderCost> riderCosts = nearbyRiders.parallelStream()
            .map(rider -> calculateCost(rider, merchant, deliveryAddress))
            .collect(Collectors.toList());

        // 4. 选择最优骑手(综合考虑距离、骑手当前负载、时间窗口)
        RiderCost best = riderCosts.stream()
            .min(Comparator.comparingDouble(RiderCost::getTotalCost))
            .orElseThrow();

        // 5. 绑定订单
        riderService.assignOrder(best.getRiderId(), orderId);

        return DispatchDecision.success(best.getRiderId(), best.getEstimatedTime());
    }

    private RiderCost calculateCost(Rider rider, Merchant merchant,
                                   Location deliveryAddress) {
        // 距离成本:骑手到商家的距离
        double distToMerchant = distanceService.distance(
            rider.getLocation(), merchant.getLocation());

        // 配送成本:商家到用户地址的距离
        double distToUser = distanceService.distance(
            merchant.getLocation(), deliveryAddress);

        // 时间成本:骑手当前等单时间(等得越久越优先派单)
        long waitTime = System.currentTimeMillis() - rider.getLastDeliveryTime();

        // 综合成本
        double totalCost = distToMerchant * 1.0
                         + distToUser * 0.5
                         - waitTime * 0.1;  // 负号:等得越久成本越低

        return new RiderCost(rider, distToMerchant, distToUser,
            waitTime, totalCost);
    }
}

第三阶段:配送网络优化(2016-2019)

多点配送:骑手同时接多单

美团配送的核心优化是骑手同时接多单——骑手不是一次只送一单,而是一次取多个商家的多份餐,按最优路线依次配送。

这让配送效率提升了 40% 以上,但调度复杂度急剧上升:

// 多点配送:路径规划
@Service
public class MultiStopDispatcher {

    // 骑手一次接 N 单,按最优路线配送
    public DeliveryPlan planMultiStopRoute(Rider rider, List<Order> orders) {
        Merchant firstPickup = orders.get(0).getMerchant();
        Location currentPos = rider.getLocation();

        List<DeliveryStop> plan = new ArrayList<>();

        for (Order order : orders) {
            // 1. 前往商家取餐
            DeliveryStop pickup = new DeliveryStop();
            pickup.setType(StopType.PICKUP);
            pickup.setLocation(order.getMerchant().getLocation());
            pickup.setOrderId(order.getId());
            pickup.setEstimatedArrival(
                calculateArrivalTime(currentPos, pickup.getLocation()));
            plan.add(pickup);
            currentPos = pickup.getLocation();

            // 2. 前往用户地址送餐
            DeliveryStop dropoff = new DeliveryStop();
            dropoff.setType(StopType.DROPOFF);
            dropoff.setLocation(order.getDeliveryAddress());
            dropoff.setOrderId(order.getId());
            dropoff.setEstimatedArrival(
                calculateArrivalTime(currentPos, dropoff.getLocation()));
            dropoff.setTimeWindow(order.getLatestAcceptableTime()); // 用户可接受的最晚时间
            plan.add(dropoff);
            currentPos = dropoff.getLocation();
        }

        // 3. 优化路线顺序(贪心算法 + 2-opt 局部优化)
        plan = optimizeRouteOrder(plan);

        // 4. 验证时间窗口约束
        validateTimeWindows(plan);

        return new DeliveryPlan(rider.getId(), plan);
    }

    // 2-opt 局部优化:交换路径中两条边的中点,看是否能缩短总距离
    private List<DeliveryStop> optimizeRouteOrder(List<DeliveryStop> plan) {
        double bestDistance = calculateTotalDistance(plan);

        for (int i = 1; i < plan.size() - 2; i++) {
            for (int j = i + 1; j < plan.size() - 1; j++) {
                List<DeliveryStop> newPlan = twoOptSwap(plan, i, j);
                double newDistance = calculateTotalDistance(newPlan);

                if (newDistance < bestDistance) {
                    plan = newPlan;
                    bestDistance = newDistance;
                }
            }
        }

        return plan;
    }
}

实时需求预测

美团配送系统在用户下单前,就开始预调度骑手——根据历史数据和实时情况,预测接下来 30 分钟的订单分布,提前安排骑手到热门区域等待。

// 需求预测:提前 30 分钟预调度骑手
@Service
public class DemandPredictionService {

    @Autowired private HistoryOrderStore orderHistory;

    // 每 5 分钟跑一次预测
    @Scheduled(fixedRate = 5 * 60 * 1000)
    public void predictAndPreposition() {
        // 1. 按 1 公里网格统计历史订单分布
        Map<GeoHash, Integer> demandDistribution = predictDemand30min();

        // 2. 获取当前骑手分布
        Map<GeoHash, Integer> riderDistribution = riderService.getCurrentDistribution();

        // 3. 计算供需差
        Map<GeoHash, Double> gapMap = new HashMap<>();
        for (GeoHash cell : demandDistribution.keySet()) {
            double demand = demandDistribution.get(cell);
            double supply = riderDistribution.getOrDefault(cell, 0);
            gapMap.put(cell, demand - supply);
        }

        // 4. 发送预调度指令:引导骑手前往需求高的区域
        for (Map.Entry<GeoHash, Double> entry : gapMap.entrySet()) {
            if (entry.getValue() > 5) {  // 需求超过供给 5 单
                sendPrepositioningDirective(entry.getKey(), entry.getValue());
            }
        }
    }

    // 需求预测模型:基于历史数据 + 当前实时数据
    private Map<GeoHash, Integer> predictDemand30min() {
        LocalDateTime now = LocalDateTime.now();
        // 获取同时间(昨天/上周同期)的历史数据
        LocalDateTime[] historySlots = {
            now.minusDays(1),
            now.minusDays(7),
            now.minusDays(14)
        };

        Map<GeoHash, Integer> prediction = new HashMap<>();

        for (GeoHash cell : allCells) {
            int totalHistory = 0;
            for (LocalDateTime slot : historySlots) {
                totalHistory += orderHistory.getCount(cell, slot);
            }
            int avg = totalHistory / historySlots.length;

            // 加上实时趋势修正(当前 30 分钟的实时流量)
            double trendFactor = getRealTimeTrendFactor(cell);
            prediction.put(cell, (int) (avg * trendFactor));
        }

        return prediction;
    }
}

第四阶段:平台化与本地超脑(2019-至今)

本地超脑:统一调度引擎

2019 年,美团发布了「本地超脑」系统,将原本分散的调度能力统一到一个平台。本地超脑的核心是一个每秒可决策数百万次的实时调度引擎。

本地超脑的技术特点:

  • 实时计算:订单从创建到分配骑手,延迟 < 3 秒
  • 全局优化:不只是优化单个订单,而是优化整个配送网络的总效率
  • 弹性扩容:高峰期自动扩容,低谷期缩容,节省成本

架构启示

启示一:LBS 是本地生活服务的基础能力

美团所有业务的核心能力都是 LBS:搜索附近商家、计算配送距离、规划骑手路线。

LBS 的关键技术点:

  • 空间索引:GeoHash、Quadtree、R-Tree,用于高效查询地理空间数据
  • 距离计算:Haversine 公式(球面距离),或投影距离(平面近似)
  • Geofence:定义虚拟地理边界,触发区域事件(如进入配送范围)

启示二:供给侧优化比需求侧优化更重要

大多数平台优化的思路是「让用户更快找到商品」。但美团的经验是供给侧优化更重要——骑手预调度、多点配送、路径优化,这些供给侧改进带来的效率提升远超搜索排序优化。

建议:在做需求侧优化之前,先审视供给侧是否还有提升空间。

启示三:实时性有代价,要分优先级

美团的调度系统有几套不同的时效要求:

  • 核心链路(订单分配骑手):< 3 秒,必须优先保障
  • 预调度(提前安排骑手):< 30 秒,可接受延迟
  • 需求预测:< 5 分钟,离线计算即可

建议:不要把所有需求都做成实时。先区分优先级,再决定技术方案。

术语表

术语类型说明
王兴人名美团创始人兼 CEO,1980 年生于福建龙岩,曾创办校内网、饭否网
LBS(Location Based Service)技术名词基于位置的服务,根据用户地理位置提供相关服务
TMS(Transportation Management System)技术名词运输管理系统,用于调度和优化配送路线
GeoHash技术名词将经纬度编码为字符串的算法,相邻位置有相似前缀,便于范围查询
2-opt技术名词路径优化的局部搜索算法,通过交换路径片段来找更短的路线
供需匹配技术名词在特定时间地点匹配服务供给和需求,最大化资源利用率
O2O(Online to Offline)技术名词线上到线下,连接互联网用户与线下实体服务的商业模式
Geofence技术名词地理围栏,虚拟的地理边界,用于触发区域事件

总结

美团的技术演进,始终围绕一个核心命题:如何在秒级时间内,把百万骑手和千万订单精准匹配在一起

演进脉络

  • 2010-2013:团购时代,LBS 基础能力
  • 2013-2016:外卖扩张,O2O 实时化,骑手调度起步
  • 2016-2019:多点配送,路径优化,需求预测,配送网络优化
  • 2019-至今:本地超脑,统一调度引擎,实时决策

核心技术亮点

  • 智能调度:综合距离、等待时间、订单负载的骑手匹配算法
  • 多点配送:骑手同时接多单,2-opt 路径优化提升 40% 效率
  • 需求预测:基于历史数据和实时趋势,提前 30 分钟预调度骑手
  • 本地超脑:每秒百万次决策,< 3 秒完成订单分配

对普通项目的启发

  • LBS 能力是本地生活服务的基础,要优先建立
  • 供给侧优化往往比需求侧优化更有价值
  • 实时性有代价,按优先级分开处理
  • 多点配送的路径优化有成熟算法,不要自己造轮子