一. 背景交代
某NFT数藏平台于3月11日开启抽奖系统, 做了社群推广等市场营销行为, 期间市场负责人有联系技术负责人, 询问是否需要升级服务等, 技术负责人回复先观望;
该平台全部使用阿里云服务, 由于某些原因中间停顿了几个月, 现在又开始活跃起来了
基础服务框架:
出口 == 阿里云负载均衡 <== nginx 静态资源
出口 == 阿里云负载均衡 <== 接口层server <== 服务层server
基础组件包含: 云服务RDS-MySQL, 云服务RDS-Redis
二. 大流量涌入导致问题
自3月11日 20:52 分起, 有大量用户在社群反应:
- 访问卡顿, 尤其是抽奖页面
- 登陆失效
- 无法注册
三. 解决记录
1. 访问卡顿
经运维查询, 数据库压力过大, 截图如下
由百度统计数据查询, 用户访问量激增
时间紧迫, 紧急提升数据库配置, 得益于阿里云服务动态扩容功能, 将服务从4核扩容到12核;
扩容到12核后, 仍然卡顿, 遂扩容到32核以应对高峰访问(扩容只为临时解决问题, 根本原因后来经查询是慢SQL导致)
- SQL 扩容后, 访问有所改善, 但仍然卡顿
经监控数据查得: nginx 服务器 timewait 链接过高, 且出口访问数据速率很大, 两台nginx一度打到 100Mib/s 的上传速率
解决:
- 临时调整 timewait 断开时间为 30s(默认75s), 情况有所改善
- 横向扩容 nginx 从两台扩容到四台, 分担流量压力
- 横向扩容 api 接口服务从两台扩容到四台, 分担流量压力
- 扩容后用户访问速度显著改善
2. 登陆失效
-
测试表现为: 前端页面token莫名丢失, 期间有
redis:connection pool failed
或者read tcp timeout
等错误(连接池满;读取链接超时) -
登陆token 使用 redis 保存, 在阿里云上操作 redis 临时增加一个分片, 负载并不高
-
经配置排查发现 redis PoolSize 参数设置过低(500), 经云服务RDS-Redis 最大支持链接数计算(总支持链接数/服务数量), 将参数设置到 40000
-
问题仍未解决, 进一步排查发现关键服务(登陆后保存token到redis) 的 PoolSize 未设置, 为 go-redis 默认值:
5*内核数
, 遂增加该项目 PoolSize 参数 -
问题解决
3. 无法注册
用户注册显示 redis get lock timeout
根据用户微服务模块代码审查及日志信息表现来看, 属于redis分布式锁无法释放/获取的问题
代码段:
lock, err := redis.GetLock(redis.UserRegisterLock)
if err != nil {
logger.Error("GetLock err:", err.Error())
return
}
nickName := ""
// 生成nickname
for {
nickName = utils.RandSeq(8)
has, err1 := dao.CheckNickName(nickName)
if err1 != nil {
logger.Error("CheckNickName err:", err1.Error())
err = err1
return
}
if has == 0 {
break
}
}
redis.ReleaseLock(redis.UserRegisterLock, lock)
代码段中存在的问题:
- 死循环查询 nickname 是否重复, 大量占用 SQL 资源
- 未设置 超时 或 maxRetry次数, 导致 redis 锁卡死
- nickname 8位太短, 用户量大的情况下, 容易重复
解决:
- 排查数据库中 用户表内的 nickname 字段为 Normal 索引后, 紧急去除 nickname 不允许重复的规则, 用户注册功能恢复
四. 复盘
1. SQL 优化
- 抽奖页面有查询
用户邀请实名人数
的SQL请求, 其关键查询字段未加索引(或单个字段的索引, 不是联合索引), 随即为该SQL查询增加联合索引 - 联合索引增加生效后, SQL CPU占用明显降低
- RDS CPU 占用降低后, 将32核 RDS 降级为 8核 RDS, 经测试访问稳定
- 开启 RDS自动升级策略, 以免类似情况再被打的措手不及
2. 改进尝试
- 静态资源改进
- 根据nginx访问流量推断, 用户激增的情况下, 单个用户需要下载约 440KB 大小的
JS文件
到本地方可进行页面渲染, 导致了 nginx 服务压力急剧上升 - 现根据 阿里云 ALB 负载均衡访问方式, 将静态资源放到CDN中, 去除nginx代理静态资源, 释放静态资源访问压力到CDN中
- 根据nginx访问流量推断, 用户激增的情况下, 单个用户需要下载约 440KB 大小的
- 慢SQL持续优化
- 在阿里云慢SQL访问日志中观察到其余慢SQL(由一条慢SQL引发的连锁反应), 后续根据评估和SQL日志持续跟进优化
- timewait 改善
- 根据阿里云建议, 对 timewait 进行linux内核参数调整
- 调节 keepalive 参数以期降低 timewait
- 做好横向扩展模板设置
- 阿里云机器模板设置, 降低横向扩展时长, 做好快速部署响应
3. 小结
- 架构稳固, 横向扩展性良好
- 扩展后, 成功承载后续21:00-24:00大流量访问需求
- SQL问题不要慌, 先查慢SQL, 扩容只是临时解决方案