一、百万TPS的技术挑战

百万TPS(Transaction Per Second)不是简单的"堆机器",而是从网络层、计算层、存储层、架构设计每个环节的精细化工程。以下是主要挑战:

  • 网络IO:100万并发长连接,单机网卡带宽成为瓶颈(10Gbps网卡约1250MB/s)
  • 内存带宽:每请求KB级数据,100万TPS需要内存带宽达数GB/s
  • 锁竞争:共享资源(数据库、缓存)成为全局瓶颈
  • 长尾延迟:P99/P999延迟决定用户体验,GC停顿是最大敌人
  • 热点数据:1%的Key承担99%的流量

1.1 延迟分解:100ms响应的16ms法则

// 100ms响应时间的组成(典型)
// 网络延迟:30ms(跨地域/公网)
// DNS解析:5ms
// TCP连接(3次握手):10ms
// SSL握手:15ms
// 应用处理:30ms(业务逻辑 + DB/缓存)
// 合计:约90ms(不含排队延迟)

// 16ms帧法则(端到端P99 < 50ms):
// 如果目标P99=50ms,允许的最大等待队列长度为:
// 队列长度 = 目标QPS × P99延迟
//          = 1,000,000 × 0.05s = 50,000个请求
// 即:系统需要能在50ms内处理完50,000个请求的吞吐量

// TPS与QPS的关系:
// TPS(Transaction Per Second):含完整业务逻辑的事务
// QPS(Query Per Second):单个查询
// 1个TPS可能包含多个QPS(如下单TPS = 商品查询QPS + 库存查询QPS + 库存扣减QPS + 订单创建QPS)
// 百万TPS系统通常指QPS,1个TPS ≈ 3-5个QPS

二、全链路压测与容量规划

2.1 全链路压测方案

// 全链路压测三要素:压测数据隔离 + 流量染色 + 监控告警

// 1. 流量染色(TraceId透传)
// 在HTTP Header中注入压测标记
public class TraceFilter {
    public static final String TRACE_HEADER = "X-TRACE-ID";
    public static final String TRACE_TYPE = "X-TRACE-TYPE"; // "normal" | "stress"

    @Override
    public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
        String traceId = Optional.ofNullable(
            exchange.getRequest().getHeaders().getFirst(TRACE_HEADER)
        ).orElse(UUID.randomUUID().toString());

        String traceType = Optional.ofNullable(
            exchange.getRequest().getHeaders().getFirst(TRACE_TYPE)
        ).orElse("normal");

        exchange.getAttributes().put("traceId", traceId);
        exchange.getAttributes().put("traceType", traceType); // 压测标记

        // 透传给下游
        exchange.getResponse().getHeaders().add(TRACE_HEADER, traceId);
        exchange.getResponse().getHeaders().add(TRACE_TYPE, traceType);
        return chain.filter(exchange);
    }
}

// 2. 影子库/影子缓存:压测数据与生产数据隔离
// Redis压测隔离:压测流量使用不同的Redis前缀
if ("stress".equals(traceType)) {
    redisKey = "shadow:" + realKey;
} else {
    redisKey = realKey;
}

// MySQL影子库:压测库前缀 shadow_
String tableName = "stress".equals(traceType)
    ? "shadow_orders"
    : "orders";

// 3. JMeter + 分布式压测架构
// JMeter Server Cluster: 50台压测机 × 500并发 = 25000并发
// 目标:25000并发 × 40 QPS/并发 = 100万TPS

// JMeter分布式压测配置
# jmeter.properties (压测机)
remote_hosts=10.0.0.1,10.0.0.2,...,10.0.0.50
client.rmi.localport=1099
server.rmi.create_ca_cert=true

// 4. 压测指标监控
// Prometheus + Grafana
// - 请求QPS: rate(http_requests_total[1m])
// - P99延迟: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[1m]))
// - 错误率: rate(http_requests_total{status=~"5.."}[1m])
// - JVM GC: rate(jvm_gc_pause_seconds_sum[1m])

2.2 容量规划模型

