复盘-记一次慢SQL导致的雪崩

一. 背景交代

某NFT数藏平台于3月11日开启抽奖系统, 做了社群推广等市场营销行为, 期间市场负责人有联系技术负责人, 询问是否需要升级服务等, 技术负责人回复先观望;

该平台全部使用阿里云服务, 由于某些原因中间停顿了几个月, 现在又开始活跃起来了
基础服务框架:
出口 == 阿里云负载均衡 <== nginx 静态资源
出口 == 阿里云负载均衡 <== 接口层server <== 服务层server
基础组件包含: 云服务RDS-MySQL, 云服务RDS-Redis

二. 大流量涌入导致问题

自3月11日 20:52 分起, 有大量用户在社群反应:

  1. 访问卡顿, 尤其是抽奖页面
  2. 登陆失效
  3. 无法注册

三. 解决记录

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. 改进尝试

  1. 静态资源改进
    • 根据nginx访问流量推断, 用户激增的情况下, 单个用户需要下载约 440KB 大小的JS文件到本地方可进行页面渲染, 导致了 nginx 服务压力急剧上升
    • 现根据 阿里云 ALB 负载均衡访问方式, 将静态资源放到CDN中, 去除nginx代理静态资源, 释放静态资源访问压力到CDN中
  2. 慢SQL持续优化
    • 在阿里云慢SQL访问日志中观察到其余慢SQL(由一条慢SQL引发的连锁反应), 后续根据评估和SQL日志持续跟进优化
  3. timewait 改善
    • 根据阿里云建议, 对 timewait 进行linux内核参数调整
    • 调节 keepalive 参数以期降低 timewait
  4. 做好横向扩展模板设置
    • 阿里云机器模板设置, 降低横向扩展时长, 做好快速部署响应

3. 小结

  • 架构稳固, 横向扩展性良好
  • 扩展后, 成功承载后续21:00-24:00大流量访问需求
  • SQL问题不要慌, 先查慢SQL, 扩容只是临时解决方案
posted @ 2023-03-12 15:11  GETTOLIVE  阅读(52)  评论(0编辑  收藏  举报