2

Redis让系统卡爆了,灵机一动换成MongoDB…… - MongoDB - dbaplus社群:围绕Data、Bloc...

 7 months ago
source link: https://dbaplus.cn/news-162-5843-1.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Redis让系统卡爆了,灵机一动换成MongoDB……

苏三 2024-01-16 10:23:31



前言

最近我们的商城系统出现了一个线上问题,用户访问商城首页的时候要差不多20秒,才返回数据,可以说卡爆了。到底怎么回事呢?

一、案发现场

上周四晚上,我们有一个正常的迭代版本按照预期的时候上线。本次迭代,我所涉及的功能,很快上线,并且测试通过了。但没法下班,因为项目组其他同事,还有线上问题在紧急处理。

我过去了解了一下情况,用户访问商城首页的时候响应太慢了,要20秒才返回,有用户投诉过来了。进一步了解之后发现,造成这个问题的根本原因是redis服务器挂了。

为什么会挂呢?

是因为一次性往redis中存储的数据太多了,导致内存不足。这个商城系统部署到了阿里云上,当时购买了1G的内存空间。

但由于这次上线,有个新功能,需要在商城首页上,按不同的地区,推荐不同的商品。商品还要按不同的分类做区分。原本商品只有几十万其实不多,但是按地区和分类做区分之后,保存的数据量乘以了几百倍,一下子占用了大量的内存。

redis挂了为什么会导致首页慢呢?

答:因为代码中有业务逻辑,如果从redis中没有获取到数据,或者访问redis失败了,会从数据库中获取。虽说当时是晚上,用户并发量不大,但是直接访问数据库,响应时间一下子下降了很多。

图片

二、如何快速解决问题?

目前的这套方案,先从redis中获取数据,如果失败了,再从数据库中获取。

现在的问题是:redis内存不足,临时解决问题,只能加内存资源了。

因为加内存是最快的,直接加到了4G。如果要改代码,这个功能今天晚上可能没法上线,之前购买的1G的资源确实有点小。

在阿里云上redis加了内存之后,这个问题很快解决了,首页访问速度一下子提升。但这不是问题的本质。

三、复盘

第二天,我们开始复盘问题。

发现之前的方案有点问题:

  • 这次新增的推荐商品功能,保存到redis的数据量太大了,把有些为null值的字段,或者前端用不到的字段也保存到redis中了,数据结构设计不合理

  • redis出现问题之后的兜底方案有点问题,如果redis挂了,就直接访问了数据库,导致了用户访问慢的问题。如果是白天用户并发量上来,可能会直接导致数据库挂掉。

那么,如何优化呢?

四、如何优化?

数据结构不合理的问题,可以通过调整数据结构解决,非常容易。

但如果redis挂了该如何处理呢?

1.页面静态化

其实对于商城首页,最好的方案是做页面静态化处理。

但由于目前商城的用户并发量,还不算很大,而且如果改成页面静态化,前后端的改动都太大了。

因此,这个方案最先被我们否定了。

2.加本地缓存

为了防止后面再次出现商城首页访问慢的问题,可以在应用服务增加本地缓存。

这样不管redis以后能否正常运行,都不影响商城首页的功能。但需要考虑一个事情:应用服务的内存是否够用?

显然如果将所有推荐的商品数据,都保存到应用服务的本地内存中,同样可能会导致应用服务的内存不足的问题。

因此,直接加本地内存是不行的。

3.改成MongoDB

使用MongoDB替代Redis保存数据。

  • Redis:数据全部存在内存,定期写入磁盘,当内存不够时,可以选择指定的 LRU 算法删除数据。

  • MongoDB:数据存在内存,由 linux系统 mmap 实现,当内存不够时,只将热点数据放入内存,其他数据存在磁盘。

显然MongoDB更适合保存大批量的结构化的文档数据。由于我们之前在做其他功能时,使用过MongoDB,它的性能也是挺不错的。

但如果直接改成从MongoDB中获取数据,商城首页的访问速度可能会有所下降。

4.本地缓存 + MongoDB

上面说到过的加本地缓存,和使用MongoDB都有各自的优缺点。

为什么不把两种方案结合一下呢?在本地缓存中保存热点数据,每隔5分钟更新一次。

图片

用户的请求过来,先从本地缓存中获取推荐商品数据,如果有则直接返回。如果没有,则从MongoDB获取数据。

这样可以解决性能的问题,也可以解决保存大量的数据。

五、兜底方案

上面的说的本地缓存 + MongoDB,基本可以解决redis挂了的问题。

但如果MongoDB挂了该怎么办呢?这就需要有一套更好的兜底方案。

1.使用Apollo配置

如果MongoDB挂了,则直接返回Apollo配置中默认数据,默认是北京市东城区的推荐商品数据。该配置由于在Apollo中,我们可以根据实际情况动态调整。

我们都知道Apollo可以配置成集群模式,是高可用的,一般不容易挂掉。但它有一个硬伤,就是如果数据并更了,需要人手动调整数据,没法保证数据的实时性。

2.再从数据库访问数据

如果从MongoDB中获取数据失败了,则直接从数据库中获取数据。

该方案从业务的角度来说,确实没有问题。但万一真的出现这种情况,同样会出现商城首页访问很慢的问题。

3.再从redis访问数据

如果从MongoDB中获取数据失败了,则直接从redis中获取数据。Redis中只保留热点商品数据。

这也是一种方案,不过要维护两份数据:MongoDB一份,Redis一份。可能会存在数据不一致的问题。

4.再加一个本地缓存

在从数据库获取数据之后,再加一个本地缓存,保存默认的数据,即:北京市东城区的推荐商品数据。

这个本地缓存,只有在第一次访问数据库时写入,并且有效期是24小时。相当于在MongoDB和数据库之间,再加了一层默认的本地缓存。

这样就能解决数据库访问慢的问题。

六、最终方案

经过激烈讨论之后,我们最终选择的方案是:本地缓存+MongoDB+本地默认缓存+数据库。

图片

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK