土法炼钢兴趣小组的算法知识备份

【系统架构设计百科】阿里巴巴架构:双十一的流量工程

文章导航

分类入口
architecture
标签入口
#Alibaba#Double-11#unitization#TDDL#full-link-stress-testing

目录

2024 年双十一,天猫交易峰值达到 58.3 万笔/秒。这个数字背后,是阿里巴巴十余年架构演进的结晶——从最初的单体 LAMP 栈,到如今横跨全球多个数据中心的单元化架构(Logical Data Center,LDC),每一次双十一都是对系统极限的真实检验。本文将从单元化架构、分库分表中间件、全链路压测、弹性伸缩、高可用交易链路、消息中间件、分布式事务七个维度,完整拆解阿里巴巴流量工程的核心设计。


一、阿里巴巴技术架构演进概览

1.1 四个关键阶段

阿里巴巴的技术架构大致经历了四个阶段:

阶段 时间范围 核心特征 代表技术
单体时代 2003-2007 LAMP 单体应用,Oracle 数据库 PHP,Oracle
服务化时代 2008-2012 去 IOE,SOA 拆分 HSF,TDDL,OceanBase
异地多活时代 2013-2017 单元化部署,三地五中心 LDC,DRC,GSLB
云原生时代 2018-至今 全面上云,Serverless,Service Mesh ACK,MSE,ARMS

1.2 去 IOE 的战略决策

2009 年,阿里巴巴正式启动”去 IOE”(IBM 小型机、Oracle 数据库、EMC 存储)战略。这并非单纯的技术偏好,而是业务增速远超硬件垂直扩展能力后的必然选择。每年双十一的流量增长率在 30%-50%,而 Oracle RAC(Real Application Cluster)的扩展上限约在 16 节点左右,且授权费用随节点数线性增长。

去 IOE 的核心路径:

  1. 将 Oracle 替换为 MySQL(MySQL),通过分库分表实现水平扩展
  2. 用 PC 服务器集群替代 IBM 小型机
  3. 用分布式文件系统(TFS,Tair)替代 EMC 集中式存储

1.3 架构全景

以下是阿里巴巴双十一交易系统的整体架构分层:

graph TB
    subgraph 接入层
        CDN["CDN(全球节点)"]
        GSLB["GSLB 全局负载均衡"]
        Tengine["Tengine / Aserver"]
    end

    subgraph 网关层
        Gateway["统一接入网关"]
        RateLimit["限流模块(Sentinel)"]
        Auth["鉴权服务"]
    end

    subgraph 业务层
        Trade["交易中心"]
        Inventory["库存中心"]
        Order["订单中心"]
        Payment["支付中心"]
        User["用户中心"]
        Promotion["营销中心"]
    end

    subgraph 中间件层
        HSF["HSF / Dubbo RPC"]
        RocketMQ["RocketMQ 消息"]
        TDDL["TDDL 数据层"]
        Diamond["Diamond 配置中心"]
        Tair["Tair 分布式缓存"]
    end

    subgraph 数据层
        MySQL_Group["MySQL 主从集群"]
        OceanBase["OceanBase 分布式数据库"]
        Lindorm["Lindorm 宽表"]
    end

    CDN --> GSLB
    GSLB --> Tengine
    Tengine --> Gateway
    Gateway --> RateLimit
    Gateway --> Auth
    RateLimit --> Trade
    RateLimit --> Inventory
    RateLimit --> Order
    Trade --> HSF
    Trade --> RocketMQ
    Trade --> TDDL
    Inventory --> Tair
    Order --> TDDL
    Payment --> HSF
    HSF --> MySQL_Group
    TDDL --> MySQL_Group
    TDDL --> OceanBase
    Tair --> Lindorm

二、单元化架构(LDC)设计

2.1 为什么需要单元化

传统的集中式部署在应对双十一级别的流量时面临三个根本性问题:

  1. 单机房容量上限:即使是阿里张北数据中心,单机房物理机数量也有上限,网络带宽存在瓶颈
  2. 故障爆炸半径不可控:一次网络抖动可能影响全部用户
  3. 扩容周期过长:采购服务器到上架调试至少需要 2-3 个月

单元化架构的核心思想是:将用户按照某个维度(通常是用户 ID)进行分片,每个分片的全部业务流量在一个逻辑单元(Unit)内闭环处理。

2.2 LDC 架构模型

LDC 的全称是 Logical Data Center(逻辑数据中心),它是阿里巴巴单元化架构的工程实现。

graph LR
    subgraph 杭州中心
        GZone_HZ["GZone(全局服务)"]
        RZone_HZ_1["RZone-01"]
        RZone_HZ_2["RZone-02"]
        RZone_HZ_3["RZone-03"]
    end

    subgraph 上海中心
        RZone_SH_1["RZone-04"]
        RZone_SH_2["RZone-05"]
        RZone_SH_3["RZone-06"]
    end

    subgraph 张北中心
        GZone_ZB["GZone(全局服务备)"]
        RZone_ZB_1["RZone-07"]
        RZone_ZB_2["RZone-08"]
    end

    GSLB_Global["GSLB 全局调度"] --> RZone_HZ_1
    GSLB_Global --> RZone_HZ_2
    GSLB_Global --> RZone_HZ_3
    GSLB_Global --> RZone_SH_1
    GSLB_Global --> RZone_SH_2
    GSLB_Global --> RZone_SH_3
    GSLB_Global --> RZone_ZB_1
    GSLB_Global --> RZone_ZB_2

    RZone_HZ_1 -.->|只读| GZone_HZ
    RZone_SH_1 -.->|只读| GZone_HZ
    RZone_ZB_1 -.->|只读| GZone_ZB

    GZone_HZ <-->|数据同步| GZone_ZB

2.3 三种 Zone 类型

LDC 将服务划分为三种 Zone 类型:

RZone(Region Zone):按用户分片部署的业务单元,承载交易、订单、库存等核心写操作。每个 RZone 包含完整的业务服务栈和对应分片的数据库。流量在 RZone 内闭环,不跨 Zone 调用。

GZone(Global Zone):部署全局共享服务,如商品信息、类目数据、卖家信息等。这类数据写少读多,不适合按用户分片。GZone 在所有数据中心间做数据同步,各 RZone 就近读取本地 GZone 副本。

CZone(City Zone):介于 RZone 和 GZone 之间的折中方案。部署在每个城市级数据中心,用于承载那些有一定写入量但不需要全局唯一写入点的服务。

2.4 用户分片路由

用户分片的路由规则嵌入在每一层:

/**
 * 用户 ID 路由到 RZone 的核心逻辑
 * 使用 userId 的后两位作为分片键,将用户映射到 00-99  100 个逻辑分片
 * 再通过路由表将逻辑分片映射到物理 RZone
 */
public class UnitRouter {

    // 路由表:逻辑分片 -> 物理 RZone
    // 通过配置中心动态下发,支持运行时切换
    private volatile Map<Integer, String> routeTable;

    /**
     * 根据用户 ID 获取目标 RZone
     */
    public String getTargetUnit(long userId) {
        // 取用户 ID 后两位,得到 0-99 的逻辑分片号
        int slot = (int) (userId % 100);
        // 查路由表获取物理 RZone
        String targetZone = routeTable.get(slot);
        if (targetZone == null) {
            throw new RouteException("No route found for slot: " + slot);
        }
        return targetZone;
    }

    /**
     * 切换指定分片的路由目标
     * 用于单元扩缩容和故障切换
     */
    public void switchRoute(int slot, String newZone) {
        Map<Integer, String> newTable = new HashMap<>(routeTable);
        newTable.put(slot, newZone);
        this.routeTable = Collections.unmodifiableMap(newTable);
    }

    /**
     * 批量切换分片路由
     * 用于整个 RZone 故障时的批量迁移
     */
    public void batchSwitchRoute(String fromZone, String toZone) {
        Map<Integer, String> newTable = new HashMap<>(routeTable);
        for (Map.Entry<Integer, String> entry : newTable.entrySet()) {
            if (entry.getValue().equals(fromZone)) {
                newTable.put(entry.getKey(), toZone);
            }
        }
        this.routeTable = Collections.unmodifiableMap(newTable);
    }
}

2.5 数据同步:DRC

DRC(Data Replication Center,数据复制中心)负责跨数据中心的数据同步。它解析 MySQL 的 binlog,以近实时的方式将数据变更复制到其他数据中心。

DRC 的核心设计要点:

  1. 低延迟:正常情况下端到端延迟在 200ms 以内,跨城同步在 1s 以内
  2. 冲突检测:对于 GZone 的双向同步,DRC 采用”最后写入胜出”(Last Write Wins)策略,并结合业务层的乐观锁做冲突兜底
  3. 精确过滤:只复制需要同步的表和字段,减少带宽消耗
  4. 断点续传:基于 GTID(Global Transaction ID)实现断点续传,网络恢复后自动补数据
# DRC 同步任务配置示例
drc_task:
  name: "gzone_product_sync"
  source:
    cluster: "hz-gzone-mysql"
    database: "product_db"
    tables:
      - name: "product_info"
        columns: ["id", "title", "price", "status", "gmt_modified"]
      - name: "category"
        columns: "*"
  target:
    cluster: "zb-gzone-mysql"
    database: "product_db"
  sync_mode: "bidirectional"
  conflict_strategy: "last_write_wins"
  filter:
    exclude_patterns:
      - "tmp_*"
      - "*_backup"
  monitor:
    delay_threshold_ms: 500
    alert_channels: ["dingding", "sms"]

三、TDDL 分库分表中间件

3.1 TDDL 的定位

TDDL(Taobao Distributed Data Layer)是阿里巴巴自研的分布式数据访问中间件。它以 JDBC 驱动的形式嵌入应用,在 SQL 层面实现分库分表路由、读写分离和容灾切换,对上层业务代码几乎透明。

TDDL 在整体数据链路中的位置:

应用代码
  ↓
MyBatis / JPA
  ↓
TDDL(SQL 解析 → 路由 → 执行 → 结果合并)
  ↓
JDBC Driver
  ↓
MySQL 集群(主库 + 从库)

3.2 分库分表策略

TDDL 支持多种分片策略,最常用的是哈希取模和范围分片的组合:

/**
 * TDDL 分库分表规则配置示例
 * 以订单表为例:按 buyer_id  8 个库,每个库 32 张表
 * 总共 256 个分片,支撑千万级 TPS
 */
public class OrderShardingRule implements ShardingRule {

    private static final int DB_COUNT = 8;
    private static final int TABLE_COUNT_PER_DB = 32;

    /**
     * 计算目标库索引
     * 使用 buyer_id 做分库键,保证同一买家的订单落在同一个库
     */
    @Override
    public int calculateDbIndex(long buyerId) {
        // 先对总分片数取模,再除以每库表数
        int totalShardIndex = (int) (buyerId % (DB_COUNT * TABLE_COUNT_PER_DB));
        return totalShardIndex / TABLE_COUNT_PER_DB;
    }

    /**
     * 计算目标表后缀
     */
    @Override
    public int calculateTableIndex(long buyerId) {
        int totalShardIndex = (int) (buyerId % (DB_COUNT * TABLE_COUNT_PER_DB));
        return totalShardIndex % TABLE_COUNT_PER_DB;
    }

    /**
     * 生成物理表名
     * 例如:order_0023 表示第 23 号分片表
     */
    @Override
    public String getPhysicalTableName(long buyerId) {
        int tableIndex = calculateTableIndex(buyerId);
        return String.format("order_%04d", tableIndex);
    }

    /**
     * 生成物理库名
     * 例如:order_db_03 表示第 3 号分片库
     */
    @Override
    public String getPhysicalDbName(long buyerId) {
        int dbIndex = calculateDbIndex(buyerId);
        return String.format("order_db_%02d", dbIndex);
    }
}

3.3 SQL 解析与路由

TDDL 的 SQL 处理流程包含四个阶段:

  1. SQL 解析:使用自研的 SQL Parser(FastSQL)将 SQL 文本解析为 AST(抽象语法树),提取分片键的值
  2. 路由计算:根据分片规则和分片键值,计算出目标物理库表
  3. SQL 改写:将逻辑表名替换为物理表名,修正 LIMIT、ORDER BY 等子句
  4. 结果合并:跨分片查询时,合并多个分片的结果集,执行全局排序、聚合、分页
/**
 * TDDL SQL 执行流程简化示例
 */
public class TddlExecutor {

    private final SqlParser parser;
    private final ShardingRouter router;
    private final ConnectionPool connPool;

    /**
     * 执行一条 SQL
     */
    public ResultSet execute(String sql, Object[] params) throws SQLException {
        // 阶段一:SQL 解析
        SqlStatement stmt = parser.parse(sql);
        ShardingKey key = stmt.extractShardingKey(params);

        // 阶段二:路由计算
        List<TargetNode> targets = router.route(stmt, key);

        if (targets.size() == 1) {
            // 单分片查询:直接下推
            return executeSingle(targets.get(0), stmt, params);
        }

        // 阶段三:跨分片查询 - 并行执行
        List<Future<ResultSet>> futures = new ArrayList<>();
        for (TargetNode target : targets) {
            futures.add(asyncExecute(target, stmt, params));
        }

        // 阶段四:结果合并
        List<ResultSet> results = collectResults(futures);
        return mergeResults(results, stmt);
    }

    /**
     * 合并多分片结果
     * 处理 ORDER BYGROUP BYLIMIT 等聚合操作
     */
    private ResultSet mergeResults(List<ResultSet> results, SqlStatement stmt) {
        if (stmt.hasOrderBy()) {
            return new MergeSortResultSet(results, stmt.getOrderByColumns());
        }
        if (stmt.hasGroupBy()) {
            return new GroupByMergeResultSet(results, stmt.getGroupByColumns());
        }
        return new SimpleUnionResultSet(results);
    }
}

3.4 读写分离与权重配置

# TDDL 数据源配置示例
tddl:
  app_name: "trade-center"
  group_rules:
    - group: "order_db_00"
      master: "10.0.1.10:3306"
      slaves:
        - host: "10.0.1.11:3306"
          weight: 5
        - host: "10.0.1.12:3306"
          weight: 3
        - host: "10.0.1.13:3306"
          weight: 2
      read_strategy: "weight_random"
      slave_delay_threshold_ms: 1000
      # 从库延迟超过阈值时自动切到主库读
      failover_on_delay: true

    - group: "order_db_01"
      master: "10.0.2.10:3306"
      slaves:
        - host: "10.0.2.11:3306"
          weight: 5
        - host: "10.0.2.12:3306"
          weight: 5
      read_strategy: "round_robin"
      slave_delay_threshold_ms: 800
      failover_on_delay: true

3.5 分库分表的常见陷阱

陷阱 现象 解决方案
跨分片 JOIN SQL 性能急剧下降 冗余数据或在应用层做关联
全表扫描 查询未命中分片键,扫描所有分片 建立路由索引表或使用搜索引擎
分布式唯一 ID 自增 ID 跨库冲突 使用 Snowflake 算法或 Leaf 发号器
数据倾斜 某些分片数据量远大于其他分片 采用虚拟桶映射,支持动态调整
扩容迁移 增加分片数需要大规模数据迁移 预分配足够分片,使用逻辑分片到物理分片的映射表
分布式事务 跨分片写入无法保证 ACID 使用 TCC 或 Saga 模式

四、全链路压测平台

4.1 全链路压测的必要性

双十一的流量模式极其特殊:零点峰值可能是日常流量的 20-30 倍,且持续时间短(通常只有前 5 分钟是真正的流量高峰)。传统的单服务压测无法暴露系统间的级联故障和资源竞争问题。

全链路压测(Full-Link Stress Testing)是指在生产环境中,使用与真实流量高度相似的模拟流量,对完整业务链路进行压力测试。阿里巴巴从 2013 年开始建设全链路压测平台,是业界最早将这一实践工业化的公司之一。

4.2 压测流量与真实流量的隔离

全链路压测最大的技术挑战是:如何在生产环境中注入大量压测流量,同时保证不污染真实业务数据。

/**
 * 压测流量标记传递机制
 * 通过 ThreadLocal 在整个调用链中传递压测标记
 */
public class ShadowFlag {

    private static final ThreadLocal<Boolean> IS_SHADOW = ThreadLocal.withInitial(() -> false);

    /**
     * 标记当前请求为压测流量
     */
    public static void markAsShadow() {
        IS_SHADOW.set(true);
    }

    /**
     * 清除压测标记
     */
    public static void clear() {
        IS_SHADOW.remove();
    }

    /**
     * 判断当前请求是否为压测流量
     */
    public static boolean isShadow() {
        return IS_SHADOW.get();
    }
}

/**
 * 压测数据路由 - 数据源层面的隔离
 * 压测流量写入影子表(Shadow Table),不影响正式数据
 */
public class ShadowDataSourceRouter {

    private final DataSource normalDataSource;
    private final DataSource shadowDataSource;

    public ShadowDataSourceRouter(DataSource normal, DataSource shadow) {
        this.normalDataSource = normal;
        this.shadowDataSource = shadow;
    }

    /**
     * 根据压测标记选择数据源
     */
    public Connection getConnection() throws SQLException {
        if (ShadowFlag.isShadow()) {
            return shadowDataSource.getConnection();
        }
        return normalDataSource.getConnection();
    }
}

/**
 * 影子表 SQL 改写器
 * 将正式表名改写为影子表名
 * 例如:order -> __shadow_order
 */
public class ShadowTableRewriter {

    private static final String SHADOW_PREFIX = "__shadow_";

    /**
     * 改写 SQL 中的表名为影子表名
     */
    public String rewrite(String sql, List<String> tableNames) {
        if (!ShadowFlag.isShadow()) {
            return sql;
        }
        String rewritten = sql;
        for (String table : tableNames) {
            rewritten = rewritten.replace(table, SHADOW_PREFIX + table);
        }
        return rewritten;
    }
}

4.3 压测流量模型构建

压测流量模型需要尽可能还原双十一零点的真实访问模式:

"""
压测流量模型生成器
根据历史数据分析,构建符合双十一流量分布的压测模型
"""

import random
import time
from dataclasses import dataclass
from typing import List


@dataclass
class TrafficModel:
    """流量模型定义"""
    qps_curve: List[float]          # 每秒 QPS 曲线
    api_distribution: dict          # API 调用比例
    user_behavior_chain: List[str]  # 用户行为链
    hot_item_ratio: float           # 热点商品比例
    cart_size_distribution: dict    # 购物车大小分布


def build_double11_model() -> TrafficModel:
    """构建双十一零点流量模型"""

    # QPS 曲线:模拟零点前 10 秒到零点后 300 秒的流量变化
    # 零点前缓慢上升,零点瞬间达到峰值,然后缓慢下降
    qps_curve = []
    for t in range(-10, 301):
        if t < 0:
            # 零点前:指数上升
            qps = 50000 * (2 ** (t / 3.0))
        elif t == 0:
            # 零点:峰值
            qps = 583000
        elif t <= 5:
            # 峰值持续 5 秒
            qps = 583000 * (1 - 0.02 * t)
        elif t <= 60:
            # 快速下降
            qps = 500000 * (0.9 ** ((t - 5) / 10.0))
        else:
            # 平稳期
            qps = 200000 + random.gauss(0, 5000)
        qps_curve.append(max(0, qps))

    # API 调用比例:基于历史数据
    api_distribution = {
        "query_item": 0.35,          # 商品查询
        "add_cart": 0.15,            # 加购物车
        "create_order": 0.20,        # 下单
        "pay_order": 0.15,           # 支付
        "query_order": 0.10,         # 查询订单
        "query_logistics": 0.05,     # 查物流
    }

    # 用户行为链:典型的购买路径
    user_behavior_chain = [
        "browse_item",
        "add_to_cart",
        "checkout",
        "confirm_order",
        "pay",
        "query_order_status",
    ]

    return TrafficModel(
        qps_curve=qps_curve,
        api_distribution=api_distribution,
        user_behavior_chain=user_behavior_chain,
        hot_item_ratio=0.2,
        cart_size_distribution={1: 0.3, 2: 0.25, 3: 0.2, 5: 0.15, 10: 0.1},
    )


def generate_pressure_plan(model: TrafficModel, target_peak_qps: int) -> dict:
    """生成压测计划"""
    scale_factor = target_peak_qps / max(model.qps_curve)
    scaled_curve = [int(qps * scale_factor) for qps in model.qps_curve]

    return {
        "total_duration_sec": len(scaled_curve),
        "peak_qps": target_peak_qps,
        "qps_curve": scaled_curve,
        "api_distribution": model.api_distribution,
        "estimated_total_requests": sum(scaled_curve),
    }

4.4 压测数据清理

压测结束后必须清理影子数据,防止残留数据影响后续业务:

-- 影子表数据清理脚本
-- 在压测结束后执行

-- 清理影子订单表
TRUNCATE TABLE __shadow_order;

-- 清理影子支付表
TRUNCATE TABLE __shadow_payment;

-- 清理影子库存变更日志
DELETE FROM __shadow_inventory_log
WHERE gmt_create >= '2026-10-20 00:00:00'
  AND gmt_create <= '2026-10-21 00:00:00';

-- 重置影子序列号
ALTER TABLE __shadow_order AUTO_INCREMENT = 1;

-- 验证清理结果
SELECT '__shadow_order' AS table_name, COUNT(*) AS remaining_rows
FROM __shadow_order
UNION ALL
SELECT '__shadow_payment', COUNT(*)
FROM __shadow_payment
UNION ALL
SELECT '__shadow_inventory_log', COUNT(*)
FROM __shadow_inventory_log;

4.5 压测平台架构

graph TB
    subgraph 压测控制台
        Console["压测控制台 Web"]
        SceneManager["场景管理"]
        ReportEngine["报告引擎"]
    end

    subgraph 流量引擎
        TrafficEngine["流量调度引擎"]
        AgentCluster["压测 Agent 集群"]
        ModelGen["流量模型生成"]
    end

    subgraph 数据隔离层
        ShadowRouter["影子路由"]
        ShadowDB["影子数据库"]
        ShadowMQ["影子消息队列"]
        ShadowCache["影子缓存空间"]
    end

    subgraph 监控层
        RealTimeMonitor["实时监控大盘"]
        AlarmSystem["异常熔断系统"]
        CompareAnalysis["基线对比分析"]
    end

    Console --> TrafficEngine
    SceneManager --> ModelGen
    ModelGen --> TrafficEngine
    TrafficEngine --> AgentCluster
    AgentCluster -->|压测流量| ShadowRouter
    ShadowRouter --> ShadowDB
    ShadowRouter --> ShadowMQ
    ShadowRouter --> ShadowCache
    AgentCluster -->|指标上报| RealTimeMonitor
    RealTimeMonitor --> AlarmSystem
    AlarmSystem -->|自动停止| TrafficEngine
    RealTimeMonitor --> CompareAnalysis
    CompareAnalysis --> ReportEngine

五、弹性伸缩与容量规划

5.1 容量评估模型

双十一的容量规划始于大促前 3 个月。容量评估的核心公式:

所需机器数 = 峰值 QPS × 单请求资源消耗 × 安全系数 / 单机处理能力

其中:
- 峰值 QPS:根据业务预测,通常取去年峰值的 1.5 倍
- 单请求资源消耗:CPU 时间 + 内存占用 + IO 次数的综合指标
- 安全系数:通常取 1.5-2.0,预留容灾和突发余量
- 单机处理能力:通过单机压测得出的稳定处理上限

5.2 弹性伸缩策略

/**
 * 弹性伸缩决策引擎
 * 根据实时指标和预测模型决定扩缩容
 */
public class AutoScalingEngine {

    // 扩容触发阈值
    private static final double CPU_SCALE_UP_THRESHOLD = 0.70;
    // 缩容触发阈值
    private static final double CPU_SCALE_DOWN_THRESHOLD = 0.30;
    // 最小观测窗口(秒)
    private static final int OBSERVATION_WINDOW = 60;
    // 冷却时间(秒),避免频繁伸缩
    private static final int COOLDOWN_PERIOD = 300;

    /**
     * 计算目标实例数
     */
    public ScalingDecision evaluate(ClusterMetrics metrics) {
        double avgCpu = metrics.getAvgCpuUsage();
        double avgMemory = metrics.getAvgMemoryUsage();
        int currentInstances = metrics.getCurrentInstanceCount();
        double avgRt = metrics.getAvgResponseTimeMs();

        // 多维度综合判断
        if (avgCpu > CPU_SCALE_UP_THRESHOLD || avgRt > 200) {
            // 计算需要扩容到多少实例
            double targetCpuUsage = 0.50;
            int targetInstances = (int) Math.ceil(
                currentInstances * avgCpu / targetCpuUsage
            );
            // 单次扩容不超过当前实例数的 50%
            int maxScaleUp = (int) (currentInstances * 1.5);
            targetInstances = Math.min(targetInstances, maxScaleUp);

            return new ScalingDecision(
                ScalingAction.SCALE_UP,
                targetInstances,
                String.format("CPU=%.1f%%, RT=%.0fms", avgCpu * 100, avgRt)
            );
        }

        if (avgCpu < CPU_SCALE_DOWN_THRESHOLD && avgRt < 50) {
            int targetInstances = (int) Math.ceil(
                currentInstances * avgCpu / 0.50
            );
            // 至少保留最小实例数
            targetInstances = Math.max(targetInstances, getMinInstances());

            return new ScalingDecision(
                ScalingAction.SCALE_DOWN,
                targetInstances,
                String.format("CPU=%.1f%%, RT=%.0fms", avgCpu * 100, avgRt)
            );
        }

        return new ScalingDecision(ScalingAction.NO_OP, currentInstances, "stable");
    }

    private int getMinInstances() {
        return 3;
    }
}

5.3 混部与资源借调

双十一期间,阿里巴巴通过”资源借调”机制在不同业务间调配计算资源:

  1. 离线让在线:大促期间暂停非紧急的离线计算任务(如数据仓库 ETL),释放计算资源给在线服务
  2. 低优先级让高优先级:搜索推荐等非核心链路降级运行,将资源让给交易支付链路
  3. 弹性容器:使用 Pouch 容器在秒级完成资源分配和服务部署
资源调配策略 可释放资源比例 恢复时间 业务影响
离线任务暂停 30%-40% 分钟级 离线报表延迟
搜索降级 10%-15% 秒级 搜索相关性下降
推荐降级 5%-10% 秒级 推荐精度下降
日志采样 5%-8% 秒级 日志完整性降低
图片降质 3%-5% 秒级 图片清晰度降低

六、交易链路的高可用设计

6.1 多级限流体系

阿里巴巴的限流体系从接入层到服务层共分四级:

/**
 * Sentinel 限流规则配置示例
 * 实现令牌桶 + 滑动窗口的多层限流
 */
public class SentinelConfig {

    /**
     * 配置交易接口的限流规则
     */
    public static void initFlowRules() {
        List<FlowRule> rules = new ArrayList<>();

        // 第一层:接口级别限流(集群维度)
        FlowRule clusterRule = new FlowRule();
        clusterRule.setResource("createOrder");
        clusterRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        clusterRule.setCount(100000);
        clusterRule.setClusterMode(true);
        clusterRule.setClusterConfig(
            new ClusterFlowConfig()
                .setFlowId(1001L)
                .setThresholdType(ClusterRuleConstant.FLOW_THRESHOLD_GLOBAL)
        );
        rules.add(clusterRule);

        // 第二层:单机级别限流
        FlowRule localRule = new FlowRule();
        localRule.setResource("createOrder");
        localRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        localRule.setCount(500);
        localRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
        localRule.setWarmUpPeriodSec(10);
        rules.add(localRule);

        // 第三层:热点参数限流(防止单商品被刷)
        // 限制单个商品 ID 的下单 QPS
        ParamFlowRule hotRule = new ParamFlowRule();
        hotRule.setResource("createOrder");
        hotRule.setParamIdx(0);
        hotRule.setCount(50);
        hotRule.setDurationInSec(1);

        FlowRuleManager.loadRules(rules);
    }
}

