场景积累

常见问题及解决方法

接口幂等性

  • 数据库唯一键
    • 唯一键(插入操作)
    • 防重表(插入操作)
  • 乐观锁
    • 更新操作
  • 防重 token
    • 插入、更新操作
    • 可能由于网络原因,客户端无法感知服务端处理请求失败

定时任务

  • cron 表达式
  • 定时扫表
    • MySQL、Redis
  • 延迟队列(多级时间轮)
    • rocketmq

数据一致性

  • 强一致
    • 分布式事务(TCC)
    • 分布式锁
  • 最终一致性
    • 先写 MySQL,再删 Redis
    • 延迟双删
    • 异步更新
    • 消息队列补偿
    • 通过canal 订阅 binlog

消息推送

  • 推模式
    • 优点
      • 实时性高
      • 生产者控制发送时机
    • 缺点
      • 资源浪费,消息可能不是消费者所需要的
      • 消费者可能过载,如果生产速度过快,消费者可能会崩溃
  • 拉模式
    • 优点
      • 消费者控制
    • 缺点
      • 实时性差
      • 额外的轮询开销

《亿级流量系统》

高并发读场景方案

  • 数据库读/写分离
  • 本地缓存
  • 分布式缓存
    • 缓存穿透
    • 缓存击穿
    • 缓存雪崩

CQRS (command query responsibility segregation)模式:是一种将数据的读取操作与更新操作分离的模式。


下面是在一个多表关联查询场景使用 CQRS 模式的例子:

高并发写场景方案

数据分片(MySQL 为例)

背景

在高并发写场景中常见问题:

  1. 数据量大
    1. 单表读/写效率低
  2. 并发量大
    1. 连接资源
    2. CPU 资源
    3. 内存资源
    4. 网络带宽
  • 分库
    • 将数据库拆分成多个小数据库
    • 充分利用多台服务器的资源
  • 分表
    • 将单个数据表中的数据分开存储到各个表中
    • 提高一台服务器的数据库处理能力
垂直分片
  • 垂直分库
    • 将实现不同业务归属的数据解耦, 将不同业务数据交给各业务研发团队独立维护,有效保证了各团队的职责单一。
    • 由于垂直分库后,不同的业务数据由不同的服务器维护,数据库并发量会有一定的提升。
  • 垂直分表
    • 将一个数据表按照字段分成多个表,可以很好地隔离核心字段和非核心字段
    • 适合与数据量不大并且字段个数比较多的场景
      • 提高查询命中率(cache)、减少磁盘 I/O
水平分片

水平分库和水平分表的区别在于,前者分片后数据在不同的数据库上,后者分片后数据仍在同一数据库中。

水平分表解决了单表数据量过大的问题,水平分库使数据库拥有分布式能力。

水平拆分规则:应尽可能避免数据偏斜、数据热点问题

  • 范围分区法
  • 哈希分区法
  • 一致性哈希分区法
扩容方案

推荐一个平滑的扩容方案是从库升级法。

  • 对于待扩容的分库增加从库,开始主从复制。(这一步不一定需要)
  • 从库数据同步完成后,主库临时禁用写操作请求
  • 检查主从数据库数据是否一致,如果一致,则断开主从关系
  • 修改数据路由规则,使得原分库负责前一半数据范围内的数据,新分库负责后一半数据范围内的数据
  • 确认上游业务感知分库数据范围发生变化后,解封全部的写操作请求
  • 启动离线任务,将原分库和新分库冗余的数据删除

异步写和写聚合

数据分片本质是通过系统的可扩展性来支持高并发写的,除此之外我们还可以从业务的角度和数据特点的角度来提高系统的写性能,常见的两种方案是:异步写和写聚合。

异步写
  • 将用户的写请求以适当的方式暂存到一个数据池中,然后立刻响应用户,缩短写请求的响应时间
  • 后台线程不断从数据池中取出数据,执行真正的写操作
  • 写操作的结果由用户主动查询,或者一种实时性较好的方式是在写操作执行完成后,主动将结果推送给用户

异步写常见场景

  • 跨公网调用
  • 秒杀系统异步化
写聚合

写聚合就是将多个写请求聚合成为一个写请求,减少了写请求量。

  • Kafka Producer 批量生产

通用的服务可用性治理手段

  • 容错性设计:要接受网络脆弱和下游服务不可靠的事实,在设计过程中要充分考虑出现相应故障时的解决方案
    • 手段
      • 重试
      • 降级
  • 流量控制
    • 既要对上游服务调用采取预防性措施,防止打垮我们的服务
    • 也要对下游服务有感知和保护意识,当发现下游服务出现质量下滑时,需要及时通过自身保护下游服务
    • 手段
      • 熔断
      • 隔离
      • 限流

重试

重试是应对网络抖动的最简单方法,但前提是下游服务接口必须支持幂等接口。

接口幂等性指的是:无论一个请求被执行多少次,其结果都与第一次相同。

幂等性

常见实现幂等性的方法:

  • Redis 分布式锁
  • 数据库防重表(写入操作)
  • token
重试时机与重试策略

RPC 接口调用通常会遇到以下几类错误:

  • 业务逻辑错误
  • 服务质量异常错误
    • 下游服务限流
    • 下游服务失败率过高触发熔断
    • 下游服务拒绝服务
  • 网络错误
    • 请求超时
    • 网络丢包
    • 网络抖动

常用的重试策略:

  • 无退避策略
  • 线性退避策略
  • 随机退避策略
  • 指数退避策略
  • 总和退避策略

一般地:

  • 业务错误不进行重试
  • 服务质量异常错误可以采取退避策略进行重试
  • 网络错误可以采取无退避策略进行重试