干货 | 分布式缓存与DB秒级一致设计实践(2)

简介: 干货 | 分布式缓存与DB秒级一致设计实践

04


   

缓存更新平台


缓存更新平台主要有下面两大功能:


  • 执行实际的缓存增、删、改命令;
  • 缓存内容发生了变更后通知业务方;


由于缓存更新平台汇总了所有的缓存更新操作,所以它能够在缓存发生变更后,通过广播消息及时通知业务方,业务方拿到该消息后可以判断是否要做处理。目前这个功能主要用于解决热点key的内容更新问题,这在前文热点key处理的相关章节已做了详细说明,后文不再赘述。

 

后文主要介绍该平台的第一点功能。

 

前文有讲到,我们为了规避并行操作同一个key导致缓存中存储旧值而非最新值的问题,从而引入消息机制将缓存操作串行化。该缓存更新平台就用于串行的从消息队列消费缓存操作消息。


所以我们的核心需求是:单线程处理同一个key的缓存操作消息且不让旧的缓存覆盖新的缓存。

 

基于上面的需求,产生了四个问题:


  • 怎么判断多个消息属于同一个key的缓存消息?
  • 缓存操作的消息量级非常大(峰值情况下几十万条/分钟),怎么快速消费完?
  • 怎么知道缓存内容是新还是旧,是否该对该消息进行处理?
  • 由于基于消息,如何保障消息一定会被处理?

 

1、怎么判断多个消息是属于同一个key的缓存消息?


针对这个问题,我们通过在消息中携带缓存的key来解决这个问题,这样做带来了几个好处:

1)将业务和缓存更新平台解耦,key的内容由业务全权决定;2)通过首先计算key的hash值,然后对其取模,可以将相同的key分配到相同的线程处理(见后文);3)可扩展性强,针对后续热点key的分析和自动化加载热点key也起到了关键作用(后续迭代计划的功能);


2、怎么快速消费消息?

由于核心需求是单线程的处理同一类key的消息,所以不同key的消息由不同的线程处理既能很好的解决性能问题,又不会产生逻辑问题。我们采取了如下架构去消费产生的消息:

同一类key通过计算其hash值,然后再对结果进行取模,可以保证它们进到同一个内存队列和线程,从而规避并行操作同一个key的问题。通过这个架构,如果某个key的消息消费过慢,也不会影响其他key的消费进度,从而既保障了消费速度也满足了需求。

实践下来,目前我们仅用了两台机器就能做到每分钟消费几十万条消息,且远未遇到瓶颈。

3、怎么知道缓存内容是新还是旧,是否该对该消息进行处理?虽然我们做到了同一类key的单线程处理,并且,我们使用的公司的消息队列能保障消息的有序性。但依然有个问题没解决,那就是旧的缓存可能会覆盖新的缓存,因为我们没法保障新的缓存消息一定在旧的缓存消息产生之后再产生。考虑下面这个场景:

时刻(由近及远) 线程1 线程2 线程3
T1 基于key,读到DB中的值为v1

T2
基于key,更新DB中的值为v2
T3

基于key,读到DB中的值为v2
T4

投递缓存内容为key<->v2的消息
T5 投递缓存内容为key<->v1的消息


从上面可以看到,由于线程3的key<->v2消息先产生,所以它会被先消费,此时缓存的数据会变为v2, 然后缓存更新平台再处理key<->v1这条消息,从而导致v1覆盖缓存中的v2,出现旧值覆盖新值的问题。在这里,我们引入了缓存版本的概念来解决这个问题,我们认为每条缓存的数据都应该有一个版本号(业务提供,例如可以是修改数据的时间戳,只要满足单调递增即可)。基于此,缓存的增、删、改操作全部基于这个版本号来进行判断是否执行操作。具体的判断逻辑,在后文介绍。

1)缓存的增、删、改流程

 

  • 删除缓存流程

先看下面流程图:


image.png


我们整个流程上是基于消息通知,这个消息生产的时机是只要业务删除了数据库中的数据就可以向缓存更新平台发送一条删除缓存消息。从流程上可以看到,针对该消息的处理,流程里面并不是简单的删除一个key,而是将删除的内容标记一下存入缓存。这样做带来了如下的好处:

1)能够避免缓存穿透;2)能够避免缓存“复活”已经删除的数据;

如果我们简单的删除缓存中的内容而不是将被删除的内容标记起来存入缓存,那么当出现下面这个场景时,缓存中就会长期存在已经删除的数据,从而导致数据使用方误认为该数据仍然有效。

首先假设现在某个key在缓存中不存在。线程先消费了删除该key的消息且删除的数据版本是v1,然后消费了存储缓存key<->v1的消息,这个时候就会将key<->v1写入缓存,但其实这个数据已经被删除了。但即便将删除的内容放入缓存,考虑极端情况,仍然可能会有问题,考虑下面这个场景:

有两条邻近产生的消息:

消息1:删除key<->v1的缓存消息消息2:新增key<->v1的缓存消息

假设消费完消息1后,因为某种原因(如平台宕机或者消息队列出问题等等),消息2过了很久(缓存key已经过期)才被消费到,这时在缓存中存入该消息也会导致被删除的数据“复活”。所以针对这类情况,有两种措施:

1)缓存永久有效2)超过一定时间未处理的消息就不处理了(我们采取的方案)关于删除缓存消息中的版本,前文有提到,我们认为每条缓存数据都是有版本的。所以即便业务要删一条数据,那么被删的数据肯定也是有版本号的,而这个版本就是该条消息的版本。我们借助这个版本,就知道缓存中的数据是否是更新的版本,是否可以被覆盖并且被标记为删除了。

  • 新增&修改缓存流程

新增缓存消息的处理流程和修改缓存消息的处理流程一致,见下:

首先,消息的生产时机是:

新增缓存消息:

  • 业务往数据库中插入数据
  • 业务流程因为缓存缺失导致直接访问到数据库的数据

修改缓存消息:

业务修改了数据库中的数据流程上可以看到,当缓存更新平台收到新增/修改缓存消息时,拿着消息中的key去查缓存,如果没有,则直接存入缓存;如果缓存中存在,则拿着缓存中的数据版本与消息的数据版本进行对比,如果消息中的数据版本更高(即更新),那么就可以安全覆盖缓存中的数据;反之,则不应该覆盖。通过这个流程,就可以很好的避免传统缓存更新里面经常出现的低版本数据覆盖缓存中高版本的数据。新增&修改缓存流程与删除缓存流程大体一致,仅有一个区别点,如下:

新增&修改缓存:


删除缓存:


删除流程中关心的是消息中的版本是否大于等于缓存中的版本,而新增&修改缓存流程只关心消息中的版本是否大于缓存中的版本,为什么删除流程要关心版本相同的情况而新增&修改流程不关心呢?

针对删除,假设删除的数据对应的版本是3,而缓存中正好也有这个数据且数据版本也是3,这说明删除操作其实针对的是最新的数据,所以可以将缓存标记为删除态。

针对新增&修改,假设某条数据修改后,数据版本为3。此时缓存里面正好也有版本为3的数据,那么缓存中的这条数据会有下面两种情况:

  • 该数据在缓存中被标记为删除态了(即被业务删除了该数据)



若此时写入缓存,会导致删除态数据重新“复活”

  • 该数据处于正常状态



若此时写入缓存,没有任何意义。

综上,无论针对上述哪种情况,只要不对这条消息进行处理,就不会有任何问题。所以,修改流程只有当消息中的版本高于缓存中的版本时才设置缓存。4、如何保障消息一定会被处理?由于整个平台依赖于消息队列中间件,那么如果消息队列中间件出了问题(如宕机/网络问题/消息投递失败等等)导致消费变得很慢或漏掉消息,怎么办?前文提到,我们提供的缓存访问组件内部会将每条消息记录到业务DB。缓存更新平台通过业务提供的接口增量轮询该表,确保所有消息都被及时消费掉。通过这样的容错措施,确保不会因为单点故障导致缓存来不及更新。

   

05


   小结


可以看到,通过上述的缓存访问组件和缓存更新平台,可以做到缓存与数据库数据的快速一致,从而既保障了性能同时又最大程度的降低了用户看到过期数据的可能性。

接下来,我们将继续迭代,解决1)减少缓存访问组件在业务代码上的侵入性;2)在缓存更新平台引入缓存key的分析机制,可以自动判定是否是热点key等问题。

相关文章
|
1月前
|
人工智能 安全 Java
分布式 Multi Agent 安全高可用探索与实践
在人工智能加速发展的今天,AI Agent 正在成为推动“人工智能+”战略落地的核心引擎。无论是技术趋势还是政策导向,都预示着一场深刻的变革正在发生。如果你也在探索 Agent 的应用场景,欢迎关注 AgentScope 项目,或尝试使用阿里云 MSE + Higress + Nacos 构建属于你的 AI 原生应用。一起,走进智能体的新世界。
459 39
|
1月前
|
关系型数据库 Apache 微服务
《聊聊分布式》分布式系统基石:深入理解CAP理论及其工程实践
CAP理论指出分布式系统中一致性、可用性、分区容错性三者不可兼得,必须根据业务需求进行权衡。实际应用中,不同场景选择不同策略:金融系统重一致(CP),社交应用重可用(AP),内网系统可选CA。现代架构更趋向动态调整与混合策略,灵活应对复杂需求。
|
3月前
|
数据采集 消息中间件 监控
单机与分布式:社交媒体热点采集的实践经验
在舆情监控与数据分析中,单机脚本适合小规模采集如微博热榜,而小红书等大规模、高时效性需求则需分布式架构。通过Redis队列、代理IP与多节点协作,可提升采集效率与稳定性,适应数据规模与变化速度。架构选择应根据实际需求,兼顾扩展性与维护成本。
106 2
|
6月前
|
人工智能 安全 应用服务中间件
阿里巴巴 MCP 分布式落地实践:快速转换 HSF 到 MCP server
本文分享了阿里巴巴内部将大规模HSF服务快速转换为MCP Server的实践经验,通过Higress网关实现MCP协议卸载,无需修改代码即可接入MCP生态。文章分析了MCP生态面临的挑战,如协议快速迭代和SDK不稳定性,并详细介绍了操作步骤及组件功能。强调MCP虽非终极解决方案,但作为AI业务工程化的起点具有重要意义。最后总结指出,MCP只是AI原生应用发展的第一步,未来还有更多可能性值得探索。
1169 48
|
2月前
|
消息中间件 缓存 监控
中间件架构设计与实践:构建高性能分布式系统的核心基石
摘要 本文系统探讨了中间件技术及其在分布式系统中的核心价值。作者首先定义了中间件作为连接系统组件的&quot;神经网络&quot;,强调其在数据传输、系统稳定性和扩展性中的关键作用。随后详细分类了中间件体系,包括通信中间件(如RabbitMQ/Kafka)、数据中间件(如Redis/MyCAT)等类型。文章重点剖析了消息中间件的实现机制,通过Spring Boot代码示例展示了消息生产者的完整实现,涵盖消息ID生成、持久化、批量发送及重试机制等关键技术点。最后,作者指出中间件架构设计对系统性能的决定性影响,
|
6月前
|
监控 Linux 应用服务中间件
Linux多节点多硬盘部署MinIO:分布式MinIO集群部署指南搭建高可用架构实践
通过以上步骤,已成功基于已有的 MinIO 服务,扩展为一个 MinIO 集群。该集群具有高可用性和容错性,适合生产环境使用。如果有任何问题,请检查日志或参考MinIO 官方文档。作者联系方式vx:2743642415。
2178 57
|
6月前
|
安全 JavaScript 前端开发
HarmonyOS NEXT~HarmonyOS 语言仓颉:下一代分布式开发语言的技术解析与应用实践
HarmonyOS语言仓颉是华为专为HarmonyOS生态系统设计的新型编程语言,旨在解决分布式环境下的开发挑战。它以“编码创造”为理念,具备分布式原生、高性能与高效率、安全可靠三大核心特性。仓颉语言通过内置分布式能力简化跨设备开发,提供统一的编程模型和开发体验。文章从语言基础、关键特性、开发实践及未来展望四个方面剖析其技术优势,助力开发者掌握这一新兴工具,构建全场景分布式应用。
682 35
|
6月前
|
缓存 NoSQL Java
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
168 5
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
|
5月前
|
存储 缓存
.NET 6中Startup.cs文件注入本地缓存策略与服务生命周期管理实践:AddTransient, AddScoped, AddSingleton。
记住,选择正确的服务生命周期并妥善管理它们是至关重要的,因为它们直接影响你的应用程序的性能和行为。就像一个成功的建筑工地,工具箱如果整理得当,工具选择和使用得当,工地的整体效率将会大大提高。
226 0
|
7月前
|
存储 负载均衡 测试技术
ACK Gateway with Inference Extension:优化多机分布式大模型推理服务实践
本文介绍了如何利用阿里云容器服务ACK推出的ACK Gateway with Inference Extension组件,在Kubernetes环境中为多机分布式部署的LLM推理服务提供智能路由和负载均衡能力。文章以部署和优化QwQ-32B模型为例,详细展示了从环境准备到性能测试的完整实践过程。
下一篇
oss云网关配置