面试题答案
一键面试架构设计
- 读写分离架构
- 读操作:在高并发读场景下,优先从Redis缓存读取数据。应用程序发起读请求时,首先查询Redis。若命中缓存,直接返回数据,大大减轻MySQL压力。例如,对于商品详情页数据,先从Redis中查找商品信息。
- 写操作:写操作则先更新MySQL,成功后再更新Redis缓存。为保证数据一致性,可采用事务机制确保MySQL更新和Redis更新要么都成功,要么都失败。比如用户修改个人资料,先在MySQL中更新,成功后同步更新Redis中的用户缓存数据。
- 多级缓存架构
- 本地缓存:在应用服务器端设置本地缓存(如Guava Cache)。对于一些高频访问且不经常变化的数据,先从本地缓存读取。若本地缓存未命中,再查询Redis。这样可以减少对Redis的压力,提高响应速度。例如,一些系统配置参数可放在本地缓存中。
- 分布式缓存(Redis):作为一级缓存的补充,存储大部分热点数据。对于本地缓存未命中的数据,从Redis读取。同时,当数据发生变化时,需要及时更新本地缓存和Redis缓存。
- 缓存集群
- 主从复制:使用Redis主从复制模式,主节点负责写操作,从节点负责读操作。这样可以提高读性能,并且在主节点出现故障时,从节点可以提升为主节点继续提供服务。例如,设置一个主节点和多个从节点,主节点处理写请求并将数据同步到从节点。
- 哨兵模式:结合哨兵机制,对Redis主从节点进行监控。当主节点出现故障时,哨兵可以自动选举新的主节点,保证系统的高可用性。
- 集群模式:对于大规模数据和高并发场景,采用Redis Cluster模式。它将数据分布在多个节点上,每个节点负责一部分数据,提高了存储能力和并发处理能力。
缓存预热
- 数据预加载
- 定时任务:在系统启动前或业务低峰期,通过定时任务从MySQL中批量读取热点数据,加载到Redis缓存中。例如,每天凌晨2点,从MySQL读取前一天访问量最高的1000条商品数据,插入到Redis缓存。
- 初始化脚本:在应用程序启动时,执行初始化脚本,将一些基础数据(如系统字典数据)加载到Redis。例如,加载地区字典、商品分类等数据到Redis。
- 预热策略
- 热门数据优先:根据历史数据和业务分析,确定热门数据,优先进行缓存预热。比如电商平台中销量高的商品、搜索频率高的关键词等相关数据。
- 逐步预热:对于大量数据,采用逐步预热的方式,避免一次性加载过多数据导致系统资源耗尽。可以分批次加载数据到Redis,每次加载一定数量的数据。
缓存失效处理
- 缓存穿透
- 布隆过滤器:在查询Redis前,先通过布隆过滤器判断数据是否存在。布隆过滤器可以快速判断一个元素是否在集合中,几乎不会出现误判(有极小概率的误判)。如果布隆过滤器判断数据不存在,直接返回,不再查询MySQL,从而避免大量无效请求穿透到MySQL。例如,对于用户ID查询,如果布隆过滤器中不存在该ID,则直接返回无数据。
- 空值缓存:当查询MySQL发现数据不存在时,将空值(如null)也缓存到Redis中,并设置较短的过期时间。这样后续相同的无效请求直接从Redis获取空值,不再查询MySQL。
- 缓存雪崩
- 随机过期时间:为不同的缓存数据设置随机的过期时间,避免大量缓存同时过期。例如,原本设置缓存过期时间为1小时,可以改为在50分钟到70分钟之间随机设置过期时间。
- 二级缓存兜底:当一级缓存(Redis)出现大面积失效时,启用二级缓存(如本地缓存)暂时提供服务,同时尽快恢复Redis缓存数据。
- 缓存击穿
- 互斥锁:在查询MySQL数据前,先获取一个互斥锁。只有获取到锁的请求才能查询MySQL并更新缓存,其他请求等待。这样可以避免高并发下大量请求同时查询MySQL。例如,使用Redis的SETNX命令获取互斥锁,处理完数据后释放锁。
- 永不过期:对于一些热点数据,设置永不过期,同时在后台定时更新缓存数据,确保数据的实时性。
性能调优
- Redis配置优化
- 合理设置内存:根据服务器内存情况,合理分配Redis可用内存。通过配置文件(如redis.conf)中的
maxmemory
参数设置最大内存。同时,选择合适的内存淘汰策略,如allkeys - lru
(在所有键中使用LRU算法淘汰数据),确保在内存不足时淘汰不常用的数据。 - 调整线程数:对于多核CPU服务器,可以适当增加Redis的线程数,提高并发处理能力。在Redis 6.0及以上版本支持多线程,通过配置文件中的
io - threads
参数设置线程数。
- 合理设置内存:根据服务器内存情况,合理分配Redis可用内存。通过配置文件(如redis.conf)中的
- 网络优化
- 减少网络延迟:确保Redis服务器与应用服务器网络畅通,尽量部署在同一机房或相近的网络环境中。同时,优化网络带宽,避免网络拥塞。
- 连接池优化:在应用程序中使用连接池管理与Redis的连接,减少连接创建和销毁的开销。合理设置连接池的最大连接数、最小空闲连接数等参数,如使用Jedis连接池时,通过
JedisPoolConfig
类进行配置。
- 数据结构优化
- 选择合适的数据结构:根据业务场景选择合适的Redis数据结构。例如,对于列表数据可使用Redis的List结构,对于哈希类型数据可使用Hash结构。对于计数器场景,使用Redis的Incr命令结合String结构实现高效计数。
- 数据压缩:对于一些较大的缓存数据,可以考虑进行压缩后再存储到Redis。例如,使用Snappy、LZ4等压缩算法对数据进行压缩,减少存储空间和网络传输量。