前因
2020年之初,新冠肺炎突然来势汹汹,在全国人民的团结努力下,抗疫战进入了僵持阶段,2月24日我公司一复工,就接到了成都市XX区领导的需求,需要在一周内开发上线一个针对全区师生(10万人左右)疫情每日打卡平台。
分析
其实就是需要做一个用户每日登陆填报问卷的功能。此平台需要有一个管理后台,一个h5打卡填报及其附属页面。
雏形
由于时间紧,对项目进行简单分析后,确定功能(后台:用户信息管理、打卡记录、数据统计,前台:用户登陆、打卡、快速打卡、打卡记录)后,我们就着手开发。功能很快开发开发完成并上线。
迭代
上线前的测试阶段,我们发现有些信息是固定的但每个人都需要查询,比如省市区信息,于是我们进行了redis缓存。由于后台的统计过于复杂,于是使用golang开发了计划任务,每两分钟统计一次数据。
领导验收时又提了一些功能改进,在改进时,我们发现用户数有10万左右,单一的打卡接口可能会堵塞,于是进行了优化,打卡接口只进行普通的填报信息校验,校验通过后,异步调用golang接口进行打卡信息保存,这样有一个坏处就是数据保存延迟,可能会出现打卡后看不到记录。为防止用户多次打卡,在填报信息校验通过后,将当天打卡人的唯一标识存入redis,如果存在redis中就不让再次打卡,当然如果打卡信息保存失败,go程序需删除该人当日的打卡缓存。
上线试运行期间,发现用户打卡打卡页面很慢,经分析,服务器带宽只有10M,前端页面图片未压缩,于是进行图片、js、css等资源文件压缩,单纯压测前端页面(压缩后未425k),明显带宽不够,加大到100M,前端页面图片CND加速后,压测1000并发无问题。压测打卡接口,1000并发连接超时失败率20%,800并发连接超时失败率10%。
压测一个通宵后,本以为平安无事了,结果第二天一早上就卡得不行,原来是后台查询过慢,导致cpu飙升,前台用户查询不到数据,看慢查询发现,时不时有几个查询sql消耗时间过长,show preocesslist也看到异常。先加索引优化,但效果不明显,结果发现是在进行联表查询的时候,大表联了大表,大表牵引本来就是问题,于是对业务表进行了合并,5张表合成一张表,并添加索引,后台查询立马飞快。更新程序后,暂时一切正常了。
没想到第二天一早上服务器有出问题了,并发飙升到2万多,有很多insert 语句造成了锁表,而很多的select语句有无法执行。突然意识到myisam使用insert的时候会造成锁表,需要支持并发插入,得修改concurrent_insert=2,;另一方面有个在慢查询中有两个select语句出现很频繁,但耗时不高(0.60s左右),explain发现居然没有使用索引,立马加上索引,查询耗时降低到0.02s。在无法kill sql进程的情况下,只能进行停止Nginx,重启数据库,重启后一切正常。
由于可能并发过高,我们选择了服务器升级,单台数据库可能扛不住。全迁移到阿里云,使用阿里云的数据库,读写分离,一主多从,弹性伸缩。一切ok,监控两日后发现并发在8000左右。
总结
架构设计的原则:简单、合适高于一切。
高并发需考虑带宽,页面缓存压缩、静态数据缓存、接口异步多线程处理。
慢查询除了关注耗时过长的sql,还得注意使用频繁的sql。
高并发当程序优化到极致后,需考虑服务器优化,读写分离,redis集群等。