面试题答案
一键面试双写场景
- 先写MySQL,再写Redis
- 策略:业务逻辑先将数据写入MySQL,成功后再写入Redis。这样保证MySQL的数据是最新的,后续读请求从Redis获取数据也能得到最新值。
- 面临问题:如果写MySQL成功,但写Redis失败,会导致数据不一致。
- 解决方案:
- 重试机制:对写Redis操作进行重试,直到成功或达到最大重试次数。
- 异步补偿:将写Redis失败的操作记录到消息队列(如Kafka),由专门的消费者从队列中获取数据并再次尝试写入Redis。
- 先写Redis,再写MySQL
- 策略:先将数据写入Redis,成功后再写入MySQL。这种方式能快速响应写请求,因为Redis写操作速度快。
- 面临问题:若写Redis成功,写MySQL失败,会出现缓存与数据库不一致。同时,高并发下可能导致脏数据,比如A写操作先写Redis成功,此时B读操作从Redis读到脏数据,然后A写MySQL失败。
- 解决方案:
- 事务机制:可以借助Redis的Lua脚本实现简单事务,确保多个写操作的原子性。对于MySQL操作失败的情况,回滚Redis写操作(但Redis本身没有类似数据库完整的回滚机制,需要业务层模拟实现)。
- 缓存失效机制:写MySQL失败时,设置Redis缓存的过期时间,让数据在短时间内失效,后续读请求从MySQL加载最新数据并重新写入Redis。
读写分离场景
- 读Redis,写MySQL(MySQL写成功后更新Redis)
- 策略:读请求先从Redis读取数据,写请求直接写入MySQL,MySQL写成功后更新Redis。这种架构适合读多写少的场景,充分利用Redis的高并发读性能。
- 面临问题:
- 缓存击穿:大量请求同时查询一个在Redis中过期但MySQL中存在的数据,瞬间大量请求打到MySQL,可能压垮数据库。
- 缓存雪崩:大量缓存同时过期,导致大量请求直接访问MySQL,造成数据库压力剧增。
- 缓存更新不及时:MySQL数据更新后,Redis缓存更新存在延迟,期间读请求读到的是旧数据。
- 解决方案:
- 缓存击穿:
- 互斥锁:在查询Redis发现数据过期时,先获取互斥锁(如使用Redis的SETNX命令),只有获取到锁的请求才能去MySQL查询数据并更新Redis,其他请求等待,锁释放后再次从Redis获取数据。
- 热点数据永不过期:对于热点数据不设置过期时间,定期由后台任务更新Redis缓存。
- 缓存雪崩:
- 随机过期时间:为不同的缓存设置不同的过期时间,避免大量缓存同时过期。
- 二级缓存:设置一级缓存(如Redis)和二级缓存(如Memcached),一级缓存失效后从二级缓存读取,减少对MySQL的直接访问。
- 缓存更新不及时:
- 异步更新:MySQL写操作成功后,通过消息队列(如RabbitMQ)异步通知更新Redis缓存,减少主业务流程的延迟。
- 读写锁:对于读多写少的场景,可以使用读写锁。写操作获取写锁,读操作获取读锁,写锁优先级高于读锁,确保写操作时读操作等待,写操作完成后更新Redis并释放锁,读操作再继续执行。
- 缓存击穿: