面试题答案
一键面试系统架构设计
- 客户端:发起历史数据查询请求。
- 应用服务器:接收请求,首先检查Redis缓存中是否有对应数据。如果有,直接返回;若没有,则查询MySQL数据库,将查询结果存入Redis缓存后再返回给客户端。
- Redis缓存:存储经常查询的历史数据,减轻MySQL压力。
- MySQL数据库:作为历史数据的持久化存储。
挑战及解决方案
数据同步
- 挑战:MySQL数据更新后,如何保证Redis缓存数据也及时更新,避免数据不一致。
- 解决方案:
- 双写模式:在更新MySQL数据时,同时更新Redis缓存。但要注意更新顺序,先更新MySQL再更新Redis,防止并发情况下出现缓存和数据库不一致。
- 异步消息队列:使用消息队列(如Kafka),当MySQL数据更新后,发送一条消息到队列,由消费者从队列中获取消息并更新Redis缓存。这样可以解耦数据库和缓存的更新操作,提高系统的稳定性和性能。
缓存雪崩
- 挑战:大量缓存数据在同一时间过期,导致大量请求直接打到MySQL数据库,可能使数据库崩溃。
- 解决方案:
- 设置不同过期时间:对缓存数据设置不同的过期时间,避免集中过期。例如,可以在原本设置的过期时间基础上,加上一个随机的时间偏移量。
- 使用互斥锁:当缓存过期后,只有一个请求能获取到互斥锁去查询数据库并更新缓存,其他请求等待。这样可以防止大量请求同时查询数据库。
- 持久化缓存:部分关键数据设置为永不过期,或者使用Redis的持久化机制(RDB、AOF),即使Redis重启也能快速恢复数据。
缓存穿透
- 挑战:查询不存在的数据,每次都绕过缓存直接查询数据库,可能导致数据库压力过大甚至被拖垮。
- 解决方案:
- 布隆过滤器:在查询前使用布隆过滤器判断数据是否存在。如果布隆过滤器判断不存在,则直接返回,不再查询数据库。布隆过滤器可以显著减少对不存在数据的查询请求。
- 空值缓存:当查询数据库发现数据不存在时,也将这个空值缓存起来,并设置一个较短的过期时间,这样后续相同的查询就可以直接从缓存中获取空值,避免查询数据库。