Uber 架构

2010 年 3 月,Travis Kalanick 在旧金山的 iPhone 4 发布会上,收到了一条来自朋友 Garrett Camp 的短信:「人们总是在需要打车的时候找不到车。我们应该做个 App,让车来找你。」

Travis 回复:「我马上过来。」

那一年,Uber 还叫 UberCab,只在旧金山市中心运营,车型仅限于豪华轿车(黑屏)。用户打开 App,按一下按钮,几分钟后一辆黑色林肯城市车就会出现在面前。那时候的 Uber 完全不像现在这么普及——它更像是一种「用钱换便利」的小众服务,只有科技圈和商旅人士知道。

但 Travis Kalanick 的野心远不止于此。他的长期愿景是:让每个人随时随地都能叫到车,而且价格合理。这个愿景后来演变成了共享出行经济的基础范式,也催生了全球估值最高的未上市科技公司之一。

公司画像

Uber 是一家总部位于旧金山的科技公司,核心业务是连接乘客和司机——乘客通过 App 叫车,司机通过 App 接单,Uber 从每笔订单中抽取佣金。截至 2024 年,Uber 业务覆盖全球 70+ 个国家、10,000+ 个城市,平台上有超过 500 万名司机,每季度完成超过 20 亿次行程

理解 Uber 技术挑战的关键,在于它的核心业务特征——实时匹配

  • 秒级响应:用户叫车后,等待时间超过 30 秒就会感到焦虑,超过 60 秒就可能取消
  • 位置强相关:司机和乘客都在地理空间中移动,匹配算法必须考虑实时位置
  • 供需动态变化:上下班高峰期、演唱会散场、雨雪天气,供需关系剧烈波动
  • 多边市场:平台同时服务乘客和司机,两边用户的行为互相影响
  • 全球化复杂度:不同城市的法规、货币、语言、支付方式都不同

这些特征让 Uber 的技术挑战跟 Google(搜索)、Netflix(视频)、阿里(电商)都有本质区别。Google 的核心是「找出用户想要的信息」,Netflix 的核心是「把视频稳定地送到用户屏幕上」,而 Uber 的核心是在正确的时间,把正确的人和正确的车匹配在一起

架构演进时间线

时间阶段核心技术解决的核心问题
2010-2013单体起步期Node.js + PostgreSQL快速验证商业模式
2013-2016微服务拆分期MySQL Schemaless + Cassandra + Kafka单体臃肿,数据库写入成为瓶颈
2016-至今混合存储 + 智能化Redis GEO + Flink + GraphQL + 实时调度全球化扩展,实时数据处理能力不足

第一阶段:单体起步(2010-2013)

起步:Node.js 单体架构

Uber 的第一批工程师是 Travis Kalanick 从团购网站 Stamped 挖来的。Stamped 用的是 Node.js,所以 Uber 的后端也顺理成章地选了 Node.js。

这个选择有它的道理:Node.js 的异步 I/O 模型天然适合 I/O 密集型的 Web 服务,开发效率高,团队上手快。在 2010 年,Node.js 是硅谷创业公司的热门选择。

// Uber 早期 Node.js 单体服务(示意)
const express = require('express');
const app = express();

// 叫车接口
app.post('/api/v1/ride', async (req, res) => {
    const { userId, pickupLocation, dropoffLocation } = req.body;

    // 1. 验证用户
    const user = await userService.findById(userId);
    if (!user) return res.status(401).json({ error: 'Unauthorized' });

    // 2. 查询附近司机(直接查 MySQL)
    const nearbyDrivers = await db.query(`
        SELECT * FROM drivers
        WHERE status = 'available'
        AND ST_DWithin(
            current_location,
            ST_Point(${pickupLocation.lng}, ${pickupLocation.lat}),
            5000  -- 5km 半径
        )
        ORDER BY distance
        LIMIT 10
    `);

    // 3. 匹配最优司机
    const bestDriver = matchingAlgorithm.findBestMatch(nearbyDrivers, user);

    // 4. 创建行程记录
    const ride = await rideService.create({
        userId,
        driverId: bestDriver.id,
        pickup: pickupLocation,
        dropoff: dropoffLocation,
        status: 'requested'
    });

    // 5. 通知司机
    await pushNotification.send(bestDriver.deviceToken, {
        type: 'NEW_RIDE_REQUEST',
        rideId: ride.id
    });

    res.json({ rideId: ride.id, status: ride.status });
});

