面试题答案
一键面试缓存架构
- 多级缓存:
- 前端缓存:在浏览器端利用
localStorage
或sessionStorage
缓存不经常变化的商品信息,如商品分类、品牌等。这样在用户多次访问相关页面时,可直接从本地获取数据,减少对 API 的请求。 - CDN 缓存:对于静态资源,如商品图片、样式文件、脚本等,使用 CDN 进行缓存。CDN 节点分布在各地,能根据用户地理位置快速返回缓存的资源,减轻后端服务器压力。
- API 网关缓存:在 API 网关层设置缓存,针对一些频繁请求且不常变化的 API 响应进行缓存。例如,商品列表 API 的响应,若商品信息更新频率低,可在网关缓存一段时间。可选用 Redis 作为缓存工具,因其读写速度快,支持多种数据结构。
- 后端应用缓存:在后端应用内部,针对业务逻辑中的一些常用数据进行缓存。比如,库存管理模块中,可缓存热门商品的库存数量,减少对数据库的频繁查询。同样可使用 Redis 或 Ehcache 等缓存框架。
- 前端缓存:在浏览器端利用
- 缓存数据结构设计:
- 商品信息:以商品 ID 为键,商品详细信息(JSON 格式)为值存储在缓存中。例如,
{ "productId1": {"name": "商品1", "price": 100, "description": "描述1" } }
。 - 订单相关:可按订单 ID 缓存订单详情,对于订单列表,可采用分页缓存,以
pageNumber - pageSize
组合为键,订单列表数据为值。
- 商品信息:以商品 ID 为键,商品详细信息(JSON 格式)为值存储在缓存中。例如,
数据更新机制
- 缓存失效策略:
- 基于时间:为缓存数据设置过期时间(TTL)。例如,商品列表缓存设置 TTL 为 5 分钟,订单相关缓存根据业务场景设置不同的 TTL,已完成订单缓存时间可长些,未支付订单缓存时间短。
- 基于事件:当商品数据、库存数据或订单状态发生变化时,触发缓存更新事件。比如,商品价格更新后,立即删除对应商品 ID 的缓存数据,下次请求时重新从数据库获取并缓存。
- 数据一致性保证:
- 读写锁:在更新数据时,使用读写锁保证数据一致性。例如,在库存更新时,先获取写锁,更新数据库后,再删除相关缓存。读操作获取读锁,确保在写操作进行时,读操作等待,避免读到旧数据。
- 异步更新:对于一些对实时性要求不高的数据更新,可采用异步方式。如订单状态更新后,通过消息队列(如 Kafka)异步通知相关服务更新缓存,减少对业务处理的阻塞。
API 网关路由策略
- 基于请求类型:
- GET 请求:优先检查 API 网关缓存,若缓存命中则直接返回;未命中则根据请求路径将请求路由到对应的后端服务,并在获取响应后缓存结果。
- POST、PUT、DELETE 请求:直接路由到后端服务进行业务处理,处理完成后根据情况更新相关缓存。例如,POST 提交新订单后,更新订单列表缓存和库存缓存。
- 负载均衡:
- 轮询:将请求均匀分配到后端多个服务实例上,适用于各实例性能相近的情况。
- 加权轮询:根据后端服务实例的性能指标(如 CPU、内存使用率等)设置权重,性能好的实例权重高,接收更多请求。
- 基于 IP 哈希:根据客户端 IP 地址计算哈希值,将相同 IP 的请求路由到固定的后端实例,有助于保持会话一致性。
应对高并发下的一致性问题和性能瓶颈
- 一致性问题:
- 分布式事务:对于涉及多个服务的数据更新操作,如订单创建同时更新库存,使用分布式事务框架(如 Seata)保证数据一致性。
- 版本控制:在数据表中添加版本号字段,每次更新数据时版本号递增。读取数据时,将版本号一同返回给客户端,客户端更新数据时带上版本号,服务端校验版本号一致才进行更新,避免并发更新导致数据不一致。
- 性能瓶颈:
- 水平扩展:增加后端服务实例数量,通过负载均衡器将请求均匀分配,提高系统整体处理能力。同时,增加缓存服务器节点,提高缓存读写性能。
- 异步处理:将一些非关键业务逻辑(如订单创建后的邮件通知)异步化处理,通过消息队列解耦主业务流程,提高系统响应速度。
- 优化数据库:对数据库进行索引优化、查询语句优化,采用读写分离架构,主库负责写操作,从库负责读操作,提高数据库并发处理能力。