跳转至

营销活动压测面试案例 —— 云盘会员日活动

一、案例背景

某运营商开展“全网云盘会员日”营销活动,活动规则如下:

  • 活动时间:长期运营活动
  • 核心玩法:每月根据会员等级给基础积分;会员日开启当天,用户可通过完成拉新、拉活、AI工具试用等任务获取积分,用于抽奖或兑换奖品。
  • 约束条件:奖品设有库存限制,手动领奖时扣减库存
  • 数据统计周期:当天

关键特征

  • 仅在当天倒计时结束时开放抽奖。
  • 任务包括:拉新/拉活/指定活动停留浏览几秒/AI工具(如文生图)试用,跳转到对应活动h5,满足条件后(通过http回调)获得抽奖次数。
  • 奖品包括:云盘白银会员、联合会员、打车券、外卖券、现金红包(热点奖品)。

活动核心数据及术语说明

基于前端埋点获取到,压测计划时上一期的活动数据

指标 数值
PV 1,315,078
UV 626,039
登陆用户数 469,431
登录转换率 74.98%
中奖数 154,130
中奖率 32.83%
中奖用户数 115,953
领奖数 78,024
领奖率 50.62%

术语说明:

  • PV(Page View,页面浏览量) 指活动页面被打开/刷新的总次数(用户每刷新一次即累计一次),反映页面被请求的总量;
  • UV(Unique Visitor,独立访客) 指访问页面的去重设备/用户数(同一用户多次访问仅计一次),用于衡量真实的用户覆盖规模。

两者结合可计算人均浏览深度(PV/UV)及分析活动吸引力。

二、技术风险分析

基于活动特性,从测试工程师角度,识别以下技术风险:

序号 风险点 测试要点 严重等级
1 中奖奖品被重复领取 检查幂等、高并发(针对奖品库存)、领奖记录最终一致性 L0
2 倒计时结束瞬时高发流量 检查瞬时高并发时系统表现是否达标 L1
3 缓存击穿/雪崩 检查热点奖品Key的缓存压测时不失效,防止大量请求直接打到数据库 L1
4 兜底策略 检查存在服务降级、熔断、限流等 L2
5 消息队列积压 模拟高并发下消息队列积压,检查有预警和扩容机制 L2

基于压测常见瓶颈,增加测试要点

  1. 维持30分钟高并发压测,检查是否内存泄露出现OutOfMemory(OOM);或者压测结束后,CPU使用率锯齿状且一直未下降,需要检查是否GC频繁、死循环、激烈锁竞争;结合arthas排查高cpu线程。
  2. 韧性测试,故障注入模拟服务故障的降级、兜底处理。

三、压测关键指标获取与计算

数据来源以APM(SkyWalking)埋点日志为主

1. 数据来源及业务配比计算

遵循 "历史基线 + 业务预期 + 漏斗转化" 三层校准。

数据来源权重建议:

数据源 权重 理由
APM 埋点日志 50-60% 最精准,含实时调用量、RT、错误率
数据侧预测增长 20-30% 业务侧对营销力度、用户增长有预判
漏斗转化衰减 10-20% 修正压测脚本,确保符合业务逻辑

团队协作职责边界:

职责项 主要负责方 测试人员需掌握/行动
SkyWalking 探针部署 开发/运维 提醒运维压测期间临时关闭或过滤探针,避免污染线上监控
SkyWalking 数据提取 测试人员(自行动手) 登录 Dashboard 按接口维度导出调用量 CSV
业务转化率(中奖率等) 数据侧(BI/DBA) 主动提出统计口径(如按小时粒度),拿到数据后校准压测脚本
Nginx 原始日志捞取 运维/基础架构组 掌握 Shell(awk/grep)自行分析,不依赖运维代劳

2. 高峰期QPS、TPS、并发数计算

SkyWalking 提供的数据指标

