面试题答案
一键面试缓存结构设计
- 按地区与商品ID进行缓存分区
- 以地区和商品ID作为缓存的复合键。例如,使用
{region}_{productId}
作为缓存键,这样不同地区的用户可以快速定位到自己对应的商品缓存数据。 - 优点是能快速满足不同地区用户对商品信息的请求,避免不同地区数据的相互干扰。
- 以地区和商品ID作为缓存的复合键。例如,使用
- 分层缓存
- 一级缓存(如内存缓存,如Redis):存储热门商品的详情页数据,包括基本信息、描述等相对稳定的数据,以及价格和库存的缓存版本号。这一层缓存用于快速响应大部分请求,因为内存缓存的读写速度极快。
- 二级缓存(如分布式文件系统或磁盘缓存):存储全量的商品详情页数据,包括价格和库存。当一级缓存未命中时,从二级缓存获取数据,并将数据加载到一级缓存中。二级缓存容量较大,可容纳更多商品数据。
- 缓存数据结构
- 对于商品详情页数据,采用JSON格式进行存储,这样可以方便地存储各种类型的数据,如字符串、数字、数组等。例如:
{ "productName": "示例商品", "description": "这是一个示例商品的描述", "price": 100, "stock": 1000, "cacheVersion": 1 }
- 其中
cacheVersion
用于标识价格和库存的版本,每次价格或库存更新时,该版本号递增。
更新策略设计
-
写后更新
- 当商品价格或库存发生变化时,首先更新数据库中的数据。
- 然后,根据变化的商品ID和地区,更新对应的缓存数据。更新缓存时,同时递增
cacheVersion
。例如,使用Redis的SET
命令更新缓存数据:
SET {region}_{productId} '{"productName": "示例商品", "description": "这是一个示例商品的描述", "price": 120, "stock": 990, "cacheVersion": 2}'
- 优点是操作相对简单,能保证数据库和缓存最终一致性。缺点是在更新数据库和更新缓存之间存在短暂的数据不一致。
-
读写锁机制
- 在更新商品价格或库存时,获取写锁。只有获取到写锁的请求才能进行更新操作。在更新数据库和缓存完成后,释放写锁。
- 读请求获取读锁,允许多个读请求同时获取读锁进行数据读取。当有写锁存在时,读请求等待写锁释放。
- 这种机制可以保证在更新数据时,不会有其他读请求读取到旧数据,从而保证数据的一致性。例如,在Java中可以使用
ReentrantReadWriteLock
实现读写锁。
-
缓存失效策略
- 设置短缓存过期时间:对于价格和库存数据,设置较短的过期时间,如1 - 5分钟。这样即使缓存更新出现问题,过期后也能从数据库重新加载最新数据。
- 基于版本号的失效:当客户端读取缓存数据时,检查
cacheVersion
。如果本地缓存的cacheVersion
与服务器缓存中的版本号不一致,说明数据已更新,需要重新获取数据。
-
异步更新缓存
- 使用消息队列(如Kafka)。当商品价格或库存变化时,将更新消息发送到消息队列。
- 有专门的消费者从消息队列中读取更新消息,并异步更新缓存。这样可以避免同步更新缓存带来的性能问题,同时保证缓存的最终一致性。