商品列表性能提升尝试,先放Redis里存着看看效果怎么样
- 问答
- 2025-12-26 08:24:50
- 3
这个想法其实挺直接的,就是感觉商品列表页面每次打开都去数据库里查,有点慢,尤其是做活动的时候,人一多,数据库就有点吃不消,所以就想,能不能找个办法让它快一点,听别人说Redis这东西特别快,是放在内存里的,就想着先把商品数据放Redis里试试看,看效果怎么样。
最开始的时候,没什么经验,就想得特别简单,心想着,不就是把数据从数据库搬到Redis里嘛,当时商品列表主要就是展示商品ID、名称、图片、价格这些基本信息,我就打算在后台更新商品信息的时候,除了更新数据库,再顺便把同样的数据转成JSON格式,然后以一个固定的键,mall:product:list”,存到Redis里,设置一个过期时间,比如一小时,然后前端请求商品列表的时候,程序就不去查数据库了,先跑去Redis查这个键,如果有数据,就直接把JSON数据返回给前端;如果Redis里没有(比如过期了或者第一次启动),那就再去数据库查,查完再塞回Redis里,同时返回给前端。
这么弄完之后,自己测试了一下,在没什么人访问的时候,感觉是快了一点,但好像也没快到飞起,不过心里还是挺期待的,觉得人多了效果应该就明显了,结果等到做一个小促销活动的时候,问题就来了,先是发现商品列表显示的价格不对,有个别商品明明已经打折降价了,但页面上显示的却还是原价,赶紧查原因,发现是后台管理员修改了商品价格,数据库里的数据已经更新了,但Redis里缓存的那个旧数据还没到期,所以用户看到的还是缓存里的老价格,这就尴尬了,只能手动去Redis里把那个缓存键删掉,让系统重新从数据库加载,这肯定不是长久之计。
然后还发现另一个问题,就是商品的上架和下架,有次下架了一个问题商品,但因为它还在Redis缓存列表里,导致用户依然能看到并能点击,虽然点进去会提示商品不存在,但这个体验非常差,这说明我这种“一把嗦”把所有商品存成一个列表的方式太粗糙了,数据更新不及时,缓存和数据库很容易不一致。

看来简单的办法不行,得想复杂一点的法子,不能把所有商品都混在一起缓存了,我们商品列表通常是有分类的,手机数码”、“家用电器”,还有排序,按价格从低到高”、“按销量”,如果还用一个键存所有商品,那只要任何一个商品信息有变动,或者排序方式一变,整个大列表缓存都得失效,这缓存命中率太低了,基本没啥用。
所以就得换思路,不能缓存整个渲染好的列表页面,也不能缓存那个巨大的包含所有商品的列表数据,应该更灵活一点,可以尝试缓存单个商品的信息,给每个商品在Redis里单独存一份,键可以设计成“mall:product:info:[商品ID]”,里面存这个商品的详细信息(JSON格式),这样,当后台修改了某个商品的价格时,在更新数据库的同时,也更新一下Redis里这个商品对应的缓存,这样,即使用户从不同的列表入口(比如不同分类、不同排序)点进来,最终展示商品基本信息时,都是从Redis里读取的单个商品缓存,能保证看到的是最新信息。

那列表本身怎么办呢?列表其实就是一堆商品ID的集合,加上排序规则,对于那种不怎么变动的分类列表,手机数码”分类下的商品,除非有新品上架或旧品下架,否则这个商品ID的集合是相对固定的,可以缓存这个商品ID的列表,键可以像“mall:product:cate:[分类ID]:sort_by_time”,前端请求这个分类按时间排序的列表时,后台程序先看Redis里有没有这个ID列表,有的话,就直接取出这一串ID,然后再根据这些ID,去Redis里批量获取(比如用MGET命令)每个商品的详细信息,最后组装成列表数据返回给前端,如果某个商品的详细信息在Redis里没找到(可能因为过期),再单独去数据库查一下,并补回到Redis里。
这样做的好处是,粒度更细了,更新一个商品,只会让这个商品自己的缓存失效,不会影响到整个大列表的缓存,只有当你对分类下的商品进行增删(上下架)时,才需要让对应的分类商品ID列表缓存失效,这样缓存的有效期可以设得更长一些,利用率更高。
这比最开始那个“一把嗦”的方案要复杂不少,写代码的逻辑也多了,需要维护两种缓存:商品ID列表的缓存和单个商品详情的缓存,而且还要处理批量获取商品详情时,可能部分商品缓存缺失的情况,但为了性能和数据的准确性,看来这个复杂度是省不掉的了。
先按照这个思路去改改看吧,看看效果会不会好一些,至少得解决那个价格显示不及时和已下架商品还能看见的问题,如果这个方案还不行,可能还得再想别的招,比如是不是缓存整个页面片段,或者用更高级的缓存策略,先用Redis试试水,走一步看一步。
本文由黎家于2025-12-26发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://waw.haoid.cn/wenda/68680.html
