面试题答案
一键面试1. 缓存方案设计
- 商品列表缓存:
- 缓存介质:使用 Redis 作为缓存数据库,因其读写速度快,适合存储不常变化的数据。
- 缓存策略:采用读缓存策略,在 API Route 处理商品列表查询时,首先检查 Redis 中是否存在对应缓存。若存在,直接返回缓存数据;若不存在,查询数据库,将结果存入 Redis 并返回。
- 缓存时间设置:由于商品列表数据变化频率低,可以设置较长的缓存时间,如 1 天(86400 秒)。
- 用户订单数据缓存:
- 缓存介质:同样使用 Redis,但采用不同的缓存策略。
- 缓存策略:采用写后更新缓存策略。当用户订单数据发生变化(如创建新订单、订单状态更新)时,先更新数据库,再更新 Redis 缓存。在获取订单数据时,从 Redis 读取,保证数据的实时性。
- 缓存时间设置:可以设置较短的缓存时间,如 1 分钟(60 秒),以确保即使缓存更新失败,也能在短时间内获取最新数据。
2. 缓存失效机制
- 商品列表:
- 主动更新:当商品信息发生变化(如价格调整、库存变化等),在更新数据库的同时,主动删除 Redis 中对应的缓存,下次查询时重新生成缓存。
- 被动失效:利用 Redis 的过期时间机制,达到设定的缓存时间后,缓存自动失效,下次查询重新获取并缓存数据。
- 用户订单数据:
- 主动更新:订单状态改变或新订单创建时,立即更新 Redis 缓存,保证数据一致性。
- 被动失效:设置较短的过期时间,让缓存定期失效,促使系统重新从数据库获取最新数据。
3. 缓存穿透应对策略
- 布隆过滤器:在 API Route 入口处,使用布隆过滤器判断查询参数(如商品 ID、订单 ID)是否存在。布隆过滤器可以快速判断一个元素是否在集合中,若不存在则直接返回,避免无效查询穿透到数据库。
- 空值缓存:当查询结果为空时,将空值也存入 Redis 缓存,并设置较短的过期时间。下次相同查询时,直接返回空值,减少数据库压力。
4. 缓存雪崩应对策略
- 分散过期时间:对于商品列表缓存,不要设置统一的过期时间,而是在一定范围内随机设置过期时间,避免大量缓存同时失效。例如,原本设置 1 天过期,可以改为在 23 - 25 小时之间随机设置过期时间。
- 加锁排队:在缓存失效后,大量请求可能同时访问数据库。通过加锁机制,只允许一个请求去查询数据库并更新缓存,其他请求等待。当第一个请求更新完缓存后,其他请求直接从缓存获取数据。可以使用 Redis 的 SETNX 命令实现分布式锁。
- 搭建多级缓存:除了 Redis 缓存外,还可以在应用层(如 Node.js 服务器)搭建一层本地缓存(如使用内存缓存库)。在高并发情况下,先从本地缓存获取数据,若本地缓存未命中,再查询 Redis 缓存,最后查询数据库,减轻 Redis 和数据库的压力。
5. 结合 Next.js 特性优化
- Incremental Static Regeneration:
- 商品列表页面:对于商品列表页面,可以利用 Incremental Static Regeneration 特性。在构建时生成静态 HTML,并设置合理的 revalidate 时间。例如,设置 revalidate = 3600(1 小时),表示每小时重新验证缓存数据。在这 1 小时内,用户访问商品列表页面直接返回静态 HTML,减轻服务器压力。当 revalidate 时间到达后,下一个请求会触发重新生成静态页面,保证数据的及时性。
- 用户订单页面:由于订单数据实时性要求高,可以根据用户登录状态动态渲染订单页面。在页面组件中,通过 useEffect 钩子函数在客户端发起 API 请求获取最新订单数据。同时,可以在服务器端设置较短的缓存时间,如 1 分钟,结合 Incremental Static Regeneration 的 revalidate 机制,保证用户看到的数据接近实时更新。
6. 高并发场景下系统稳定性保障
- 负载均衡:使用负载均衡器(如 Nginx、HAProxy)将流量均匀分配到多个服务器实例上,避免单个服务器压力过大。
- 限流:在 API 网关层设置限流策略,限制每个 IP 或用户在单位时间内的请求次数。例如,每分钟最多允许 100 次请求,超出限制返回错误提示,防止恶意请求或大量突发请求压垮系统。
- 熔断与降级:引入熔断机制,当某个 API 服务出现故障或响应时间过长时,自动熔断该服务,返回兜底数据或提示信息,避免故障扩散。同时,在高并发场景下,可以根据系统资源情况进行服务降级,如减少一些非核心功能的响应,优先保证核心业务(如商品查询、订单提交)的正常运行。