早期 Uber 的架构就是一个标准的 Node.js 单体:一个代码库、一个数据库(PostgreSQL)、一个团队维护所有功能。所有模块(用户、司机、行程、支付、通知)都在同一个代码库里,模块之间直接函数调用。

快速验证商业模式

这个阶段的 Uber,技术上没有任何特别之处。真正重要的是商业模式验证——证明了「用手机叫车」这个需求是真实存在的,用户愿意为此付费。

2010 年底,Uber 扩展到纽约、芝加哥、西雅图等城市。2011 年拿到了第一笔 1100 万美元的 A 轮融资,同年获得了知名风投 Benchmark 的 3200 万美元 B 轮投资。2012 年,Uber 推出了更便宜的车型 UberX,开始从高端小众走向大众市场。

但代码库也在快速膨胀。到 2013 年,Uber 的代码库已经有数十万行,数百个路由处理器,数十个数据库表。单体的开发效率开始下降:每次部署都要全量测试,一个小 bug 可能影响整个系统。

第二阶段:微服务拆分(2013-2016)

为什么必须拆分

Uber 早期架构有三个致命问题:

问题一:数据库写入成为瓶颈。MySQL 在高并发写入场景下性能急剧下降。行程创建、状态更新、位置上报,每秒可能有数万次写入。MySQL 的主从复制在高并发下延迟飙升,导致读操作读到旧数据。

问题二:扩展困难。所有功能都在一个代码库里,一个团队的修改可能意外影响另一个团队的功能。数据库表之间的依赖错综复杂,改一个表可能影响数十个接口。

问题三:故障隔离差。一个模块的 bug 可能拖垮整个系统。2013 年,Uber 的支付模块出现了一次 bug,导致部分行程被重复扣款。由于所有模块共享同一个数据库连接池,支付模块的 bug 耗尽了整个应用的连接资源,导致地图、通知等其他功能全部不可用。

MySQL Schemaless

Uber 工程师在 2013 年开发了一个介于关系数据库和 NoSQL 之间的存储系统——Schemaless。它基于 MySQL,但提供了类似 NoSQL 的灵活写入能力:

  • schema-less 写入:可以在不修改表结构的情况下写入任意字段
  • MySQL 底层:保留 MySQL 的事务能力和强一致性
  • 水平分片:按 UUID 分片,每个分片独立扩展
-- Schemaless 的底层存储仍是 MySQL,但提供 KV + JSON 能力
-- 写入:不需要预定义字段
INSERT INTO trip_data (uuid, body, created_at)
VALUES (
    'ride-uuid-12345',
    '{"driver_id": 1001, "status": "in_progress",
      "route": {"distance_km": 5.2, "duration_min": 12},
      "surge_multiplier": 1.5}',
    NOW()
);

-- 读取:MySQL 的事务保证一致性
START TRANSACTION;
SELECT * FROM trip_data WHERE uuid = 'ride-uuid-12345';
-- 复杂的读写混合操作都可以在事务内完成
COMMIT;

Schemaless 的核心价值是让 Uber 在不放弃事务能力的前提下,获得了 NoSQL 的扩展性。这对金融级数据(如行程、支付)尤其重要。

Cassandra 的引入

2014 年,Uber 引入了 Cassandra 来存储事件日志和时序数据:

  • 高吞吐写入:Cassandra 的 LSM-Tree 存储引擎支持每秒百万级写入,非常适合 GPS 位置上报
  • 多数据中心复制:Cassandra 原生支持跨数据中心复制,Uber 在全球多个数据中心部署了 Cassandra 集群
  • 无需 schema:新增数据字段不需要 ALTER TABLE,对快速迭代友好
// Cassandra 存储司机位置轨迹(高吞吐写入)
@Service
public class DriverLocationEventStore {
    @Autowired private CassandraTemplate cassandraTemplate;

    private static final String TABLE = "driver_location_events";

    // 写入位置事件:每天数十亿条
    public void recordLocation(Long driverId, double lat, double lng,
                               long timestampMs) {
        // 按天分表:driver_location_events_2024_03_15
        String table = getTableForDate(new Date(timestampMs));

        String cql = "INSERT INTO " + table +
            " (driver_id, ts, lat, lng, geohash) VALUES (?, ?, ?, ?, ?)";

        cassandraTemplate.execute(cql,
            driverId, timestampMs, lat, lng, Geohash.encode(lat, lng));

        // 这条数据会被用于:行程分析、司机审核、地图优化
    }
}

Kafka 异步消息

