面试题答案
一键面试缓存Key设计整体思路
- 模块区分:在大型电商平台中,不同模块功能不同,数据特征也不同。所以首先要根据模块进行区分,如商品展示模块的Key可统一以“product_”为前缀,购物车模块以“cart_”为前缀,订单处理模块以“order_”为前缀,库存管理模块以“stock_”为前缀。这样能快速定位数据所属模块,便于管理和维护。
- 数据唯一标识:每个缓存数据都应通过唯一标识来区分。例如商品展示模块,除了前缀“product_”,还需加上商品ID,形成类似“product_12345”(12345为商品ID)的Key,确保缓存的商品数据唯一对应特定商品。
- 考虑依赖关系:由于模块间存在数据关联和依赖,缓存Key设计要能体现这种关系。例如订单中的商品信息依赖商品展示模块的商品数据,在订单缓存Key中可以体现商品ID等信息,如“order_6789_product_12345”(6789为订单ID,12345为商品ID),便于在订单相关操作时快速获取依赖的商品数据。
具体方法
- 分层设计:可以采用分层设计Key。以商品展示模块为例,第一层为模块前缀“product_”,第二层可以是商品分类ID,第三层是商品ID,形成如“product_100_12345”(100为商品分类ID,12345为商品ID)。这种分层结构便于对商品数据进行分类管理和批量操作。
- 参数化设计:对于一些需要根据不同参数缓存数据的场景,将参数融入Key。比如商品搜索结果缓存,Key可以设计为“product_search_关键词_页码_每页数量”,如“product_search_手机_1_10”,这样能精确缓存不同搜索条件下的结果。
确保缓存数据一致性、可用性和高性能
- 一致性
- 数据更新策略:当数据发生变化时,采用“写后失效”策略。例如商品价格更新后,立即失效对应的商品缓存Key,下次请求时重新从数据库加载最新数据并缓存。对于跨模块依赖的数据,如订单中的商品信息,在商品数据更新时,除了失效商品缓存Key,还需失效相关订单缓存Key。
- 双写模式:对于一些对一致性要求极高的场景,可以采用双写模式,即数据更新时同时更新数据库和缓存,但要注意处理并发问题,防止数据不一致。
- 可用性
- 缓存集群:采用分布式缓存集群,如Redis Cluster,提高缓存的可用性和扩展性。通过数据分片将数据分布在不同节点上,避免单点故障。
- 故障转移:配置好缓存节点的故障转移机制,如Redis Sentinel,当主节点出现故障时,能自动将从节点提升为主节点,保证服务的连续性。
- 高性能
- 合理设置过期时间:对于不经常变化的数据,设置较长的过期时间,减少数据库读取压力。对于变化频繁的数据,设置较短过期时间,确保数据的及时性。例如商品库存变化频繁,缓存过期时间可设置为几分钟;而商品基本信息(如名称、描述)变化较少,过期时间可设置为几小时甚至一天。
- 缓存预热:在系统启动时,预先加载一些热门数据到缓存中,避免用户首次访问时的冷启动问题,提高响应速度。
结合实际案例解决缓存穿透、缓存雪崩问题
- 缓存穿透
- 缓存Key设计辅助解决:在缓存Key设计中,可以加入一些验证信息。例如对于商品查询,除了商品ID,还可以在Key中加入用户ID(如果查询权限与用户有关)。当恶意用户使用不存在的商品ID进行大量查询时,由于Key中包含用户ID,即使商品ID不存在,不同用户的查询也会有不同的Key,不会集中穿透到数据库。同时,可以在缓存中设置一个特殊的Key表示“不存在的数据”,如“product_not_exist_12345”,当查询不存在的商品ID时,直接返回缓存中的这个标识,避免查询数据库。
- 实际案例:某电商平台曾遭受恶意攻击,攻击者利用不存在的商品ID进行大量查询,导致数据库压力剧增。通过在缓存Key中加入用户ID和设置“不存在数据”的缓存标识,成功拦截了大部分无效查询,减轻了数据库压力。
- 缓存雪崩
- 缓存Key设计辅助解决:在设计缓存Key的过期时间时,避免大量Key在同一时间过期。可以采用随机过期时间,例如商品缓存过期时间设置在1 - 2小时的随机值。这样即使有大量商品缓存,也不会在同一时刻过期,从而避免缓存雪崩。另外,可以将不同模块的缓存Key设置不同的过期时间范围,如商品展示模块缓存过期时间为1 - 3小时,订单处理模块缓存过期时间为30分钟 - 1小时,进一步分散过期时间。
- 实际案例:某电商大促活动后,由于大量商品缓存设置了相同的过期时间,导致缓存过期后瞬间大量请求涌向数据库,造成数据库压力过大甚至短暂崩溃。通过设置随机过期时间和区分模块过期时间范围,成功避免了类似问题再次发生。