原生指标 含义 获取路径
CPM Calls Per Minute(每分钟调用数) Dashboard → Service/Endpoint → CPM 折线图
响应时间 P50/P75/P90/P95/P99 RT Dashboard → Response Time 折线图
错误率 Error Rate % Dashboard → Error 折线图
SLA 成功率 % Dashboard → SLA 折线图

数据提取步骤

步骤一:定位高峰时段

1. Dashboard → Service Dashboard
2. 时间选活动当天全天
3. CPM 折线图峰值 → 鼠标悬停看具体时间(如 10:05)

步骤二:放大高峰时段获取接口级数据

1. 时间范围改为高峰时段(如 10:00-10:15)
2. Service → Endpoint List
3. 点击 Export → CSV(含各接口 CPM、P99 RT)

QPS/TPS/并发数计算

指标 计算公式 说明
QPS CPM ÷ 60 读接口(GET)每秒查询数
TPS CPM ÷ 60 写接口(POST/PUT/DELETE)每秒事务数
并发数 QPS × 平均响应时间(秒) Little 定律:并发 = QPS × RT

计算示例(高峰时段 10:00-10:10):

接口 CPM P99 RT QPS/TPS 并发数
GET /api/user/info 18,000 150ms 300 QPS 300×0.15 = 45
POST /api/lottery/draw 6,000 300ms 100 TPS 100×0.3 = 30
POST /api/task/submit 30,000 200ms 500 TPS 500×0.2 = 100
合计 - - 900 175

注意事项

  • CPM 粒度为 1 分钟,高峰时段建议取多个点求平均
  • 区分读写:GET → QPS,POST/PUT/DELETE → TPS
  • 并发计算用平均值更准确,压测目标设定用 P99
  • 如需按业务关键字筛选(如"会员日"),需提前埋 ActiveSpan.tag("activity", "会员日")

最终得出压测目标: - 目标TPS:700-1000 - 实际达标TPS:850

四、方案与资源规划

click me

五、面试追问高频题与深度答疑

Q1:压测链路业务配比,当只有elk+nginx时,如何获取?

面试官考察点:在没有 APM 的情况下,是否具备从基础日志中提取压测数据的能力。

(一)Nginx 日志提取接口 PV 分布

当没有 APM 时,Nginx access.log 是最基础的数据来源。

基础命令:

cat access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20

生产级优化(面试加分点):

# 1. 过滤静态资源,只看业务接口
cat access.log | grep -v -E '\.(css|js|png|jpg|gif|ico)$' \
  | awk '{print $7}' | sort | uniq -c | sort -rn | head -20

# 2. 按高峰时段切片(如活动开启后10分钟)
grep "15/Jul/2025:10:0[0-9]" access.log \
  | awk '{print $7}' | sort | uniq -c | sort -rn | head -20

# 3. 统计各接口 QPS(按分钟粒度)
grep "15/Jul/2025:10:0[0-9]" access.log \
  | awk '{print $4, $7}' | awk -F: '{print $2":"$3}' \
  | uniq -c | awk '{print $1/60, $2}'  # 每分钟调用量 ÷ 60 = QPS

推荐工具: - GoAccess:实时分析 Nginx 日志,生成 HTML 报告,直接看接口 PV 排名 - ELK/Kibana:将 Nginx 日志导入 ES,用 Kibana Visualize 做接口调用量折线图

(二)ELK 提取业务转化率

ELK 存储的是应用日志(如业务日志、错误日志),可用于提取转化率。

典型查询(Kibana Discover):

# 查询中奖日志
message: "lottery_success" AND activity: "会员日"

# 统计中奖次数
activity: "会员日" AND message: "lottery_success" | count()

# 计算中奖率
中奖次数 / 抽奖请求次数(需两条查询合并)

注意:ELK 查业务转化率的前提是应用日志中有业务埋点(如中奖、领奖的日志输出)。如果没有业务埋点,则需从业务数据库/数据仓库获取。

(三)业务配比计算流程(ELK + Nginx 组合)