Uber 在这个阶段引入了 Kafka 来解耦服务间通信:

  • 异步削峰:高峰期的大量事件(位置上报、行程状态变更)先进入 Kafka,后端服务按自己的节奏消费
  • 解耦:发消息的服务不需要知道谁在消费,消费的服务也不需要知道消息是谁发的
  • 容错:Kafka 持久化消息,消费失败可以重试,不会丢失数据
// Kafka 事件处理:行程状态变更
@Service
public class TripEventProducer {
    @Autowired private KafkaTemplate kafkaTemplate;

    // 发布事件:任何服务都可以订阅这个 topic
    public void publishTripCreated(Trip trip) {
        TripEvent event = new TripEvent();
        event.setEventType("TRIP_CREATED");
        event.setTripId(trip.getId());
        event.setTimestamp(System.currentTimeMillis());
        event.setPayload(trip.toJson());

        kafkaTemplate.send("trip-events", trip.getId().toString(), event);
    }

    public void publishTripCompleted(Trip trip) {
        TripEvent event = new TripEvent();
        event.setEventType("TRIP_COMPLETED");
        event.setTripId(trip.getId());
        event.setTimestamp(System.currentTimeMillis());
        event.setAmount(trip.getFare().getAmount());
        event.setDriverPayout(trip.getDriverPayout());

        kafkaTemplate.send("trip-events", trip.getId().toString(), event);
        // 消费方:支付系统、财务系统、数据分析系统
    }
}

// 消费事件:支付服务
@KafkaListener(topics = "trip-events", groupId = "payment-service")
public class PaymentEventConsumer {
    public void onEvent(TripEvent event) {
        switch (event.getEventType()) {
            case "TRIP_COMPLETED":
                // 触发支付扣款
                paymentService.chargeForTrip(event.getTripId());
                break;
            case "TRIP_CANCELLED":
                // 处理退款(如有预授权)
                paymentService.handleCancellation(event.getTripId());
                break;
        }
    }
}

第三阶段:混合存储与智能化(2016-至今)

为什么需要混合存储

到 2016 年,Uber 的数据规模和复杂度已经远超早期架构的设计预期:

  • 行程数据:需要强一致性(金融级),事务性写入,推荐关系数据库
  • 位置数据:每秒数百万次 GPS 上报,需要极高写入吞吐,推荐 Cassandra / Druid
  • 司机状态:需要毫秒级读取延迟(附近有没有车),推荐 Redis
  • 分析数据:需要 OLAP 能力,支持复杂查询,推荐 ClickHouse / Druid
  • 配置和元数据:需要强一致性的 KV,推荐 etcd / Zookeeper

没有一个单一数据库能同时满足所有需求,Uber 的选择是为不同类型的数据选择最适合的存储引擎

核心调度系统

DISPATCH(调度服务)是 Uber 技术栈中实时性要求最高的组件。用户按下叫车按钮的那一刻,DISPATCH 系统需要在 100ms 以内完成以下全部工作:

  1. 验证用户身份和支付方式
  2. 查询附近可用司机(Geofence 查询)
  3. 计算每个司机的匹配分数(距离、评分、实时供需)
  4. 选择最优司机
  5. 发送派单请求,等待司机响应
// 调度服务:核心决策流程(伪代码)
@Service
public class DispatchService {
    @Autowired private DriverService driverService;
    @Autowired private PricingService pricingService;
    @Autowired private LocationService locationService;
    @Autowired private MetricsService metrics;

    // 调度决策:SLA = 100ms
    public DispatchDecision dispatch(Long passengerId, Location pickup) {
        long startTime = System.nanoTime();

        // 1. 查询附近可用司机(Redis GEO 查询,< 5ms)
        //    这个查询本身要快,因为是整个链路的第一步
        List<Driver> nearbyDrivers = driverService.findNearby(
            pickup.getLat(), pickup.getLng(), 5000); // 5km 半径

        if (nearbyDrivers.isEmpty()) {
            metrics.record("dispatch.no_drivers", 1);
            return DispatchDecision.noDriversAvailable();
        }

        // 2. 过滤不在线的司机(Redis 缓存,< 2ms)
        //    司机 App 定期上报心跳,心跳超时视为离线
        List<Driver> onlineDrivers = nearbyDrivers.stream()
            .filter(d -> driverService.isOnline(d.getId()))
            .collect(Collectors.toList());

        // 3. 并行计算每个司机的匹配分数
        //    充分利用多核,不要串行
        List<DriverScore> scoredDrivers = onlineDrivers.parallelStream()
            .map(driver -> calculateScore(driver, pickup))
            .collect(Collectors.toList());

        // 4. 选择最优司机
        DriverScore bestMatch = scoredDrivers.stream()
            .max(Comparator.comparingDouble(DriverScore::getTotalScore))
            .orElseThrow();

        // 5. 尝试派单(乐观锁,失败则重试最多 3 次)
        DispatchResult result = attemptDispatchWithRetry(
            bestMatch.getDriver(), passengerId, 3);

        long latencyMs = (System.nanoTime() - startTime) / 1_000_000;
        metrics.record("dispatch.latency_ms", latencyMs);
        metrics.record("dispatch.candidates_count", scoredDrivers.size());

        return result;
    }

    // 计算匹配分数
    private DriverScore calculateScore(Driver driver, Location pickup) {
        // 距离分数:越近越高(指数衰减,5km 外接近 0)
        double distanceScore = calculateDistanceScore(
            driver.getCurrentLocation(), pickup);

        // 评分分数:越高越好(线性归一化)
        double ratingScore = driver.getRating() / 5.0;

        // 供需系数:高峰期加成
        double surgeMultiplier = pricingService.getSurgeMultiplier(pickup);

        // 综合分数:距离权重最高(60%),因为等待时间是用户最敏感的指标
        double totalScore =
            distanceScore * 0.6 +
            ratingScore * 0.3 +
            surgeMultiplier * 0.1;

        return new DriverScore(driver, totalScore,
            distanceScore, ratingScore, surgeMultiplier);
    }
}

DISPATCH 的延迟预算分配(总计 100ms):

步骤目标延迟说明
验证用户5ms读 Redis 缓存
Geofence 查询10ms查 Redis GEO
过滤在线司机5ms读 Redis 缓存
计算匹配分数20ms并行计算,CPU 密集
选择最优2ms排序和选择
派单 + 响应50ms网络请求 + 数据库写入
缓冲8ms应对 GC、抖动

Redis GEO:实时位置服务

Uber 用 Redis 的 GEO 功能存储司机的实时位置,核心操作是 GEOADD(更新位置)和 GEORADIUS(查询附近)。

Redis GEO 基于 Geohash 算法,将经纬度编码为一个字符串,使得「查询附近」操作变成了字符串范围查询,在 Redis 的有序集合(Sorted Set)中可以高效完成。

// Redis GEO 实现司机位置服务
@Service
public class DriverLocationService {
    @Autowired private RedisTemplate<String, String> redisTemplate;

    private static final String GEO_KEY = "driver:locations";

    // 更新司机位置:每 5 秒调用一次
    public void updateLocation(Long driverId, double longitude, double latitude) {
        // GEOADD:原子操作,同时设置经纬度和 Geohash
        redisTemplate.opsForGeo().add(
            GEO_KEY,
            new Point(longitude, latitude),
            driverId.toString()
        );

        // 同时更新司机在线状态(带过期时间)
        String availabilityKey = "driver:" + driverId + ":available";
        redisTemplate.opsForValue().set(availabilityKey, "1",
            Duration.ofMinutes(2)); // 2 分钟内无心跳视为离线
    }

    // 查询附近可用司机
    public List<Driver> findNearby(double longitude, double latitude,
                                   double radiusMeters) {
        // 构建圆形查询范围
        Circle circle = new Circle(
            new Point(longitude, latitude),
            new Distance(radiusMeters, MetricsUnit.METERS)
        );

        // GEORADIUS:返回指定半径内的所有成员及其距离
        GeoResults<GeoOperations.GeoLocation<String>> results =
            redisTemplate.opsForGeo().radius(GEO_KEY, circle);

        if (results == null) return Collections.emptyList();

        return results.getContent().stream()
            .filter(r -> {
                // 检查司机是否在线(心跳未超时)
                String key = "driver:" + r.getContent().getName() + ":available";
                return Boolean.TRUE.equals(
                    redisTemplate.hasKey(key));
            })
            .map(r -> {
                // 转换为完整的 Driver 对象
                Long driverId = Long.parseLong(r.getContent().getName());
                return driverService.findById(driverId);
            })
            .collect(Collectors.toList());
    }
}

支付系统

金融级系统的金额处理

Uber 支付系统的设计原则:金额必须精确,服务可以降级但不能计费错误

BigDecimal 而非 doubledoublefloat 在浮点数计算中存在精度损失,比如 0.1 + 0.2 = 0.30000000000000004。涉及金钱时,这是不可接受的。Uber 所有金额计算使用 BigDecimal(或自定义的 Money 类型)。

// 支付服务:使用事务保证一致性
@Service
public class PaymentService {
    @Autowired private PaymentRepository paymentRepository;
    @Autowired private TripRepository tripRepository;
    @Autowired private LedgerService ledgerService;
    @Autowired private PaymentGateway paymentGateway;

    @Transactional(isolation = Isolation.SERIALIZABLE)
    public PaymentResult chargeRide(Trip trip) {
        // 1. 幂等检查:防止重复扣款
        if (paymentRepository.existsByTripId(trip.getId())) {
            return PaymentResult.alreadyCharged(trip.getId());
        }

        // 2. 验证行程状态:只有完成的行程才能扣款
        if (trip.getStatus() != TripStatus.COMPLETED) {
            throw new IllegalStateException(
                "Trip not completed: " + trip.getStatus());
        }

        // 3. 计算费用(使用 BigDecimal,精确到分)
        Money fare = pricingService.calculateFare(trip);
        // platformFee = fare * 25%(Uber 佣金)
        Money platformFee = fare.multiply(PLATFORM_COMMISSION_RATE);
        // driverPay = fare - platformFee(司机实收)
        Money driverPay = fare.subtract(platformFee);

        // 4. 创建支付记录(先插入,状态为 PENDING)
        Payment payment = new Payment();
        payment.setId(UUID.randomUUID().toString());
        payment.setTripId(trip.getId());
        payment.setPassengerId(trip.getPassengerId());
        payment.setAmount(fare.getAmount()); // BigDecimal 类型
        payment.setCurrency(fare.getCurrency()); // "USD", "CNY" 等
        payment.setStatus(PaymentStatus.PENDING);
        paymentRepository.save(payment);

        try {
            // 5. 调用支付网关(Stripe、Braintree 等)
            PaymentGatewayResult gatewayResult =
                paymentGateway.charge(trip.getPassenger().getPaymentMethod(), fare);

            if (gatewayResult.isSuccess()) {
                // 6. 支付成功:更新状态 + 写入交易流水
                payment.setStatus(PaymentStatus.COMPLETED);
                payment.setGatewayTransactionId(
                    gatewayResult.getTransactionId());
                paymentRepository.save(payment);

                // 7. 更新司机账户(异步,通过 Kafka 消息触发)
                ledgerService.creditDriver(trip.getDriverId(), driverPay);

                metrics.record("payment.success", 1);
                return PaymentResult.success(payment);

            } else {
                // 8. 支付失败:记录原因,等待重试
                payment.setStatus(PaymentStatus.FAILED);
                payment.setFailureReason(gatewayResult.getErrorMessage());
                payment.setRetryCount(payment.getRetryCount() + 1);
                paymentRepository.save(payment);

                metrics.record("payment.failed", 1);
                return PaymentResult.failed(gatewayResult.getErrorMessage());
            }

        } catch (PaymentGatewayException e) {
            // 9. 网关超时:标记为待重试
            //    定期重试任务会处理这些待重试的支付
            payment.setStatus(PaymentStatus.RETRY);
            payment.setRetryCount(payment.getRetryRetry() + 1);
            paymentRepository.save(payment);

            metrics.record("payment.retry", 1);
            return PaymentResult.retry();
        }
    }
}

架构启示

启示一:按需选择存储引擎

Uber 从 MySQL 一把梭,到 Schemaless + Cassandra + Redis + Druid 的混合架构,每一步都是因为碰到了单引擎的边界:

  • MySQL 事务好,但写入吞吐不够 → 引入 Cassandra 存事件日志
  • Cassandra 查询能力弱,但 Redis 读写极快 → 用 Redis 做热点数据和缓存
  • 单机房 MySQL 有单点 → 跨机房多活

对普通项目的建议:不要迷信 NoSQL,MySQL 在 90% 的业务场景下依然是最佳选择。先用 MySQL 解决问题,等碰到具体的性能瓶颈,再考虑引入其他存储引擎。不要因为「大家都用 MongoDB」就去用 MongoDB。

启示二:低延迟系统的设计要点

Uber 调度系统的 100ms SLA 拆解了每个环节的预算。要做到这一点,有几个关键设计:

内存优先:热点数据(司机位置、用户信息、定价系数)全部放在 Redis,避免磁盘 I/O。

