CC 咖啡猫的工作空间 Coding Space

系统设计

1. 高并发系统设计

1.1 垂直扩展 (Scale Up)

方式 说明
增加硬件资源 CPU、内存、磁盘
数据库优化 索引、查询优化
应用优化 代码调优、算法优化

1.2 水平扩展 (Scale Out)

                    ┌─────────────┐
                    │   Nginx     │
                    │  负载均衡   │
                    └──────┬──────┘
                           │
            ┌──────────────┼──────────────┐
            │              │              │
       ┌────▼────┐    ┌────▼────┐    ┌────▼────┐
       │ Server1 │    │ Server2 │    │ Server3 │
       └─────────┘    └─────────┘    └─────────┘

1.3 核心策略

策略 手段
缓存 Redis、Memcached、CDN
异步 消息队列、线程池
拆分 分库分表、微服务
负载均衡 Nginx、LVS、F5

2. 高可用设计

2.1 限流 (Rate Limiting)

// 令牌桶算法
public class TokenBucket {
    private int capacity;        // 桶容量
    private int tokens;          // 当前令牌数
    private double refillRate;   //  refill rate

    public boolean tryAcquire(int permits) {
        if (tokens >= permits) {
            tokens -= permits;
            return true;
        }
        return false;
    }
}
算法 特点
令牌桶 允许突发流量
漏桶 平滑输出
滑动窗口 精度高

2.2 熔断器 (Circuit Breaker)

CLOSED (正常) → 失败率超阈值 → OPEN (熔断) →  半开 → OPEN/CLOSED
// Resilience4j 熔断示例
@CircuitBreaker(name = "backend", fallbackMethod = "fallback")
public String callService() {
    return restTemplate.getForObject("http://service/api", String.class);
}

public String fallback(Exception e) {
    return "服务暂时不可用,请稍后重试";
}

2.3 降级 (Degradation)

  • 读服务降级:返回缓存数据/默认值
  • 写服务降级:延迟写入/本地队列
  • 非核心功能关闭:推荐、统计等

2.4 多活架构

┌─────────────┐    ┌─────────────┐
│   机房 A     │    │   机房 B     │
│  (主站)     │    │  (备站)     │
│  写+读      │    │  读         │
└─────────────┘    └─────────────┘

3. 缓存设计

3.1 缓存策略

策略 说明 场景
Cache-Aside 先读缓存,miss后读DB并写缓存 读多写少
Write-Through 同步写缓存和DB 数据一致性要求高
Write-Behind 异步写DB 写并发高
Read-From-Replica 读从库 主库压力大的分库分表场景

3.2 缓存问题

缓存穿透

// 布隆过滤器或空值缓存
public String get(String key) {
    String value = redis.get(key);
    if (value == null) {
        value = db.query(key);
        if (value == null) {
            redis.setex(key, 60, "");  // 空值缓存,避免穿透
        } else {
            redis.setex(key, 3600, value);
        }
    }
    return value;
}

缓存击穿

// 互斥锁 + 永不过期
public String get(String key) {
    String value = redis.get(key);
    if (value == null) {
        synchronized(this) {
            value = redis.get(key);
            if (value == null) {
                value = db.query(key);
                redis.set(key, value, 0);  // 永不过期,手动控制
            }
        }
    }
    return value;
}

缓存雪崩

// 过期时间 + 随机偏移
public void set(String key, String value, long expire) {
    // 添加随机偏移,避免同时过期
    long randomOffset = ThreadLocalRandom.current().nextLong(0, expire / 10);
    redis.setex(key, expire + randomOffset, value);
}

3.3 Redis 集群

  • 主从复制:读写分离
  • Sentinel:故障自动切换
  • Cluster:数据分片

4. 消息队列

4.1 常用场景

场景 说明
异步解耦 订单完成后,通知库存、物流
削峰填谷 秒杀流量写入MQ,慢慢处理
系统间通信 跨服务消息传递

4.2 Kafka vs RabbitMQ

特性 Kafka RabbitMQ
吞吐量
延迟
消息持久化
消息顺序 ✅ 分区有序 ✅ 单队列有序
复杂路由
适用场景 日志、大数据 业务消息

4.3 消息可靠性

// Kafka 可靠生产
properties.put("acks", "all");        // 等待所有副本确认
properties.put("retries", 3);         // 重试3次
properties.put("enable.idempotence", true);  // 幂等生产

// 消费端确认
consumer.commitSync();  // 手动提交offset

4.4 消息顺序

// 确保同一Key的消息发送到同一Partition
producer.send(new ProducerRecord<>("topic", "user_id", message));

// 消费端按Partition顺序处理
while (true) {
    ConsumerRecords<String, String> records = consumer.poll();
    for (ConsumerRecord<String, String> record : records) {
        // 按顺序处理
    }
}

5. 数据库设计

5.1 读写分离

写 → 主库
读 → 从库
优点 缺点
提升读性能 数据延迟
主库专注写入 写操作仍需主库
负载分担 复杂查询可能不一致

5.2 分库分表

垂直拆分

用户库:用户基本信息
订单库:订单相关表
商品库:商品相关表

水平拆分

拆分方式 说明
按ID范围 user_0: id 0-1000万, user_1: id 1000万-2000万
按时间 order_202401, order_202402
按Hash id % 4 = 0/1/2/3

5.3 分页问题

-- 深度分页优化:游标分页
SELECT * FROM orders
WHERE id > last_max_id
ORDER BY id
LIMIT 20;

-- 或者使用ElasticSearch

5.4 索引设计

索引类型 场景
B+Tree 范围查询、等值查询
Hash 精确匹配
复合索引 多条件查询,遵循最左前缀
全文索引 文本搜索

6. 微服务架构

6.1 服务拆分

方式 说明
业务拆分 按业务域拆分(用户、订单、商品)
技术拆分 前台、后台、开放平台
团队拆分 匹配小团队,2Pizza团队

6.2 服务治理

注册中心:Nacos / Consul / Eureka
配置中心:Apollo / Nacos Config
服务网关:Kong / Spring Cloud Gateway
服务监控:Prometheus + Grafana
链路追踪:SkyWalking / Zipkin

6.3 服务间通信

方式 场景
同步 HTTP/gRPC 实时性要求高
异步 MQ 解耦、削峰
Dubbo Java 高性能 RPC

7. 分布式 ID 生成

7.1 方案对比

方案 优点 缺点
UUID 简单、全球唯一 无序、占用空间大
数据库自增 有序、简单 单点、无法横向扩展
Snowflake 高性能、有序 依赖时钟
雪花算法变种 可自行优化 实现复杂

7.2 Snowflake 算法

┌─────────┬────────┬────────┬────────┬────────┬────────┐
│ 符号位   │  时间戳  │ 机器ID │ 序列号  │        │        │
│  1 bit  │ 41 bit │ 10 bit│ 12 bit│        │        │
└─────────┴────────┴────────┴────────┴────────┴────────┘

- 时间戳:可使用69年
- 机器ID:1024个节点
- 序列号:每毫秒4096个ID
public class SnowflakeIdGenerator {
    private final long workerId;
    private final long epoch = 1609459200000L;  // 2021-01-01
    private long sequence = 0L;

    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis() - epoch;
        sequence = (sequence + 1) & 4095;  // 毫秒内序列溢出归零
        return (timestamp << 22) | (workerId << 12) | sequence;
    }
}

8. 接口设计原则

8.1 RESTful API

方法 用途 示例
GET 查询 GET /users/123
POST 创建 POST /users
PUT 更新(全量) PUT /users/123
PATCH 更新(部分) PATCH /users/123
DELETE 删除 DELETE /users/123

8.2 统一响应格式

{
    "code": 200,
    "message": "success",
    "data": {
        "id": 1,
        "name": "张三"
    },
    "timestamp": "2024-01-15T10:30:00Z"
}

8.3 幂等性设计

方法 幂等性 实现
GET 天然幂等
PUT 资源替换
DELETE 删除操作幂等
POST 需额外处理
// 幂等Token
public String createToken() {
    return UUID.randomUUID().toString();
}

// 提交时验证
public boolean submitOrder(String idempotentKey, Order order) {
    if (redis.exists("order:token:" + idempotentKey)) {
        return false;  // 已处理
    }
    // 处理订单
    redis.setex("order:token:" + idempotentKey, 86400, "1");
    return true;
}

9. 系统设计步骤

9.1 分析需求

  1. 功能需求:核心功能有哪些
  2. 非功能需求:QPS、延迟、可用性、数据量
  3. 约束条件:技术栈、团队能力

9.2 设计要点

1. 高并发 → 缓存 + 异步 + 负载均衡
2. 高可用 → 限流 + 熔断 + 降级 + 多活
3. 数据一致性 → 最终一致 vs 强一致
4. 扩展性 → 水平扩展优先于垂直扩展

9.3 常见系统

系统类型 关键设计
设计秒杀系统 库存预扣、MQ异步下单、限流防刷
设计短链系统 哈希算法、301重定向、容量规划
设计IM系统 长连接、WebSocket、消息可靠性
设计评论系统 漏斗模型、异步写入、显示策略

参考资料