---
title: 削峰
taxonomy:
    category: docs
---

顾名思义，将突发的流量高峰削平。大促的时候，系统顶不住，其实就是那么一会儿顶不住，只要初期的高热度下去了，系统的整体负载会迅速下降到没那么危险的水平。所以，对付突发流量，削峰是第一要务。

### 缓存——饮鸩止渴

请原谅笔者用饮鸩止渴这个词形容 95% 的 Web 系统的真正性能顶梁柱，实际上缓存的贡献远不止于此。

1. 缓存以降低数据更新频率为代价，极大地提升了读取 API 的 QPS 极限
2. 缓存可以设计成多级，形成一个逻辑上的“存储器山”
3. 缓存可以像事务隔离级别一样划分成好几种“性能+数据一致性”级别

但是，使用缓存确实是在饮鸩止渴：缓存在带来性能的同时，大幅削弱了数据库提供的“单点性”，为系统失效埋下了一堆地雷。

缓存的毒性无法消除，一旦系统的某些部分失效，这杯毒酒就会发作，但是会不会把自己毒死还要看架构水平和基建能力：你的机房别随便断电，你的服务器别随便宕机，你在喝下缓存毒酒之后，就能活的够长。

#### 终极缓存方案

我们前面章节中讨论过的 OceanBase 就采用了一种终极缓存方案：

1. 内存不是快吗，那我就把所有热数据全部放到内存里
2. 那对热数据的修改怎么办？redo log 落盘啊
3. 你说数据库重启怎么办？上冗余，一个节点一时半会儿起不来也没问题
4. 整个集群重启呢？都天灾了，集群重启后，停止服务一个小时还是可以接受的吧

12306 每天夜里都要维护一个小时，笔者没有证据地胡乱猜测一下，它在将 redo log 落盘。参考资料：[《12306互联网售票系统的架构优化及演进》](http://tljsjyy.xml-journal.net/article/id/3756)。

### 队列——欢迎来到地球

缓存可以削“读”的峰，队列就是拿来削“写”的峰的。

队列的思想其实早就贯穿在人类社会的每一个角落了：超市结账需要排队，做核酸需要排队，火车站打车需要排队，网上抢购商品也需要排队。排队的本质是将一拥而上购买变成了“异步”购买：你想买东西？先排队，过段时间轮到你了，看看商品有没有卖完，没卖完你就能买到。

排队的思维特别好理解，而现实中防止超售功能也确实是基于排队功能做的。传统数据库的事务隔离属于强迫用户等待，而现在使用队列系统来处理排队，排队这件事情才真正异步了起来。

### 奇技淫巧

电商下单在普通压力下，一个队列就能解决问题，但是当你面临每秒几十万单的时候，如何让这些订单真正地下单成功，才是最需要解决的问题，这个数字就是“系统容量”。队列是无法提升系统的绝对容量的，那该怎么办呢？

继续找东西和信息之神交换：过去的时间换现在的时间。

在大促之前，先把订单生成好，然后用户下单时直接写入用户信息：不需要执行“检查库存”这个单点操作了，负载低了很多。当初想出这个方法的人实在是太机智了。

### 现实世界中的削峰

现实世界中，一个一百万 QPS 的电商系统，真正需要触达到数据库的 QPS 其实是没有 500 万那么多的，在削峰的操作下，200 万 QPS 的 PolarDB 集群在 Redis 集群的配合下，是可以顶住 100 万 API QPS 的。史上流量最大的那一年双 11，每秒订单创建最大值仅为 58.3 万笔，这已经是这个地球上的最高记录了。

但是，现实世界中，一切都要讲 ROI（收益成本之比）的，搞一套顶配的 PolarDB 集群确实可以顶住巅峰时期一百万 QPS，但是你老板看着账单肯定会肉痛，那，该如何省钱呢？

答：`站在地球表面`。