// 容量规划公式
// 目标QPS = 预估TPS × 倍数(峰值通常是日常的3-5倍)
// 机器数 = ceil(目标QPS / 单机QPS × 安全系数)

// 估算单机QPS(实测数据)
// Nginx反向代理:50,000 QPS(纯代理,无业务逻辑)
// Java API服务(无IO):80,000-100,000 QPS
// Java API服务(Redis缓存命中):20,000-30,000 QPS
// Java API服务(MySQL查询):5,000-10,000 QPS
// Go API服务(同上):2-3倍Java

// 示例:100万TPS的订单系统容量规划
// 目标:100万 QPS (含缓存,缓存命中率90%)
// 单机(8核16G):15,000 QPS(含Redis缓存命中 + MySQL写)
// 安全系数:1.5(应对突发流量)
// 机器数 = 1,000,000 / 15,000 × 1.5 ≈ 100台API服务

// Nginx层:
// 每台Nginx:80,000 QPS(CPU绑定)
// 100万 / 8万 = 13台Nginx(+2台冗余)= 15台

// Redis集群:
// 单机QPS:50,000(内存操作)
// 100万 / 5万 = 20台(+主从=40台)
// 推荐:3主3从,每个主节点16GB内存,每台可承载60万QPS

// MySQL:
// 每主库写QPS:10,000(4核16G,NVMe SSD)
// 读写分离后写占比20%,则100万×20%=20万写QPS
// 20万 / 1万 = 20台...不可行,需要分库分表
// 分库后:每库5万写QPS,需要4个分库,每个分库1主2从

三、流量治理:限流 → 熔断 → 降级

3.1 多级限流策略

// 限流维度:单机限流 → 网关限流 → 服务限流
// 算法:滑动窗口 → 令牌桶 → 漏桶

// 1. 滑动窗口算法(Redis + Lua)
-- 原理:将时间窗口划分为N个桶,每个桶记录请求数
local key = KEYS[1]
local now = tonumber(ARGV[1])
local window = tonumber(ARGV[2])     -- 窗口大小(秒)
local limit = tonumber(ARGV[3])     -- 限流阈值

local bucket_size = 10               -- 10个桶,滑动精度
local bucket_time = window / bucket_size

-- 删除过期桶
redis.call('ZREMRANGEBYSCORE', key, 0, now - window * 1000)

-- 统计当前窗口内的请求数
local current = redis.call('ZCARD', key)

if current >= limit then
    return 0                        -- 0=拒绝
end

-- 记录本次请求
redis.call('ZADD', key, now * 1000, now * 1000 + math.random())
redis.call('PEXPIRE', key, window * 1000)
return 1                            -- 1=通过

// 2. Sentinel限流(阿里的流量治理库)
@Configuration
public class SentinelConfig {
    @PostConstruct
    public void initRules() {
        // QPS限流规则
        FlowRuleManager.loadRules(List.of(
            new FlowRule("orderService")
                .setGrade(RuleConstant.FLOW_GRADE_QPS)
                .setCount(100000)             // 10万QPS
                .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)
                .setStrategy(RuleConstant.STRATEGY_DIRECT)
                .setLimitApp("default")
        ));
    }
}

// 3. 热点参数限流(Sentinel内置)
// 针对itemId=1001的热点商品进行单独限流
ParamFlowRuleManager.loadRules(List.of(
    new ParamFlowRule("orderService")
        .setGrade(RuleConstant.FLOW_GRADE_QPS)
        .setCount(50000)
        .setParamIdx(1) // 第二个参数(itemId)的位置
));

3.2 熔断与降级

