面试题答案
一键面试保证缓存与数据库数据一致性机制设计
- 读写顺序控制
- 写操作:先写数据库,成功后再更新缓存。这样做的原因是如果先更新缓存,在更新数据库失败的情况下,缓存与数据库数据不一致,且后续读操作可能读到错误的缓存数据。而先写数据库成功后再更新缓存,即使更新缓存失败,下次读操作也会从数据库读取正确数据并更新缓存。
- 读操作:先读缓存,若缓存命中则直接返回数据;若缓存未命中,则从数据库读取数据,返回给用户的同时更新缓存。这样可以保证大部分读请求直接从缓存获取数据,提高性能。
- 缓存失效策略
- 设置合理的缓存过期时间:根据数据的变化频率设置不同的过期时间。对于变化频繁的数据,设置较短的过期时间,如玩家实时在线状态等,过期后下次读操作会从数据库获取最新数据并更新缓存。对于相对稳定的数据,如玩家基本信息(在不常修改的情况下),设置较长的过期时间。
- 主动失效:当数据库数据发生变化时,除了更新缓存,还可以主动标记相关缓存失效。例如,使用发布 - 订阅模式,数据库更新操作触发一个消息,缓存服务订阅该消息,接收到消息后主动使相应缓存失效,下次读操作会重新从数据库加载数据到缓存。
高并发读写场景下性能与数据一致性平衡
- 异步处理
- 写操作异步化:对于写操作,可以使用消息队列(如 Kafka 等)将写请求异步处理。写操作先将数据发送到消息队列,返回成功响应给客户端,减少客户端等待时间,提高系统的响应性能。后台消费者从消息队列中依次读取数据,按照先写数据库再更新缓存的顺序进行处理。这样可以避免高并发写操作对数据库和缓存造成过大压力,同时保证数据一致性。
- 缓存更新异步化:在一些场景下,缓存更新可以异步进行。例如,对于一些允许短暂数据不一致的场景(如排行榜数据,在几分钟内数据稍微滞后不影响用户体验),写操作成功写入数据库后,通过异步任务更新缓存,进一步提高写操作的性能。
- 读写分离
- 数据库读写分离:使用主从数据库架构,主库负责写操作,从库负责读操作。写操作先在主库完成,通过主从复制机制将数据同步到从库。读操作从从库读取数据,这样可以分担数据库的读压力,提高系统整体性能。同时,为了保证读操作的数据一致性,可以采用一些策略,如在写操作完成后,短暂延迟从从库读取数据,确保主从数据同步完成。
- 缓存读写分离:对于缓存,可以采用读写分离的缓存架构,如使用 Redis 集群,一部分节点负责写操作,另一部分节点负责读操作。写操作节点更新数据后,通过集群同步机制将数据同步到读操作节点,保证缓存数据的一致性。在高并发读场景下,读操作节点可以分担读压力,提高系统性能。
- 缓存粒度控制
- 细粒度缓存:将数据按照不同的维度进行细分缓存,例如对于玩家数据,可以将玩家的装备信息、等级信息等分别缓存。这样在数据更新时,只需要更新对应的细粒度缓存,而不需要更新整个玩家数据缓存,减少缓存更新的频率和开销,提高系统性能。同时,通过合理的缓存失效策略,保证数据一致性。
- 粗粒度缓存:对于一些关联性强、更新频率低的数据,可以采用粗粒度缓存。例如,对于一个游戏场景中的所有 NPC 信息,可以缓存为一个整体。这样在高并发读场景下,可以减少缓存查询次数,提高性能。在数据更新时,整体更新缓存,确保数据一致性。