---
title: 笔者的电商秒杀真实经验
taxonomy:
    category: docs
---

笔者在工作中曾经成功地用 Nginx 改造了一个高并发的电商秒杀系统，保证了业务的正常开展，下面是笔者的真实故事。

### 新冠时期的机遇

在新冠疫情初期，住范儿的私域电商业务迅速发展。尽管 3 月中旬才复工，3 月份半个月的 GMV 就超过了前一年的总和，并在 4 月就完成了全年目标。然而，这也导致电商系统的性能压力急剧增加。

当时，我们的电商系统和第一版淘宝一样是购买的一个 PHP 单体系统，天生不具备扩展性。再加上我们采用团购秒杀的业务模式，这给系统带来了巨大的挑战。客户端主要使用微信小程序，而服务端则提供两种主要服务：开团瞬间的海量 HTTP API 请求以及每个页面都非常消耗资源的订单管理后台。

笔者面临的第一个问题是数据库无法承受如此巨大的压力。经过分析，笔者发现请求数最高的接口是商品详情，于是为该接口增加了一层保持时长为一分钟的 Redis 缓存，这个动作显著降低了开团瞬间的数据库压力。

而且幸运的是，当时阿里云刚刚商用 PolarDB 几个月。笔者利用 PolarDB 成功应对了开团三分钟内涌入的大约 4000 名用户带来的巨大的数据库压力。然而，当笔者将后端云主机升级到 16 核 32G 内存时，出现了一个非常奇怪的现象：

1. CPU 和内存占用率分别仅为 8% 和 6%。
2. 大量新用户无法建立 TCP 连接，客户端表现为长时间的等待。
3. 如果运气好，在等待一段时间后成功进入系统，那么访问将会非常丝滑，每个接口的响应时间都非常短。

为什么会出现这种现象呢？原因是新用户无法与服务器建立 TCP 连接！

默认情况下，CentOS 7.9 单个进程的最大文件打开数（ulimit）为 1024。由于一切皆文件，每个 TCP 连接也是一个文件，因此也被锁定在了 1024 个。通常情况下，我们会将这个数字设置为 65535。然而，笔者观察到这台虚拟机最大 TCP 连接数只能达到 5-6K 之间，远远无法满足用户需求。无论是采用 prefork、worker 还是 event 模式都是如此。而原因正是我们之前实测过的：此时 Apache 已经忙不过来，花费了一颗 CPU 核心的全部时间片来进行数据包和线程的匹配。

### 怎么解决的？

为了解决这个问题，笔者在同一台机器上安装了 Nginx 作为反向代理服务器，将所有用户请求转发给 Apache 处理。这个操作立即缓解了请求压力，而且 Nginx 使用的最大活跃 TCP 连接数量也只有 1K，完全满足了三分钟 4000 用户的需求。此时，系统架构和第一章的图 1-1 一样。