1. Nginx 日志 → 各接口 PV 比例(如登录 10%、任务提交 60%、抽奖 30%)
2. ELK 日志 → 业务转化率(如中奖率 32.83%、领奖率 50.62%)
3. 业务数据库 → 用户漏斗数据(如登录转化率、任务完成率)
4. 三者结合 → 校准压测脚本的业务配比

(四)局限性对比

数据源 优势 局限
Nginx 日志 有 PV/QPS,无需额外部署 无响应时间、无业务转化率
ELK 日志 可查业务埋点、错误堆栈 需应用埋点,无调用链路
APM 有调用链路、RT、错误率 需部署探针,有学习成本

面试金句:

"当只有 Nginx + ELK 时,我会先用 Shell 命令或 GoAccess 从 Nginx 日志提取接口 PV 分布,再用 ELK 查业务埋点日志获取转化率。虽然没有 APM 的链路追踪,但组合使用仍能得到合理的压测配比。如果条件允许,我会建议团队引入 SkyWalking 等轻量级 APM。"


通过nginx日志统计接口访问量

cat access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20
# 命令含义:awk '{print $7}' 提取URI路径 → sort排序 → uniq -c统计次数 → sort -rn降序 → head -20取Top20。

“Nginx日志法仅适用于宏观PV总量和接口热点排序,但无法支撑压测配比决策,因为:

1.无法关联用户行为漏斗(不知道这15万次抽奖是由多少‘登录用户’发起的,也无法知道‘任务完成数’)。

2.无法剔除静态资源/爬虫/健康检查(K8s的/actuator/health探针会污染统计)。

3.实时性差(离线分析,无法动态调整压测模型)。”

Q2:如何评估并发用户数?压测机台数怎么算?(QPS=1万,RT=500ms)

面试官考察点:是否掌握 Little定律 和施压机性能瓶颈。

第一步:计算总并发线程数(VU)

  • Little定律:并发用户数 = QPS × 平均响应时间(秒)
  • 代入:10,000 QPS × 0.5s = 5,000 并发线程(VU)

第二步:计算所需压测机台数

单台施压机上限认知:

  • JMeter每个线程(VU)默认占用约 1MB 堆内存
  • 8C16G施压机,分配 -Xmx12G,理论上限 12,000 线程
  • 生产经验值:单台安全并发控制在 1,500 ~ 2,000 VU(超过则GC频繁,数据失真)

计算公式:

  • 所需台数 = 5,000 ÷ 1,500 ≈ 3.3 台
  • 最终建议:准备 4台施压机(3主力 + 1冗余),确保施压机带宽 > 被压机带宽

Q3:压测时 TPS 上不去但 CPU/内存不高的排查方向?Arthas 何时上场?

面试官考察点:区分"业务处理慢"与"网络/中间件/OS限制"的能力,以及是否会使用高级动态诊断工具。

核心结论:TPS上不去但CPU/内存空闲 → 瓶颈不在应用计算或堆内存,而是卡在"IO等待"或"外部协调"。

(一)基础排查方向(按优先级):

优先级 排查方向 具体方法
1 数据库/Redis连接池 查看 DataSource 活跃连接数是否等于 maximumPoolSize,大量线程在等待连接
2 下游依赖超时(AI/第三方) 检查调用第三方接口的响应超时配置,线程堆栈是否有大量 SocketTimeout
3 网卡带宽打满 iftop/nload 查看网卡流量是否达到内网限速(如1Gbps)
4 OS内核限制 检查 net.ipv4.ip_local_port_range(默认3万)是否耗尽,TIME_WAIT 堆积
5 分布式锁竞争 抽奖扣库存用 Redissonselect...for update,大量线程在自旋等待锁释放
6 GC停顿 查看GC日志,检查是否频繁发生STW(虽然CPU不高,G1的Concurrent Mark阶段偶发)

(二)高级武器——Arthas(在线动态诊断,无需重启)

jstack 只能看到线程状态但无法定位深层原因时,Arthas 是冲击高分的关键。以下是3个杀手级场景:

场景 Arthas 命令 实战效果
定位外部接口超时根因 trace com.xxx.TaskService submitTask 动态打印方法内每一步的精确耗时。若发现 900ms 耗费在 HttpClient.execute(),而业务代码仅用 1ms → 秒级定位网络IO超时。
线上临时开启 DEBUG 日志 logger --name ROOT --level debug 怀疑分布式锁 tryLock 在空转,但线上 INFO 级别看不到。实时改为 DEBUG 观察等待时间,验证完再改回,免重启。
反编译验证线上代码版本 jad --source-only com.xxx.StockService 怀疑部署包不是最新代码。直接反编译 JVM 内存中的字节码,若发现确实缺少判空逻辑 → 实锤截图,终结扯皮。

面试金句:

“当 top -Hjstack 看不出明显锁等待时,我会果断上 Arthas 的 watch 命令,监控扣库存方法的入参和返回值,观察 remainingStock 是否在临界值异常归零。Arthas 让我能在不停服的情况下完成‘手术刀式’的精准诊断。”

Q4:压测有没有遇到什么瓶颈,做了什么优化?

思路:现象 → 分析排查 → 优化方向

大Key

现象

压测持续到第10分钟时:

  • RT曲线陡增:核心抽奖接口的P99 RT从30ms飙升到1.2秒。
  • DB负载异常:MySQL的CPU从10%突然飙到80%。
  • 资源矛盾:应用服务器CPU和内存都还有余量,说明瓶颈不在应用层。
  • 监控面板:redis 命中率下降至60%,evicted_keys(淘汰键数)指标正在以每秒几百个的速度疯狂增长,同时Redis内存使用量一直压在maxmemory的水位线上。
排查

1.分析Redis驱逐策略与Key分布

redis-cli INFO stats | grep evicted_keys
redis-cli CONFIG GET maxmemory-policy

策略是 allkeys-lru,说明最近最少使用的Key被清掉了。结合现象,怀疑是大key挤占了其他缓存的空间。

2.定位大Key(Big Key)

redis-cli --bigkeys
redis-cli MEMORY USAGE {key}

使用 redis-cli --bigkeys 扫描,重点查看 prize_config 对应的数据结构大小。

3.怀疑缓存穿透/击穿流量

查看Redis监控的 keyspace_misses(未命中数)与 keyspace_hits(命中数)曲线。未命中数陡增的时间点应与 evicted_keys 陡增时间点完全重合,并确认是否未做缓存空值处理布隆过滤器前置拦截缓存失效时间(TTL)设置是否集中

根因

技术债务+业务演进

由于奖品配置由运营同事在平台配置的,加上最初设计时所有奖品都存在string格式的key,后续有效的、无效的奖品都存在这个key里,加上ttl永不过期。

在压测高并发下,内存很快触达maxmemory。

虽然 allkeys-lru 没有淘汰这个极热的大Key(因为它太常被访问),但这个Key自身占用了大量内存,导致Redis中其他业务缓存(如抽奖校验、限流计数器等)被allkeys-lru频繁淘汰。

优化
  • 大Key拆解为Redis Hash,只缓存有效奖品
  • 逻辑过期 + 本地缓存:引入逻辑过期机制,物理TTL设为永不过期,在Value中埋入logicExpireTime,由后台异步线程定时刷新,彻底消除击穿风险。同时,在应用内存引入Caffeine本地缓存作为第一道防线,压测时99%的奖品读请求由本地内存直接返回,完全不经过Redis。

六、压测通过标准建议

维度 标准
核心接口 RT(P99) ≤ 500ms
登录接口 RT(P99) ≤ 1s
错误率 ≤ 0.1%(不含业务预期的"库存不足")
系统资源水位 CPU ≤ 70%,内存 ≤ 80%
压测时长 峰值水位持续 30分钟 无劣化趋势
超卖检测 库存字段 ≥ 0,无负数记录

面试官终极追问提示:"如果压测时发现 P99 响应时间合格,但 P999 飞得很高,你如何排查?"——此时可回答:检查GC日志中的Full GC频率、查看是否有跨机房调用、检查是否有热点Key的驱逐重加载逻辑。