// Resilience4j 熔断器配置
@Configuration
public class Resilience4jConfig {
    public CircuitBreakerConfig circuitBreakerConfig() {
        return CircuitBreakerConfig.custom()
            // 熔断器统计窗口(10秒内)
            .slidingWindowType(SlidingWindowType.COUNT_BASED)
            .slidingWindowSize(100)
            // 失败率 > 50% 则熔断
            .failureRateThreshold(50)
            // 熔断持续时间30秒
            .waitDurationInOpenState(Duration.ofSeconds(30))
            // 熔断半开时允许10个请求
            .permittedNumberOfCallsInHalfOpenState(10)
            // 慢调用阈值:调用时长 > 2秒 且 > 50% 则熔断
            .slowCallRateThreshold(50)
            .slowCallDurationThreshold(Duration.ofSeconds(2))
            .build();
    }
}

// 服务降级:核心链路保护
@Service
public class OrderService {
    @HystrixCommand(
        fallbackMethod = "placeOrderFallback",
        commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500"),
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000")
        }
    )
    public Order placeOrder(OrderRequest req) {
        return orderRepository.save(req);
    }

    // 降级方法:库存不检查直接下单(允许超卖,降级保护)
    public Order placeOrderFallback(OrderRequest req, Throwable t) {
        log.warn("订单服务降级,错误: {}", t.getMessage());
        Order order = new Order();
        order.setId(generateId());
        order.setStatus("PENDING_CHECK"); // 待核实状态
        order.setUserId(req.getUserId());
        return orderRepository.save(order);
    }
}

// Sentinel + Spring Cloud 热点降级
@SentinelResource(value = "getProduct",
    blockHandler = "getProductBlock",
    fallback = "getProductFallback")
public Product getProduct(Long itemId) {
    return productService.findById(itemId);
}

// Fallback:热点商品降级返回
public Product getProductFallback(Long itemId, Throwable t) {
    // 返回缓存的静态数据或热门商品数据
    return hotProductCache.get(itemId).orElse(Product.DEFAULT);
}

四、高性能缓存架构

4.1 多级缓存架构

// 典型多级缓存:本地缓存 → 分布式缓存 → DB
// L1: Caffeine本地缓存(热点数据,TTL短)
// L2: Redis集群(分布式,数据一致性强)
// L3: MySQL(最终一致性,数据源)

// Spring Cache + Caffeine + Redis 多级缓存
@Configuration
public class CacheConfig {
    @Bean
    public CacheManager cacheManager(
            @Qualifier("caffeineCacheManager") CacheManager cm1,
            @Qualifier("redisCacheManager") CacheManager cm2) {
        // CompositeCacheManager 按顺序查找
        CompositeCacheManager manager = new CompositeCacheManager(cm1, cm2);
        return manager;
    }

    @Bean
    public Cache caffeineCache() {
        return Caffeine.newBuilder()
            .maximumSize(10000)        // L1最多1万个商品
            .expireAfterWrite(30, TimeUnit.SECONDS)  // 30秒后过期
            .recordStats()             // 开启统计
            .build();
    }
}

// Cache Aside + Write-Through 双写一致性
@Service
public class ProductCacheService {
    @Autowired Cache l1Cache;
    @Autowired StringRedisTemplate redisTemplate;
    @Autowired ProductRepository productRepo;

    public Product getProduct(Long id) {
        // L1: 本地缓存
        Product p = l1Cache.getIfPresent(id);
        if (p != null) return p;

        // L2: Redis
        String redisKey = "product:" + id;
        p = redisTemplate.opsForValue().get(redisKey);
        if (p != null) {
            l1Cache.put(id, p); // 回填L1
            return p;
        }

        // L3: DB
        p = productRepo.findById(id).orElse(null);
        if (p != null) {
            redisTemplate.opsForValue().set(redisKey, p, 10, TimeUnit.MINUTES);
            l1Cache.put(id, p);
        }
        return p;
    }

    @Transactional
    public void updateProduct(Long id, Product p) {
        // Write-Through: 同时写L2和DB
        productRepo.save(p);
        redisTemplate.opsForValue().set("product:" + id, p, 10, TimeUnit.MINUTES);
        l1Cache.invalidate(id); // 删除L1
    }
}

4.2 Redis Cluster分片与热点数据

// 热点数据:本地缓存 + 主动推送
// 推送方案:Canal监听MySQL binlog,变更时主动推送到所有应用实例

