如果这是一道面试题#如何解决健康云app崩溃的性能问题


2022-03-31



这几天最让上海居民揪心的无疑是疫情了,然而在这战疫的节骨眼环节,“上海健康云”先崩溃了。
 

据了解,“健康云”在成立时为单一的慢病管理平台,后转变成为“互联网+医疗健康”公共服务的统一入口。近期上海本土疫情多点散发,核酸检测速度不停歇,高峰时段上海每小时采样高达12-13万,此前健康云平台按照每秒钟1万并发做限流保护措施,一般上午9:00至9:35最高峰时,平台达每秒3.6万并发,且持续时间较长,平台随即启动可控技术层面的限流,以此为重点区域人群采样“让路”先行。



来自市卫健委的数据显示,本轮疫情发生后,全市核酸采样量持续攀升。自3月11日起,核酸日均登记量和采样量均超500万。在前期高位运行的基础上,昨天上午,系统核酸检测登记数量433万,比前天同期增加一倍。这其中,10:00至11:00、11:00至12:00两个时间段登记数量均超100万。



某技术人员表示,此次“健康云”宕机事件属于典型单点故障导致全面崩溃,在“健康云”的所有模块中,并发最高的是登录模块,其次则是“核酸检测”模块,而绝大多数厂商完全依赖于集中式平台的思路,一旦遇到特殊情况就无法应对,再者,如果一些模块被其他模块普遍性依赖,也容易成为崩溃的风险。



后续上海市政府也紧急采取应对措施,持续扩容和错峰筛查,建议上海市居民提前登陆“健康云”,截图保存登记二维码。

 


而作为it从业者,如果你面临着这么大并发、大流量和大用户量的项目,该如何下手?



有位知乎网友@沪猿小韩 提出了他的解决方案:

一、核酸记录查询解决方案:

1)mysql集群:部署mysql集群(oracle没用过,咱也不敢说),把用户均匀存入各个数据库节点中,每个节点的主库设置多个从库,目前我的实战项目只设置过一个从库,历史记录查询db用从库;

2)哨兵模式:开启哨兵模式,某个节点主库挂掉可以快速选master主库;

 
3)垂直分表,针对健康云查询记录展示的字段来看,建议把这些字段单独建表,也就是垂直分表。结果字段中,身份证号,检测结构以及检测结果字段长度是比较大的,所以最好分离出来,避免放在用户主表中,而且也能减少join联查;
4)队列异步处理结果:作为phper我们常用yii2-queue队列,java用rabbitMQ和kafka比较多;核酸检测结果出来后,扫码枪扫描二维码,然后就将这个用户放入消息队列中,消费者程序将查询记录需要的字段写入在独立的结果表中,毕竟核酸结果不是实时的,24小时内出来就行;
5)DBA通过联查:把结果放入新增的表中,用insert select 组合,这个会比后端代码快很多,不过需要多一个DBA参与,增加了用人成本;
6)给表中会员ID字段建立索引;
7) 采样时间和检测时间数据库存时间戳,从数据库中取出来后端处理成北京时间;
 
8)检测结果那句话,除了阴阳两个字段后端动态提供,其余的文字前端写死;
9)查询结果页面只显示最新的核酸检查结果,想要看更多,点击更多再去请求后端;
10)最新的一条核酸记录放在redis集群中,并使用hash数据结构存储,用户ID为key,其他结果字段filed:value格式存储
11)有人会说用elasticsearch存储结果,查询起来更快,我们的场景是根据ID查询结果,但是es适合模糊查询,用的是倒排索引机制,例如项目中有大量的商品,根据商品名字查询结果,或者大量文字,根据某个关键字查询;这个时候是非常适合es的。
二、我的登记码问题解决方案
1)主从分离,提交登记码信息这个操作用主库;
2)将填写过的用户信息入数据库并放在缓存中,避免每次去数据库取这些数据,减少mysql IO操作,毕竟一个人会做N次核酸,但是用户基本信息不会经常变;
3)生成的登记码(二维码),尽可能生成一张图片,数据库或者缓存存储,甚至可以把图片放在CDN上,个人理解这个二维信息是不会变得;
 
4)接口异常,页面需要美化,稍微有点经验的产品经理都懂得吧,查询接口知道处理,为啥生成登记码这个接口就这样异常呢;

 
5)前端限流:避免多次点击,多次点击只进行一次请求,之前PHP混编开发的时候会用,现在基本都前后端分离了,这块需要前端,android和ios的同事去处理了;
6)个人检测登记中的企业查询这里的公司数据可以维护在es中,如果是调的三方接口那解决起来就不是很方便了;
 
 
7)mysql和redis连接类封装出一个连接池,避免频繁地向数据库申请资源,释放资源带来的性能损耗;
8)AB测试,用AB测试工具压力测试。。。
三、以上的问题涉及到后端,前端、运维、产品和测试,以及硬件资源。