1 问题背景
需要在商品缓存中新增2个字段,redis中已存在20万个商品缓存,如何更新这20万个商品缓存
2 前言
本博客是笔者在真实的生产环境遇到的问题,非自学的demo或者单纯地研究理论。本博客仅供笔者自己总结参考,如有不正确的地方请指出。
3 详细背景
假如在redis中有如下结构的商品缓存:
{
id: 1,
title: "Iphone",
skus: [
{
id: 1001,
price: 6999.00,
sale_price: 5999.00
}
]
}
解释:此缓存结构表示一个spu,它有1个sku。
现在要在sku层级新增两个字段:
weight
重量、weight_unit
重量单位,结构如下所示:
{
id: 1,
title: "Iphone",
skus: [
{
id: 1001,
price: 6999.00,
sale_price: 5999.00
weight: 1.20,
weight_unit: "kg"
}
]
}
往商品的缓存中加了字段,如果仍使用旧的缓存结构,那么查缓存时查出来新增的两个字段将会是null,会存在一定的问题(业务方面的问题,在此不做详细阐述),因此我们要在查询的时候,要获取最新的缓存结构,即要去db查并将值更新到缓存,就是要做缓存的更新。
生产环境上,不能直接粗暴地全部直接删掉。如果直接通过redis桌面管理软件进行删除操作,会删不干净,同时也会造成Redis阻塞。如果写代码去扫描数据库组装商品缓存的key,这样效率太低,而且也不可能组装全量商品缓存的key(因为这是要扫描整个商品库)。20万的商品缓存怎么更新呢?
4 解决方案
4.1 方案1
核心思想:设计一个字段标识查出来的商品缓存是否是最新的结构,此处假设用字段
version
标识商品缓存的版本,假设最新的版本为1,即version
的值为1。
- 查商品缓存
- 判断商品缓存中的
version
字段是否为1,若是为空或者不为1,则表示此缓存不是最新的,需要去db查最新值并写回到缓存中。如下面代码所示:
{
version: 1,
id: 1,
title: "Iphone",
skus: [
{
id: 1001,
price: 6999.00,
sale_price: 5999.00
weight: 1.20,
weight_unit: "kg"
}
]
}
- 考虑到如果要动态控制
version
的值,可以将version
的值放到配置中心中(比如nacos配置中心、apollo),那么无需改代码发版即可热更新version
的值。
缺点: 需要加入逻辑代码判断缓存中是否有
version
字段或者判断其值是否为1。如何解决这样的问题?请看下面的方案2
4.2 方案2
核心思想: 在缓存的key中加上版本号
version
的值,形成新的redis key。获取缓存的时候直接使用带版本号version
的key。
-
查询商品缓存的key加上
version
值,例子:redis的key为product_info:{shopId}:{version}
,其中{shopId}
表示店铺ID的值,{version}
表示商品缓存版本的值 -
可以考虑将
version
值放到配置中心,让系统可以热更新version
的值
缺点: 需要占用大量的服务器内存,思想是用空间换性能。如何解决这种问题,请看上面的方案1
4.3 方案3
方案3是对方案1或方案2的补全。
- 基于方案1或者方案2,可以考虑做一个缓存预热。针对近半个月或者近一个星期(这个量取决于有多少商品缓存,笔者接触的生产环境有20w,查了下单的商品,查近半个月有1万个下单的商品,查近一个星期只有1千个下单的商品,所以采用近半个月)的下单商品进行商品缓存预热。
- 商品缓存的过期时间可以采用随机散列,比如1-7天,防止雪崩。