面试题答案
一键面试缓存架构设计
缓存层次结构
- 浏览器缓存:作为第一层缓存,浏览器可缓存静态资源如图片、CSS、JavaScript 文件等。通过设置合适的缓存头(如 Cache - Control、Expires 等)来控制缓存时长。这一层缓存可极大减少用户重复请求相同资源,降低服务器压力。
- CDN 缓存:内容分发网络(CDN)分布在全球各地的边缘节点缓存内容。对于静态资源以及部分动态页面片段,CDN 可以根据用户地理位置就近提供缓存内容。CDN 缓存命中率高,能够有效减轻源站压力,提高用户访问速度。
- 应用层缓存:在应用服务器内部设置缓存,如使用 Memcached 或 Redis。这一层缓存主要缓存频繁访问且不经常变化的数据,例如热门搜索结果、用户配置信息等。应用层缓存直接与应用程序交互,响应速度快。
- 数据库缓存:数据库层面也可设置缓存,如 MySQL 的查询缓存(虽在高并发写入场景下存在问题,但某些特定场景仍有用),以及一些数据库的内置缓存机制。它主要缓存数据库查询结果,减少数据库的直接查询次数。
数据分区策略
- 哈希分区:通过对数据的某个唯一标识(如文档 ID、用户 ID 等)进行哈希计算,将数据均匀分配到不同的缓存节点上。例如,使用一致性哈希算法,它能在节点增减时尽量减少数据的迁移。哈希分区的优点是数据分布均匀,可扩展性强;缺点是无法根据数据特性进行灵活分区。
- 范围分区:按照数据的某个范围(如时间范围、ID 范围等)进行分区。例如,按日期将搜索日志数据分区缓存,新的数据存放在新的分区。这种策略适用于按时间序列访问的数据,便于数据的管理和维护,但可能导致节点负载不均衡。
- 复合分区:结合哈希分区和范围分区,先按范围分区,再在每个范围内进行哈希分区。这样既能利用范围分区的管理优势,又能保证数据分布均匀。
缓存更新机制
- 写后更新(Write - Behind):在数据更新时,先更新数据库,然后异步更新缓存。这种方式优点是对应用程序性能影响小,适合高并发写入场景;缺点是可能存在缓存与数据库短暂不一致的情况。为解决一致性问题,可设置合理的缓存过期时间,或采用一些补偿机制,如在更新数据库后,延迟一定时间再更新缓存。
- 写前更新(Write - Through):在数据更新时,先更新缓存,再更新数据库。优点是能保证缓存与数据库一致性,但在高并发时,可能因数据库写入性能问题影响整体性能。可通过批量写入数据库、使用缓存队列等方式减轻数据库压力。
- 失效策略(Invalidation):当数据发生变化时,直接使相关缓存失效。下次访问时,缓存未命中,重新从数据库加载数据并更新缓存。这种方式简单易实现,但可能导致缓存命中率下降,尤其在频繁更新数据场景下。可通过合理设置缓存粒度和预加载部分数据来缓解。
可能面临的挑战及解决方案
缓存穿透
- 挑战:恶意请求查询不存在的数据,每次都穿透缓存直接查询数据库,导致数据库压力增大。
- 解决方案:
- 布隆过滤器:在缓存之前设置布隆过滤器,先判断数据是否存在。如果布隆过滤器判断不存在,则直接返回,不再查询数据库。
- 空值缓存:对于查询不存在的数据,也将其缓存起来,设置较短的过期时间,避免后续重复查询数据库。
缓存雪崩
- 挑战:大量缓存数据在同一时间过期,导致大量请求直接查询数据库,可能使数据库因压力过大而崩溃。
- 解决方案:
- 随机过期时间:为缓存设置不同的过期时间,避免集中过期。例如,在原本的过期时间基础上,加上一个随机的时间偏移量。
- 二级缓存:设置主缓存和备用缓存,主缓存过期后,先从备用缓存获取数据,同时更新主缓存,减少对数据库的冲击。
缓存击穿
- 挑战:热点数据缓存过期瞬间,大量请求同时查询该数据,直接穿透到数据库,可能压垮数据库。
- 解决方案:
- 互斥锁:在缓存过期时,只有一个请求能获取到锁去查询数据库并更新缓存,其他请求等待,从而避免大量请求同时查询数据库。
- 永不过期:对于热点数据设置永不过期,通过后台异步线程定时更新缓存数据,保证数据的实时性。