面试题答案
一键面试设计思路
- 读写顺序控制
- 写操作:先写MySQL,成功后再更新Redis。这样能确保数据的持久化,以MySQL的数据为准。例如,一个商品库存数据的更新,先在MySQL中修改库存数量,然后同步更新Redis中的缓存数据。
- 读操作:先读Redis,如果Redis中有数据,直接返回;若没有,则从MySQL读取,并将数据写入Redis,设置合适的过期时间。比如用户信息的读取,优先从Redis获取,若缓存未命中,从MySQL加载并缓存。
- 使用队列
- 引入消息队列(如Kafka、RabbitMQ)。写MySQL成功后,将更新Redis的操作封装成消息发送到队列中。由专门的消费者从队列中消费消息来更新Redis,这样可以异步处理,减少主业务流程的性能损耗。例如,在电商订单创建后,写MySQL记录订单信息成功,接着将更新商品库存缓存的消息发送到队列。
- 缓存过期策略
- 对Redis中的缓存数据设置合理的过期时间。对于不常变化的数据,设置较长的过期时间;对于变化频繁的数据,设置较短的过期时间。比如商品的基本信息可以设置较长过期时间,而实时的商品销量数据设置较短过期时间。
可能遇到的问题及解决方案
- 缓存雪崩
- 问题:大量缓存同时过期,导致大量请求直接打到MySQL,可能使MySQL压力过大甚至崩溃。
- 解决方案:
- 随机过期时间:在设置缓存过期时间时,采用随机值,避免所有缓存同时过期。例如,原本设置过期时间为1小时,可以设置为50 - 70分钟之间的随机值。
- 热点数据永不过期:对于一些热点数据,如首页展示的热门商品信息,不设置过期时间,定期主动更新缓存。
- 缓存穿透
- 问题:查询不存在的数据,每次都绕过Redis直接查询MySQL,导致MySQL压力增大。
- 解决方案:
- 布隆过滤器:在查询前,先通过布隆过滤器判断数据是否存在。如果布隆过滤器判断不存在,则直接返回,不再查询MySQL。例如,在用户查询商品时,先通过布隆过滤器判断该商品ID是否存在。
- 空值缓存:查询MySQL后,如果数据不存在,也将空值缓存到Redis中,并设置较短的过期时间,避免下次重复查询MySQL。
- 双写一致性问题
- 问题:在高并发情况下,写MySQL和更新Redis的操作可能会出现不一致,例如先写MySQL成功,更新Redis时失败,或者在更新Redis之前另一个读操作读取到了旧的缓存数据。
- 解决方案:
- 重试机制:更新Redis失败时,设置重试策略,多次尝试更新。例如,可以使用Guava的Retryer框架进行重试。
- 事务机制:如果数据库支持,可以使用数据库事务来保证写MySQL和更新Redis的原子性。但这种方式可能会影响性能,需谨慎使用。
- 读写锁:对涉及数据读写的操作加锁,保证同一时间只有一个写操作或者多个读操作,避免并发写带来的一致性问题。但加锁会降低系统的并发性能,所以要合理控制锁的粒度和范围。