缓存的不当使用案例分析

身不由己

一、背景

      最近一朋友做社区重构,社区主要功能有发帖、回帖、查看帖子详情,详情页按不同条件展示回帖(除了预先定义的顺序外,可能每个用户看到的顺序都不一样,组合超过100个),大概的效果如下:

以前用的是开源的代码,存储用的是Mysql,系统也过于臃肿,稍微有点流量系统响应就很慢,所以准备重构。

重构后的方案如下

1、存储还是Mysql;

2、为了提高访问速度,引入MongoDB作为缓存(为什么不用Redis,因为MongoDB多线程,可扩充性好,并且支持较复杂的查询)

Mysql数据表大概如下:
image.png

    上述表格是经过简化版的内容。

    存储方面,Mysql存了全量的帖子和帖子回复,MongoDB也存了全量的帖子和帖子回复,之所以这么设计是因为让用户帖子详情页不用访问数据库,提高访问速度。

     那为什么只保存在MongoDB里呢,因为MongoDB不支持多表事务,社区的场景插入回复,还有其它逻辑需要处理,所以需要借助Mysql的InnoDB的事务机制保证数据的一致性。

    重构后访问帖子详情页顺序如下:

    1、根据帖子id从MongoDB获取帖子详情信息,包括标题、内容及发帖时间和发帖人,如果读取不到,直接报错;

    2、根据帖子id及当前条件从MongoDB获取帖子回复信息,同样读取不到也报错。

     为什么不按分页将每个帖子按页缓存回复呢,因为前面说了整个详情页展示条件非常复杂,可以倒序排,也可升序排,还可以只看作者,有的回复还有权限,如果全部缓存帖子回复列表,则缓存的数据量非常的大。

二、问题分析

      经过分析,这样的设计带来几个问题:

     1、系统设计比较复杂,因为要保证数据在Mysql、MongoDB中一致,需要做很多的代码进行数据核对、检查;

     2、系统可用性差,因为帖子详情页全部读取的是MongoDB,所以如果MongoDB挂了,则整个系统也就挂了,特别是MongoDB对于运维团队还不是特别熟悉的情况下。

    有什么更好的方案呢,回到缓存的本质,关于缓存的使用有不少模式,一般来说对缓存不要强依赖,即缓存挂了,整个系统不要挂,让系统打到后端存储并且更新缓存,这样还有最后一道防线,而在这个案例中,将MongoDB当存储用了,并且同时使用两个存储。

     如果当缓存用,怎么解决帖子详情页多种组合条件的导致缓存数据太大的问题?其实对于社区这样的场景,主要占内存的是回复的内容,只要保证帖子回复内容只缓存一份就可以了。

    改进后帖子详情页逻辑如下:

    1、根据帖子id从 MongoDB中获取帖子详情信息,如果获取不到,则从Mysql中获取,并且写回到MongoDB中;

    2、根据帖子id从MongoDB中获取当页需要展示的帖子回复id,读取不到再从Mysql回源,并写回到MongoDB中;根据上面获取的回复id再从MongoDB中获取回复的详情,同样如果获取不到则从Mysql回源,并且写入到MongoDB中。

    当然在添加、更新回复后,也需要更新相应的回复内容,这样就保证了帖子回复只缓存一份,不会造成缓存的数据量过大的问题。还有非常重要的一点,整个系统没有对缓存强依赖,即使MongoDB挂了,系统还会从Mysql读取数据。最后系统的代码也变得非常简洁。

    当然这里还有很多细节需要注意,像如何避免同一时间大量的回源Mysql的问题,这些业内已经有标准的方案,就不在此展开讨论了。

三、案例总结

1、系统设计越简单越好;

2、不要强依赖缓存;

广告联盟设计踩坑

从一次线上故障来看redis删除机制

全球智能DNS解析实践

阅读 568
18 声望
2 粉丝
0 条评论
18 声望
2 粉丝
宣传栏