并行计算:多个司机的匹配分数计算是完全独立的,parallelStream 可以将计算分散到多个 CPU 核心,20ms 内完成数百个候选司机的评分。

简化链路:从用户按下按钮到派单成功,整个链路只有 6 个关键步骤。每多一步网络调用,就要多预留 20-50ms 的缓冲。

预计算和预加载:热门路线沿途的司机位置会提前预加载到内存,定价系数每小时刷新到 Redis。尽可能减少实时计算的比例。

启示三:金融级系统的金额处理

Uber 支付系统展示了几个关键原则:

始终使用 BigDecimal:任何涉及金钱的计算、存储、传输,必须用 BigDecimal,不能用 double/float。这是最容易犯的错误,也是后果最严重的错误。

支付和行程状态同步:支付必须和行程状态在同一事务内更新(或通过消息队列保证最终一致)。不能让用户看到「行程已完成但扣款失败」的矛盾状态。

幂等性设计:支付网关调用、数据库写入必须支持幂等(同一个操作重复执行结果相同)。网络超时后重试不会导致重复扣款。

最终一致性优先:分布式环境下,强一致性的代价太高(需要分布式锁或两阶段提交)。Uber 支付系统的策略是:写入操作尽量保证强一致,读操作允许短暂的不一致(最终一致),通过补偿机制修复不一致状态。

术语表

术语类型说明
Travis Kalanick人名Uber 联合创始人兼前 CEO,1977 年生于旧金山,以激进扩张风格著称,2019 年被迫离职
Garrett Camp人名Uber 联合创始人,2007 年创办 Stamped,2009 年与 Travis Kalanick 共同创立 UberCab
Benchmark公司名美国顶级风投机构,2011 年领投 Uber 3200 万美元,合伙人 Bill Gurley 成为 Uber 董事
Geofence技术名词地理围栏,在地图上定义的一个虚拟边界,当用户或车辆进入/离开该区域时触发特定操作
Geohash技术名词将经纬度编码为字符串的算法,相邻位置有相似前缀,便于在 Redis Sorted Set 中做范围查询
Redis GEO技术名词Redis 3.2 引入的地理位置功能,支持 GEOADD、GEORADIUS 等命令,常用于附近的人/车查询
LSM-Tree(Log-Structured Merge Tree)技术名词一种写入优化的存储数据结构,先写内存再批量刷盘,适合高吞吐写入场景,Cassandra、HBase、RocksDB 均采用此结构
GraphQL技术名词Facebook 开源的 API 查询语言,客户端可以精确指定需要哪些字段,减少数据传输量
Flink技术名词Apache 顶级项目,分布式流处理引擎,支持实时数据流上的复杂计算,Uber 用于实时数据分析和事件处理
幂等性(Idempotency)技术名词同一操作重复执行结果不变的性质,对金融系统和重试机制至关重要
SLA技术名词Service Level Agreement,服务等级协议,定义服务的响应时间、可用性等承诺指标
Serialization技术名词序列化,内存中的对象转换为字节流的过程,用于网络传输或持久化存储
Kafka技术名词Apache 顶级项目,分布式流处理平台,Uber 用于异步消息传递和事件驱动架构
Ledger(账本)业务名词记录资金流向的系统,Uber 的账本服务处理司机收入、平台佣金、奖励金等财务数据

总结

Uber 是实时调度系统的典型案例,其技术演进始终围绕一个核心命题:如何在秒级时间内,把人和车精准匹配在一起?

演进脉络

  • 2010-2013:Node.js 单体 + PostgreSQL,快速验证商业模式
  • 2013-2016:微服务拆分 + Schemaless + Cassandra + Kafka,解决扩展性和数据吞吐问题
  • 2016-至今:混合存储架构 + Redis GEO 实时位置 + Flink 实时计算

核心技术亮点

  • 调度系统:100ms 内完成全链路决策,parallelStream 并行计算匹配分数
  • 位置服务:Redis GEO 支撑每秒百万次位置查询,Geofence 精确圈选可用车辆
  • 支付系统BigDecimal 精确计费,事务保证一致性,幂等设计防止重复扣款
  • 动态定价:实时供需关系驱动价格调整,高峰期通过价格杠杆平衡供需

对普通项目的启发

  • 按业务数据特征选择存储引擎,不要一把梭
  • 低延迟系统要专门优化,内存 > 磁盘,同步 > 异步(对于核心链路)
  • 金融系统金额必须用 BigDecimal,幂等性是支付系统的生命线