6.2 多级降级策略

降级级别 触发条件 降级动作 恢复策略
L0 预警 RT > 100ms 或 CPU > 60% 关闭非核心功能(推荐、评价) 指标恢复后自动开启
L1 轻度 RT > 300ms 或 错误率 > 1% 返回缓存数据,关闭个性化 人工确认后恢复
L2 中度 RT > 1s 或 错误率 > 5% 核心链路静态化,关闭库存实时校验 人工确认 + 数据校验后恢复
L3 重度 服务不可用 全站降级页面,仅保留核心下单支付 故障恢复后逐步放开

6.3 预案系统

阿里巴巴的预案系统是一套自动化的故障应对框架。所有可能的故障场景都被预先编排成可执行的预案:

/**
 * 预案执行引擎
 * 预案是一组原子操作的有序编排
 */
public class ContingencyPlanEngine {

    private final Map<String, ContingencyPlan> planRegistry = new ConcurrentHashMap<>();

    /**
     * 注册预案
     */
    public void register(ContingencyPlan plan) {
        planRegistry.put(plan.getId(), plan);
    }

    /**
     * 执行预案
     */
    public PlanResult execute(String planId, Map<String, Object> context) {
        ContingencyPlan plan = planRegistry.get(planId);
        if (plan == null) {
            return PlanResult.notFound(planId);
        }

        List<ActionResult> actionResults = new ArrayList<>();
        for (PlanAction action : plan.getActions()) {
            try {
                ActionResult result = action.execute(context);
                actionResults.add(result);

                if (!result.isSuccess() && action.isRequired()) {
                    // 必要步骤失败,中止预案并回滚已执行步骤
                    rollback(actionResults);
                    return PlanResult.failed(planId, actionResults);
                }
            } catch (Exception e) {
                actionResults.add(ActionResult.error(action.getName(), e));
                if (action.isRequired()) {
                    rollback(actionResults);
                    return PlanResult.failed(planId, actionResults);
                }
            }
        }
        return PlanResult.success(planId, actionResults);
    }

    /**
     * 回滚已执行的操作
     */
    private void rollback(List<ActionResult> results) {
        // 逆序回滚
        for (int i = results.size() - 1; i >= 0; i--) {
            ActionResult result = results.get(i);
            if (result.isSuccess() && result.getRollbackAction() != null) {
                try {
                    result.getRollbackAction().execute(Collections.emptyMap());
                } catch (Exception e) {
                    // 回滚失败记录告警,人工介入
                    log.error("Rollback failed for action: {}", result.getActionName(), e);
                }
            }
        }
    }
}

6.4 秒杀系统设计

秒杀场景是双十一最极端的流量形态。阿里的秒杀系统采用分层过滤的思路:

用户请求(1000 万/秒)
  ↓ CDN 静态化(过滤 90%)
用户端请求(100 万/秒)
  ↓ 接入层限流(过滤 80%)
有效请求(20 万/秒)
  ↓ 本地库存预扣减(过滤 99%)
实际扣减(2000/秒)
  ↓ 数据库写入

每一层的过滤都基于一个原则:尽早拒绝无效请求,减少下游压力。


七、消息中间件 RocketMQ

7.1 RocketMQ 在交易链路中的角色

RocketMQ 是阿里巴巴自研的分布式消息中间件,在双十一交易链路中承担异步解耦和削峰填谷的核心职责。

主要使用场景:

  1. 订单创建后的异步通知:下单成功后,通过消息通知库存扣减、物流预分配、积分累加等下游服务
  2. 分布式事务的最终一致性:使用事务消息保证跨服务的数据一致性
  3. 削峰填谷:将瞬时的写入高峰平滑成下游可承受的速率

7.2 事务消息机制

/**
 * RocketMQ 事务消息使用示例
 * 场景:下单时需要同时扣减库存,保证订单和库存的一致性
 */
public class OrderTransactionProducer {

    private final TransactionMQProducer producer;

    public OrderTransactionProducer() {
        this.producer = new TransactionMQProducer("order_transaction_group");
        this.producer.setNamesrvAddr("namesrv1:9876;namesrv2:9876");
        this.producer.setTransactionListener(new OrderTransactionListener());
    }

    /**
     * 发送事务消息
     * 流程:
     * 1. 发送 Half 消息到 Broker
     * 2. Broker 返回成功后,执行本地事务(创建订单)
     * 3. 根据本地事务结果,提交或回滚消息
     * 4. 消费者收到消息后扣减库存
     */
    public SendResult createOrderWithTransaction(OrderDTO order) throws Exception {
        Message msg = new Message(
            "ORDER_TOPIC",
            "CREATE",
            order.getOrderId().getBytes(),
            JSON.toJSONBytes(order)
        );
        // 将订单信息作为参数传递给本地事务执行器
        return producer.sendMessageInTransaction(msg, order);
    }
}

/**
 * 事务消息监听器
 */
public class OrderTransactionListener implements TransactionListener {

    private final OrderService orderService;
    private final TransactionLogMapper txLogMapper;

    /**
     * 执行本地事务
     *  Half 消息发送成功后被回调
     */
    @Override
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        OrderDTO order = (OrderDTO) arg;
        try {
            // 执行本地事务:创建订单 + 记录事务日志
            orderService.createOrder(order);
            txLogMapper.insert(new TransactionLog(
                order.getOrderId(),
                "ORDER_CREATE",
                "SUCCESS"
            ));
            return LocalTransactionState.COMMIT_MESSAGE;
        } catch (Exception e) {
            return LocalTransactionState.ROLLBACK_MESSAGE;
        }
    }

    /**
     * 事务回查
     *  Broker 长时间未收到提交/回滚指令时回调
     * 通过查询事务日志判断本地事务是否执行成功
     */
    @Override
    public LocalTransactionState checkLocalTransaction(MessageExt msg) {
        String orderId = new String(msg.getKeys().getBytes());
        TransactionLog log = txLogMapper.findByOrderId(orderId);
        if (log != null && "SUCCESS".equals(log.getStatus())) {
            return LocalTransactionState.COMMIT_MESSAGE;
        }
        return LocalTransactionState.ROLLBACK_MESSAGE;
    }
}

7.3 消息堆积与背压

双十一峰值期间,下游消费速度跟不上生产速度是常态。RocketMQ 通过以下机制应对:

  1. 磁盘级堆积:消息持久化到磁盘,单个 Broker 可堆积数十亿条消息
  2. 消费者扩容:动态增加消费者实例数(不超过队列数)
  3. 消费降速:当消费者处理能力不足时,主动降低拉取频率
  4. 死信队列:消费失败超过阈值的消息进入死信队列(Dead Letter Queue),后续人工处理

八、分布式事务解决方案

8.1 阿里巴巴的事务模式选择

不同场景下的分布式事务方案对比:

事务模式 一致性级别 性能开销 适用场景 阿里实际应用
TCC(Try-Confirm-Cancel) 强一致 高(三次网络调用) 资金交易 支付宝核心支付链路
Saga 最终一致 长事务流程 物流履约链路
事务消息 最终一致 异步解耦场景 订单 -> 库存通知
AT(Auto Transaction) 最终一致 通用数据库操作 Seata 框架默认模式
XA 强一致 极高(两阶段锁) 对一致性要求极高的场景 极少使用

8.2 TCC 模式实现

/**
 * TCC 分布式事务示例:转账场景
 * Try:冻结金额
 * Confirm:扣减冻结金额,增加对方余额
 * Cancel:解冻金额
 */
public interface AccountTccService {

    /**
     * Try 阶段:冻结转出金额
     *  account 表中将 amount  balance 转移到 frozen
     */
    @TwoPhaseBusinessAction(
        name = "accountTransfer",
        commitMethod = "confirm",
        rollbackMethod = "cancel"
    )
    boolean tryFreezeAmount(
        BusinessActionContext context,
        @BusinessActionContextParameter(paramName = "accountId") String accountId,
        @BusinessActionContextParameter(paramName = "amount") BigDecimal amount
    );

    /**
     * Confirm 阶段:确认扣减
     *  frozen 中的金额正式扣除
     */
    boolean confirm(BusinessActionContext context);

    /**
     * Cancel 阶段:取消冻结
     *  frozen 中的金额还原到 balance
     */
    boolean cancel(BusinessActionContext context);
}

/**
 * TCC 服务实现
 */
public class AccountTccServiceImpl implements AccountTccService {

    private final AccountMapper accountMapper;
    private final TccTransactionLogMapper logMapper;

    @Override
    public boolean tryFreezeAmount(
        BusinessActionContext context, String accountId, BigDecimal amount
    ) {
        // 幂等检查:防止重复冻结
        String xid = context.getXid();
        if (logMapper.existsByXid(xid)) {
            return true;
        }

        // 检查余额是否充足
        Account account = accountMapper.selectForUpdate(accountId);
        if (account.getBalance().compareTo(amount) < 0) {
            throw new InsufficientBalanceException(accountId, amount);
        }

        // 冻结金额:balance 减少,frozen 增加
        accountMapper.freezeAmount(accountId, amount);

        // 记录事务日志
        logMapper.insert(new TccTransactionLog(xid, accountId, amount, "TRY"));
        return true;
    }

    @Override
    public boolean confirm(BusinessActionContext context) {
        String xid = context.getXid();
        String accountId = (String) context.getActionContext("accountId");
        BigDecimal amount = new BigDecimal(
            context.getActionContext("amount").toString()
        );

        // 幂等检查
        TccTransactionLog log = logMapper.findByXid(xid);
        if (log != null && "CONFIRMED".equals(log.getStatus())) {
            return true;
        }

        // 确认扣减:减少 frozen
        accountMapper.confirmDeduction(accountId, amount);

        // 更新事务日志
        logMapper.updateStatus(xid, "CONFIRMED");
        return true;
    }

    @Override
    public boolean cancel(BusinessActionContext context) {
        String xid = context.getXid();
        String accountId = (String) context.getActionContext("accountId");
        BigDecimal amount = new BigDecimal(
            context.getActionContext("amount").toString()
        );

        // 幂等检查
        TccTransactionLog log = logMapper.findByXid(xid);
        if (log == null) {
            // 空回滚:Try 未执行,直接记录日志防悬挂
            logMapper.insert(new TccTransactionLog(xid, accountId, amount, "CANCELLED"));
            return true;
        }
        if ("CANCELLED".equals(log.getStatus())) {
            return true;
        }

        // 取消冻结:frozen 减少,balance 增加
        accountMapper.unfreezeAmount(accountId, amount);

        // 更新事务日志
        logMapper.updateStatus(xid, "CANCELLED");
        return true;
    }
}

8.3 Seata AT 模式

Seata(原 Fescar)是阿里巴巴开源的分布式事务框架。AT 模式通过自动生成反向 SQL 实现低侵入的分布式事务:

/**
 * Seata AT 模式使用示例
 * 只需在业务方法上添加 @GlobalTransactional 注解
 * Seata 自动完成分支事务注册、提交和回滚
 */
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private InventoryFeignClient inventoryClient;
    @Autowired
    private AccountFeignClient accountClient;

    /**
     * 创建订单的全局事务
     * Seata  SQL 执行前后自动记录 undo_log
     * 如果任何分支失败,自动回滚所有分支
     */
    @GlobalTransactional(name = "create-order", timeoutMills = 30000)
    @Override
    public OrderResult createOrder(OrderRequest request) {
        // 分支一:创建订单记录
        Order order = new Order();
        order.setOrderId(IdGenerator.nextId());
        order.setBuyerId(request.getBuyerId());
        order.setItemId(request.getItemId());
        order.setQuantity(request.getQuantity());
        order.setTotalAmount(request.getTotalAmount());
        order.setStatus("CREATED");
        orderMapper.insert(order);

        // 分支二:扣减库存(远程调用,Seata 自动传播事务上下文)
        inventoryClient.deduct(request.getItemId(), request.getQuantity());

        // 分支三:扣减账户余额
        accountClient.debit(request.getBuyerId(), request.getTotalAmount());

        return new OrderResult(order.getOrderId(), "SUCCESS");
    }
}

九、工程案例:双十一零点峰值应对

9.1 2024 年双十一时间线

以下是 2024 年双十一零点前后的关键操作时间线(基于公开技术分享还原):

T-72h(10月29日)
├── 完成最后一轮全链路压测,峰值达到 60 万笔/秒
├── 压测报告确认各服务水位均在安全线以下
└── 锁定所有代码变更,进入封网期

T-24h(10月30日)
├── 弹性扩容完成:在线服务器从日常 5 万台扩至 12 万台
├── CDN 预热完成:热点商品详情页命中率达 99.8%
├── 全部降级预案就绪,完成一轮空跑验证
└── 数据库只读副本扩至日常的 3 倍

T-4h(10月31日 20:00)
├── 运维作战室集合,各业务线负责人就位
├── 启动实时监控大盘,各指标基线校准
└── 关闭非核心定时任务和离线计算作业

T-1h(10月31日 23:00)
├── 启动 L0 预降级:关闭个性化推荐、评价展示
├── 开启秒杀商品本地库存预加载
└── 消息队列预创建额外分区

T-10min(10月31日 23:50)
├── 切换至大促限流配置
├── 启动流量预热:逐步放入预约用户流量
└── 确认所有 RZone 状态正常

T-0(11月1日 00:00:00)
├── 峰值瞬间到达:58.3 万笔/秒
├── 各服务 CPU 水位 65%-72%
├── 数据库主库 IOPS 达到 95% 预分配容量
├── 零错误率,RT P99 = 380ms
└── 消息堆积在 10 秒内消化完毕

T+5min
├── 流量回落至峰值的 60%
├── 关闭 L0 预降级
└── 恢复个性化推荐

T+30min
├── 流量稳定在日常的 8 倍
├── 开始弹性缩容第一批机器
└── 离线计算作业逐步恢复

T+24h
├── 弹性缩容完成,恢复日常规模
├── 全量数据校验:订单和库存一致性 100%
└── 压测影子数据清理完成

9.2 零点峰值的技术挑战

双十一零点的流量特征与日常完全不同:

  1. 瞬时脉冲:从零到峰值的爬升时间不到 1 秒,远超常规限流算法的滑动窗口粒度
  2. 热点集中:前 100 个爆款商品承担了 30% 以上的访问量
  3. 强关联性:下单、扣库存、支付三个操作紧密耦合,任何一个环节出问题都会导致用户体验崩塌
  4. 不可重来:双十一只有一次,没有”明天再试”的机会

9.3 核心应对策略详解

策略一:库存预分桶

将热点商品的库存提前按 RZone 分桶,避免所有扣减请求打到同一行数据:

/**
 * 库存预分桶方案
 * 将一件商品的 10000 件库存分散到 8  RZone
 */
public class InventoryBucketService {

    private static final int BUCKET_COUNT = 8;

    /**
     * 初始化库存分桶
     * 将总库存均匀分配到各个桶
     */
    public void initBuckets(String itemId, int totalStock) {
        int perBucket = totalStock / BUCKET_COUNT;
        int remainder = totalStock % BUCKET_COUNT;

        for (int i = 0; i < BUCKET_COUNT; i++) {
            int bucketStock = perBucket + (i < remainder ? 1 : 0);
            inventoryMapper.insertBucket(new InventoryBucket(
                itemId,
                i,
                bucketStock,
                bucketStock
            ));
        }
    }

    /**
     * 扣减库存
     * 先尝试本地桶,不足时向其他桶借调
     */
    public boolean deductStock(String itemId, int quantity, int localBucketId) {
        // 尝试本地桶扣减
        int affected = inventoryMapper.deductBucket(itemId, localBucketId, quantity);
        if (affected > 0) {
            return true;
        }

        // 本地桶不足,尝试从其他桶借调
        for (int i = 0; i < BUCKET_COUNT; i++) {
            if (i == localBucketId) {
                continue;
            }
            affected = inventoryMapper.deductBucket(itemId, i, quantity);
            if (affected > 0) {
                return true;
            }
        }

        return false;
    }
}

策略二:异步化下单链路

将下单流程从同步改为异步排队,削平峰值写入压力:

/**
 * 异步下单队列
 * 用户点击"立即购买"后进入排队
 * 后端按照数据库写入能力匀速处理
 */
public class AsyncOrderProcessor {

    // 内存队列容量
    private static final int QUEUE_CAPACITY = 100000;
    private final BlockingQueue<OrderRequest> orderQueue =
        new LinkedBlockingQueue<>(QUEUE_CAPACITY);
    // 消费线程池
    private final ExecutorService executor =
        Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);

    /**
     * 接收下单请求,放入队列
     * 如果队列已满,直接返回"系统繁忙"
     */
    public OrderResponse submitOrder(OrderRequest request) {
        boolean offered = orderQueue.offer(request);
        if (!offered) {
            return OrderResponse.busy("系统繁忙,请稍后重试");
        }
        return OrderResponse.queued(request.getRequestId());
    }

    /**
     * 消费线程:持续从队列取出请求并处理
     */
    public void startConsumers() {
        for (int i = 0; i < executor.toString().length(); i++) {
            executor.submit(() -> {
                while (!Thread.currentThread().isInterrupted()) {
                    try {
                        OrderRequest request = orderQueue.poll(100, TimeUnit.MILLISECONDS);
                        if (request != null) {
                            processOrder(request);
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            });
        }
    }

    private void processOrder(OrderRequest request) {
        try {
            OrderResult result = orderService.createOrder(request);
            // 通过长连接推送结果给前端
            pushService.pushOrderResult(request.getUserId(), result);
        } catch (Exception e) {
            pushService.pushOrderResult(
                request.getUserId(),
                OrderResult.failed(request.getRequestId(), e.getMessage())
            );
        }
    }
}

策略三:多级缓存体系

请求进入
  ↓
浏览器本地缓存(LocalStorage,5 分钟有效)
  ↓ 未命中
CDN 边缘缓存(全球 2800+ 节点,缓存商品详情页)
  ↓ 未命中
Nginx 本地缓存(Lua 脚本 + Shared Dict,热点商品 10s 缓存)
  ↓ 未命中
Tair 分布式缓存(集群模式,毫秒级响应)
  ↓ 未命中
数据库(MySQL / OceanBase)

9.4 故障复盘:2015 年双十一的一次惊险事故

2015 年双十一,阿里巴巴经历了一次未公开的惊险事故。根据事后技术分享中的线索还原:

事故现象:零点后约 3 分钟,某核心服务的 GC(Garbage Collection)时间突然从 50ms 飙升至 2s,导致该服务的所有下游调用超时。

根因分析:该服务使用了一个本地缓存组件,缓存淘汰策略是 LRU(Least Recently Used)。零点流量涌入后,缓存淘汰速度远超预期,大量短生命周期的对象被创建和销毁,触发了 JVM 的 Full GC。

应急处理

  1. T+3min:监控告警,值班 SRE 收到告警
  2. T+5min:确认故障范围,该服务的 RT 从 20ms 涨到 3s
  3. T+7min:执行预案,将受影响 RZone 的流量切换到备用 RZone
  4. T+10min:流量切换完成,用户无感
  5. T+15min:对故障实例做 JVM 参数调整,重启服务
  6. T+30min:流量切回,系统恢复正常

事后改进

  1. 将本地缓存的淘汰策略从 LRU 改为 W-TinyLFU,降低缓存抖动
  2. JVM 参数中增加 -XX:+UseG1GC-XX:MaxGCPauseMillis=100,限制 GC 停顿时间
  3. 在全链路压测中增加”缓存冷启动”场景,模拟缓存空载时的流量冲击
  4. 每个服务增加 GC 时间的 P99 监控项,超过 500ms 自动触发告警

十、流量工程的核心经验

10.1 十条原则

经过十余年双十一的锤炼,阿里巴巴总结出流量工程的十条核心原则:

第一,可预见的峰值用容量解决,不可预见的峰值用弹性解决。 双十一的峰值是已知的,提前扩容是最直接的手段。但日常运营中的突发流量(如直播带货的瞬时爆发)需要弹性伸缩机制来应对。

第二,没有经过验证的容量等于没有容量。 扩容后必须通过全链路压测验证实际承载能力。曾经出现过扩容后因网络带宽不足导致实际容量未达预期的案例。

第三,故障是常态,容错是底线。 在数万台服务器的规模下,每天都有机器故障。架构设计必须假定任何组件随时可能失败。

第四,限流是保护系统的最后一道防线。 当所有优化手段都无法应对流量时,限流确保系统不会被压垮。宁可拒绝部分用户,也不能让所有用户都无法使用。

第五,异步化是提升吞吐量的核心手段。 能异步处理的业务尽量异步化,减少同步调用链的长度和耦合度。

第六,缓存是性能优化的第一选择。 多级缓存体系将大部分读请求拦截在离用户最近的地方,大幅降低后端压力。

第七,数据分片是水平扩展的基础。 无论是数据库分库分表还是服务单元化,本质都是通过数据分片将负载分散到多个节点。

第八,可观测性是问题定位的前提。 完善的监控、日志和链路追踪系统,是快速发现和定位问题的基础。阿里的全链路追踪系统 EagleEye(鹰眼)可以在秒级定位到问题节点。

第九,预案演练比预案本身更重要。 写在文档里的预案如果没有演练过,在真正故障时大概率无法顺利执行。阿里每年会进行数十次故障演练。

第十,技术架构服务于业务目标。 所有架构决策最终都要回归到业务价值。过度设计和设计不足一样有害。

10.2 不同规模的流量工程方案选择

日均 QPS 建议架构 数据库方案 缓存方案 消息队列
< 1 万 单体应用 单实例 MySQL 主从 本地缓存(Caffeine) 可选
1-10 万 微服务 读写分离 + 分表 Redis Cluster Kafka / RocketMQ
10-100 万 微服务 + 服务网格 分库分表 + 读写分离 多级缓存 RocketMQ 集群
> 100 万 单元化架构 异地多活分库分表 CDN + 本地 + 分布式缓存 多集群消息

10.3 从阿里实践中可以复用的设计模式

即使不是阿里的体量,以下设计模式在中小规模系统中也有实用价值:

  1. 影子流量测试:在生产环境中复制一份流量到测试环境,验证新版本的正确性
  2. 分桶计数器:将热点计数器分散到多个桶,降低锁竞争
  3. 预案自动化:将常见故障场景的处理流程编排成自动化脚本
  4. 渐进式发布:灰度发布从 1% -> 5% -> 20% -> 50% -> 100% 逐步放量
  5. 熔断器模式:当下游服务故障率超过阈值时自动断开调用,快速失败
/**
 * 分桶计数器实现
 * 适用于高并发场景下的计数需求(如库存、秒杀)
 * 将单一计数器拆分为多个桶,降低 CAS 竞争
 */
public class BucketCounter {

    private final AtomicLong[] buckets;
    private final int bucketCount;

    public BucketCounter(int bucketCount) {
        this.bucketCount = bucketCount;
        this.buckets = new AtomicLong[bucketCount];
        for (int i = 0; i < bucketCount; i++) {
            buckets[i] = new AtomicLong(0);
        }
    }

    /**
     * 递增计数
     * 使用线程 ID 哈希选择桶,分散竞争
     */
    public void increment() {
        int index = (int) (Thread.currentThread().getId() % bucketCount);
        buckets[index].incrementAndGet();
    }

    /**
     * 递减计数
     */
    public boolean decrement() {
        int index = (int) (Thread.currentThread().getId() % bucketCount);
        long current = buckets[index].get();
        if (current > 0) {
            return buckets[index].compareAndSet(current, current - 1);
        }
        // 本桶不足,尝试其他桶
        for (int i = 0; i < bucketCount; i++) {
            if (i == index) continue;
            current = buckets[i].get();
            if (current > 0 && buckets[i].compareAndSet(current, current - 1)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取总计数
     */
    public long getTotal() {
        long total = 0;
        for (AtomicLong bucket : buckets) {
            total += bucket.get();
        }
        return total;
    }

    /**
     * 设置总计数(均匀分配到各桶)
     */
    public void setTotal(long total) {
        long perBucket = total / bucketCount;
        long remainder = total % bucketCount;
        for (int i = 0; i < bucketCount; i++) {
            buckets[i].set(perBucket + (i < remainder ? 1 : 0));
        }
    }
}

10.4 流量工程的技术演进方向

展望未来,阿里巴巴的流量工程正在向以下方向演进:

  1. Serverless 化:将更多业务逻辑迁移到函数计算(FC)平台,实现真正的按需弹性
  2. AI 驱动的容量规划:使用机器学习模型预测流量,自动生成扩缩容计划
  3. Service Mesh 统一流量治理:通过 Sidecar 代理统一实现限流、熔断、路由,与业务代码彻底解耦
  4. 混合云弹性:利用公有云的弹性资源应对峰值,日常使用自有数据中心

参考资料

  1. 林昊.《大规模分布式存储系统:原理解析与架构实践》. 机械工业出版社,2013.
  2. 阿里巴巴中间件团队.《阿里巴巴中间件技术发展史》. InfoQ 中国,2019.
  3. 阿里巴巴技术团队.《双 11 技术全景:全链路压测实践》. 阿里技术公众号,2020.
  4. 李运华.《从零开始学架构》. 电子工业出版社,2018.
  5. Apache RocketMQ 官方文档. https://rocketmq.apache.org/docs/
  6. Seata 官方文档. https://seata.io/zh-cn/docs/overview/what-is-seata.html
  7. 阿里巴巴技术团队.《单元化架构实践》. ArchSummit 全球架构师峰会演讲,2017.
  8. 毕玄.《阿里技术架构演进》. QCon 全球软件开发大会,2016.
  9. 阿里云开发者社区.《TDDL 分库分表原理与实践》. 2019.
  10. 阿里巴巴技术团队.《双十一背后的弹性伸缩实践》. 阿里技术公众号,2023.

上一篇: 微信架构 下一篇: Google 基础设施

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-13 · architecture

【系统架构设计百科】架构质量属性:不只是"高可用高性能"

需求评审时写下的'高可用、高性能、高并发',到了架构设计阶段几乎无法落地——因为它们不是可执行的需求。本文从 SEI/CMU 的质量属性理论出发,用 stimulus-response 场景模型把模糊需求变成可量化、可验证的架构约束,并拆解属性之间的冲突与联动关系。

2026-04-13 · architecture

【系统架构设计百科】告警策略:如何避免"狼来了"

大多数团队的告警系统都在制造噪声而不是传递信号。阈值告警看似直观,实则产生大量误报和漏报,值班工程师在凌晨三点被叫醒,却发现只是一次无害的毛刺。本文从告警疲劳的工业数据出发,拆解基于 SLO 的多窗口燃烧率告警算法,深入 Alertmanager 的路由、抑制与分组机制,结合 PagerDuty 的告警疲劳研究和真实工程案例,给出一套可落地的告警策略设计方法。

2026-04-13 · architecture

【系统架构设计百科】复杂性管理:架构的核心战场

系统复杂性是架构腐化的根源——本文从 Brooks 的本质复杂性与偶然复杂性划分出发,结合认知负荷理论与 Parnas 的信息隐藏原则,系统阐述复杂性的来源、度量与控制手段,并给出可操作的架构策略


By .