// Canal + Redis: MySQL变更 → Canal → 消息队列 → 应用 → 本地缓存
// Canal配置:
 canal.instance.gtin.ignoreTransaction = false
 canal.instance.default.connection.url = jdbc:mysql://mysql-master:3306
 canal.destinations = products_db

// 热点Key打散:HashTag + 一致性Hash
// 大促期间,将热门商品的库存拆分到多个Key
SET stock:hot:{itemId}:pool1 5000
SET stock:hot:{itemId}:pool2 5000
SET stock:hot:{itemId}:pool3 5000
SET stock:hot:{itemId}:pool4 5000

// Lua脚本:随机选取一个库存池扣减
local pools = {'pool1','pool2','pool3','pool4'}
for i, pool in ipairs(pools) do
    local key = 'stock:hot:'..ARGV[1]..':'..pool
    local stock = tonumber(redis.call('GET', key) or 0)
    if stock >= tonumber(ARGV[2]) then
        redis.call('DECRBY', key, ARGV[2])
        return pool
    end
end
return nil -- 所有库存池不足

五、GC调优:无停顿架构

// Java百万TPS系统的GC策略
// ZGC(Java 11+,推荐):P99停顿 < 1ms,支持TB级堆
# JVM参数
-XX:MaxGCPauseMillis=1
-XX:+UseZGC
-XX:+ZGenerational  # Java 21+ 开启ZGC分代
-Xms32G -Xmx32G
-XX:ParallelGCThreads=16
-XX:ConcGCThreads=4

// 或 G1 + 精细调参(Java 8-17)
-XX:MaxGCPauseMillis=10
-XX:+UseG1GC
-XX:G1NewSizePercent=30         # 新生代30%
-XX:G1MaxNewSizePercent=40
-XX:G1HeapRegionSize=8M         # 8MB region
-XX:InitiatingHeapOccupancyPercent=45  # 45%触发并发标记
-XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=512M  # 固定元空间

// GC监控
# JFR(Java Flight Recorder)生产环境采样
-XX:+UnlockCommercialFeatures
-XX:+FlightRecorder
-XX:StartFlightRecording=dumponexit=true,filename=gc.jfr

// OTel(OpenTelemetry)埋点
@Bean
public MeterRegistry meterRegistry() {
    return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
}

@Bean
public GcMetrics gcMetrics(MeterRegistry registry) {
    return new GcMetrics(registry);
}

// 避免Full GC的根本策略:
// 1. 对象不可变(String, Long, LocalDateTime)
// 2. 禁止new Object()在热路径上
// 3. 使用对象池(Recycler, DirectByteBufferPool)
// 4. 直播/大促:提前预热JVM(-XX:PreTouch)

5.1 百万TPS系统架构全景

// 百万TPS系统推荐架构
// -----------------------------------------------
// 接入层:15台 Nginx (8万QPS/台)
//    └─→ IP黑名单 / WAF / SSL卸载
//    └─→ 限流(Nginx-Lua令牌桶)
//
// 网关层:50台 Kong/APISIX (2万QPS/台)
//    └─→ 路由 / 鉴权 / 限流(Sentinel)
//    └─→ 参数校验 / 降级
//
// 业务层:100台 Java微服务 (1.5万QPS/台)
//    └─→ 多级缓存(Caffeine L1 + Redis L2)
//    └─→ 熔断(Resilience4j/Sentinel)
//    └─→ 异步化(RabbitMQ/RocketMQ)
//
// 数据层:
//    └─→ Redis Cluster: 6主6从 (每主60万QPS)
//    └─→ MySQL分库分表: 4分库 (每库5万写QPS)
//    └─→ Elasticsearch: 3主3从 (搜索集群)
//    └─→ Kafka: 24Broker (消息队列)
//
// 治理层:
//    └─→ Sentinel: 限流/熔断/热点探测
//    └─→ SkyWalking: 全链路追踪
//    └─→ Prometheus+Grafana: 监控告警
//    └─→ Docker+K8S: 弹性伸缩