面试题答案
一键面试整体架构设计
- 读写流程
- 读操作:应用程序首先尝试从 Redis 缓存中读取数据。如果缓存命中,直接返回数据;若缓存未命中,则从 MySQL 数据库中查询数据,将查询结果写入 Redis 缓存(设置合适的过期时间),然后返回给应用程序。
- 写操作:先更新 MySQL 数据库,成功后再删除 Redis 缓存。这样确保下次读操作时能从数据库加载最新数据到缓存。
- 缓存分层
- 可以采用多级缓存,如本地缓存(如 Guava Cache) + Redis 分布式缓存。本地缓存用于存储访问频率极高且数据相对稳定的数据,减少对 Redis 和数据库的压力。
优化策略及对应分析
- 数据一致性
- 策略:采用先更新数据库,再删除缓存的方式。为了应对高并发场景下可能出现的问题,可引入延迟双删策略。即更新数据库后,先删除缓存,稍作延迟(如 500ms)后再次删除缓存,以确保可能在缓存删除前读取旧数据并写入缓存的操作失效。
- 优点:在大多数情况下能保证数据的最终一致性,实现相对简单。
- 缺点:延迟双删策略增加了代码复杂度和操作时间,并且延迟时间较难精准控制,设置过短可能无法解决问题,过长则影响系统性能。
- 缓存穿透
- 策略:
- 布隆过滤器:在查询数据前,先通过布隆过滤器判断数据是否存在。布隆过滤器是一种概率型数据结构,将所有可能查询的键值对提前存入布隆过滤器。如果布隆过滤器判断数据不存在,则直接返回,不再查询数据库。
- 空值缓存:当查询数据库未命中时,将空值也存入 Redis 缓存(设置较短的过期时间),下次查询相同数据时,直接从缓存返回空值,避免再次查询数据库。
- 优点:布隆过滤器能有效减少数据库压力,对于不存在的数据查询直接拦截;空值缓存实现简单,能在一定程度上防止缓存穿透。
- 缺点:布隆过滤器存在误判率,可能将不存在的数据误判为存在;空值缓存占用一定的缓存空间,并且过期时间设置不合理可能导致短暂的缓存穿透。
- 策略:
- 缓存雪崩
- 策略:
- 均匀设置过期时间:避免大量缓存数据在同一时间过期,将缓存的过期时间设置为一个随机值,在一定时间范围内(如原过期时间的 80% - 120%)波动。
- 缓存降级:当出现大量缓存失效,系统压力过大时,采用缓存降级策略,即部分非核心业务直接返回默认值或提示信息,保证核心业务的正常运行。
- 构建多级缓存:如前面提到的本地缓存 + Redis 分布式缓存,本地缓存可以在 Redis 出现问题时,提供一定的数据缓冲。
- 优点:均匀设置过期时间简单有效,能分散缓存过期压力;缓存降级能确保核心业务可用;多级缓存增加了系统的容错能力。
- 缺点:均匀设置过期时间不能完全避免缓存雪崩的极端情况;缓存降级影响部分业务体验;多级缓存增加了系统复杂度和维护成本。
- 策略:
- 读写性能
- 读性能优化:
- 缓存预热:系统启动时,提前将热点数据加载到 Redis 缓存中,避免大量冷数据查询导致缓存未命中。
- 批量查询:对于需要多次查询相同类型数据的场景,采用批量查询方式,减少与 Redis 和数据库的交互次数。
- 优化 Redis 配置:如合理设置 Redis 的内存、线程数、持久化策略等,提高 Redis 的读写性能。
- 写性能优化:
- 异步写缓存:将写缓存操作异步化,如使用消息队列(如 Kafka),应用程序在更新数据库后,将删除缓存的操作发送到消息队列,由消息队列异步处理,减少写操作的响应时间。
- 批量写操作:对于批量数据更新,将多个写操作合并为一个,减少数据库和缓存的操作次数。
- 优点:缓存预热提高系统初始响应速度;批量查询和写操作减少交互次数,提升性能;异步写缓存减少响应时间。
- 缺点:缓存预热需要提前了解热点数据,可能存在不准确的情况;异步写缓存增加了系统的复杂性和数据一致性的管理难度。
- 读性能优化: