秒杀三阶段
通常,从秒杀开始到结束,往往会经历三个阶段:
- 准备阶段:这个阶段也叫作系统预热阶段,此时会提前预热秒杀系统的业务数据,往往这个时候,用户会不断刷新秒杀页面,来查看秒杀活动是否已经开始。在一定程度上,通过用户不断刷新页面的操作,可以将一些数据存储到Redis中进行预热。
- 秒杀阶段:这个阶段主要是秒杀活动的过程,会产生瞬时的高并发流量,对系统资源会造成巨大的冲击,所以,在秒杀阶段一定要做好系统防护。
- 结算阶段: 完成秒杀后的数据处理工作,比如数据的一致性问题处理,异常情况处理,商品的回仓处理等。
Redis助力秒杀系统
在秒杀场景中,在 Redis 中扣减库存而不是在数据库中 是一个常见的优化策略,主要原因是为了 提高系统性能 和 应对高并发。以下是详细的原因分析:
1. 性能瓶颈:数据库的局限性
- 数据库的并发能力有限:
- 数据库(如 MySQL)的并发处理能力通常较低,每秒只能处理几千到几万的请求。
- 在秒杀场景中,可能会有数十万甚至上百万的用户同时请求,数据库会成为性能瓶颈。
- 磁盘 I/O 开销大:
- 数据库的写操作通常涉及磁盘 I/O,而磁盘 I/O 的速度远低于内存操作。
- 秒杀场景需要快速响应,磁盘 I/O 会成为性能瓶颈。
2. Redis 的优势
- 内存操作,性能极高:
- Redis 是基于内存的键值存储系统,读写速度极快(每秒可处理数十万到数百万的请求)。
- 在秒杀场景中,库存扣减操作需要快速完成,Redis 的内存操作特性非常适合。
- 原子性操作:
- Redis 提供了原子性操作(如
DECR
、INCR
),可以确保库存扣减的准确性,避免超卖问题。
- 高并发支持:
- Redis 是单线程模型,通过事件驱动和非阻塞 I/O 支持高并发请求,适合秒杀场景的高并发需求。
数据库的最终一致性
- Redis 作为缓存层:
- Redis 中的库存数据是缓存层,用于快速响应高并发请求。
- 数据库作为持久化层:
- 数据库中的库存数据是持久化层,用于最终的数据存储和查询。
- 异步同步:
- 通过消息队列将 Redis 中的库存变化异步同步到数据库,确保数据的最终一致性。
5. 为什么不在数据库中直接扣减库存?
- 性能问题:
- 数据库的并发能力和磁盘 I/O 性能无法满足秒杀场景的高并发需求。
- 锁竞争:
- 在数据库中直接扣减库存可能会导致大量的锁竞争,进一步降低系统性能。
- 响应延迟:
- 数据库操作的延迟较高,无法满足秒杀场景的快速响应需求。
存在的问题:
1.Gossip 协议是一种允许在分布式系统中共享状态的去中心化通信协议,通过这种通信协议,我们可以将信息传播给网络或集群中的所有成员。
- 如果有某一项信息需要在整个网络中所有节点中传播,那从信息源开始,选择一个固定的传播周期(譬如 1 秒),随机选择它相连接的 k 个节点(称为 Fan-Out)来传播消息。
- 每一个节点收到消息后,如果这个消息是它之前没有收到过的,将在下一个周期内,选择除了发送消息给它的那个节点外的其他相邻 k 个节点发送相同的消息,直到最终网络中所有节点都收到了消息,尽管这个过程需要一定时间,但是理论上最终网络的所有节点都会拥有相同的消息。上图是 Gossip 传播过程的示意图,根据示意图和 Gossip 的过程描述,我们很容易发现 Gossip 对网络节点的 连通性和稳定性 几乎没有任何要求:
- 它一开始就将网络某些节点只能与一部分节点部分连通(Partially Connected Network)而不是以全连通网络(Fully Connected Network)作为前提;
- 能够容忍网络上节点的随意地增加或者减少,随意地宕机或者重启,新增加或者重启的节点的状态最终会与其他节点同步达成一致。
- Gossip 把网络上所有节点都视为平等而普通的一员,没有任何中心化节点或者主节点的概念,这些特点使得 Gossip 具有极强的鲁棒性,而且非常适合在公众互联网中应用。
同时我们也很容易找到 Gossip 的缺点:
- 消息最终是通过多个轮次的散播而到达全网的,因此它必然会存在全网各节点状态不一致的情况,
- 由于是随机选取发送消息的节点,所以尽管可以在整体上测算出统计学意义上的传播速率,但对于个体消息来说,无法准确地预计到需要多长时间才能达成全网一致。
- 消息的冗余,同样是由于随机选取发送消息的节点,也就不可避免的存在消息重复发送给同一节点的情况,增加了网络的传输的压力,也给消息节点带来额外的处理负载。
比喻:
- 想象一下,你在一个聚会上,听到一个八卦消息(比如“某人结婚了”)。
- 你会随机选择几个人,把这个消息告诉他们。
- 这些人又会随机选择其他人传播这个消息。
- 最终,整个聚会的人都会知道这个消息
集群模式下的redis用的通信协议并不适合秒杀场景:
. 信息传播方式
- 比喻:
- 球鞋店没有统一的公告系统,而是让每个店员(节点)随机告诉其他店员库存信息。
- 店员 A 告诉店员 B:“还剩 50 双。”
- 店员 B 告诉店员 C:“还剩 50 双。”
- 店员 C 告诉店员 D:“还剩 50 双。”
- 最终,所有店员都知道库存信息,但这需要一定的时间。
- 问题:
- 在信息传播的过程中,用户可能会收到不一致的库存信息。
- 例如,用户 A 从店员 A 那里得知还剩 50 双,而用户 B 从店员 B 那里得知还剩 50 双,但实际上库存已经快卖完了。
2. 传播延迟
- 比喻:
- 店员之间的信息传播需要时间,而用户抢购是实时的。
- 当用户抢购时,店员可能还没有收到最新的库存信息,导致超卖。
- 问题:
- 用户抢购的速度远快于店员之间的信息传播速度,导致库存信息不一致。
3. 最终一致性
- 比喻:
- 最终,所有店员都会知道准确的库存信息,但这可能需要几分钟甚至更长时间。
- 在这段时间内,用户已经完成了抢购,导致超卖或库存不一致。
- 问题:
- 秒杀场景需要 强一致性,而 Gossip 协议只能保证 最终一致性。
2.哨兵模式下存在脑裂问题
脑裂是指分布式系统中节点之间失去正常联系,导致集群分成多个孤立的子集,每个子集都认为自己是“活着”的,从而造成数据一致性和可用性的严重问题。脑裂可能由网络故障、节点故障、通信延迟等因素引起,这会导致集群状态的不一致性,进而破坏系统的正常运行
假设有一个王国,国王(主节点)负责决策和发布命令,大臣们(从节点)负责执行命令并向国王汇报。国王和大臣们通过信使(网络)保持联系。
- 正常情况:
- 国王发布命令,大臣们接收并执行。
- 所有大臣都听从国王的指挥,王国运行良好。
- 脑裂问题:
- 有一天,国王和大臣们之间的通信突然中断(网络分区)。
- 一部分大臣仍然能联系到国王,继续听从国王的命令。
- 另一部分大臣联系不到国王,以为国王驾崩了,于是他们选举了一个新国王(新的主节点)。
- 现在,王国里同时存在两个国王,各自发布不同的命令,导致混乱。
- 后果:
- 两个国王的命令可能互相冲突(数据不一致)。
- 当网络恢复后,两个国王试图重新统一,但他们的命令已经无法调和(数据冲突)。
由于网络原因或者一些特殊原因,哨兵失去了对master节点器的感知,将会通过选举进行故障转移,将slave节点提升为master节点,这就导致了当前集群中有2个master,这就是脑裂现象的体现。不同的 client 链接到不同的 redis 进行读写,那么在两台机器上的 redis 数据,就出现了不一致的现象了。由于网络分区问题,主节点并不知道自己被当作从节点。当哨兵恢复对老master节点的感知后,会将其降级为slave节点,然后从新maste同步数据(full resynchronization),导致脑裂期间老master写入的数据丢失,完犊子了。
总而言之从CAP理论上看,redis是属于满足CP的,不满足A,因此说它不“可靠”没毛病。但本质上,redis是半持久化的NoSQL, 大家用它其实就是在用可靠性换性能,所以我的意思是从本质上,大家也没有指望redis有多高的可用性,一旦崩了数据可能就全没了。
事件驱动设计
扩展一下,以往见过的另一种秒杀的思路是用事件驱动架构设计,用MQ进行异步处理,最终落库到关系型数据库上。这种架构下的确可以避免redis的问题,但反过来性能会没那么好,设计也会更复杂,可以说这种设计站在了使用redis的反面,在保证可靠性和一致性的前提下,牺牲了一定的性能和用户体验。
在现代的分布式系统和应用中,实现不同组件之间的通信、协作和数据传递变得至关重要。事件总线作为一种强大的解决方案,可以帮助我们构建灵活、松散耦合的事件驱动架构,使各个组件能够有效地响应状态变化和事件发生。本文将介绍事件总线的基本概念、架构以及适用的使用场景。
网上商城,由多个微服务构成,包括用户管理、库存管理、订单处理等。为了保持灵活性和高效性,商城选择了使用事件总线来协调不同微服务之间的通信
主要场景描述:
- 用户下单: 当用户在网站上下单时,订单服务将生成一个"订单创建"事件,其中包含订单的信息,如用户ID、商品列表和总价。
- 事件传递: "订单创建"事件被发送到事件总线,等待其他微服务处理。
- 库存检查: 库存管理微服务订阅了"订单创建"事件,一旦收到事件,它会检查商品库存情况。
- 库存不足: 如果库存不足,库存管理微服务将生成一个"库存不足"事件,将其发送回事件总线。
- 通知团队: 订阅了"库存不足"事件的通知微服务会接收到事件,通知库存管理人员及时采取行动。
- 订单处理: 如果库存充足,库存管理微服务将生成一个"订单确认"事件,并发送到事件总线。
- 订单创建成功通知: 通知微服务订阅了"订单确认"事件,将订单创建成功的信息通知到用户。
- 支付服务: 支付微服务订阅了"订单确认"事件,它会处理支付事务并生成一个"支付完成"事件,并将事件发送到事件总线。
- 支付成功通知: 通知微服务订阅了"支付完成"事件,将订单支付成功的信息通知到用户。
- 更新订单状态: 订单服务订阅了"支付完成"事件,然后更新订单状态为"已支付"。
- 物流服务出库: 物流服务订阅了"支付完成"事件,然后处理商品出库和物流信息,生成一个"商品出库"事件,并发送到事件总线。
- 物流通知: 通知微服务订阅了"商品出库"事件,将商品的出库信息通知到用户
时序图如下
事件总线的产品优势
聚焦数字化转型的今天,事件驱动架构已成为许多企业实现实时数据流处理和分析的关键。那么,事件总线自身有哪些独特的特性,以及相较于直接使用 RabbitMQ、Kafka 等常用消息中间件,事件总线的优势在哪里?
首先,对于事件总线来说它有如下特点:
简单易用:事件总线提供全托管的 Serverless 事件管理服务,免运维,免部署。不再关心事件服务配套的组件,极大降低您构建事件驱动架构的复杂性。
标准化接入:事件总线提供统一 Put Event 事件推送协议,兼容 CloudEvents 1.0 规范,全面拥抱开源社区生态。
快速构建:支持云服务事件集与云上连接器,打通多种数据渠道,进行简单必要的配置,即可将数据快速导入至事件总线。您也可以对云上的事件进行格式转换和清洗,以满足各种事件使用场景的需求。
高可用:事件总线支持 Region 化、多可用区、分布式集群化部署,部分节点异常甚至可用区异常,仍会确保服务的可用性,具备极强的容灾能力,利用不同云服务满足不同的业务场景和业务需求,使得您的服务架构更加健壮。
配套能力:提供事件查询,事件日志,事件审计及事件全链路追踪能力。支持事件重放,事件备份,事件重试策略等基础事件驱动配套能力。
其次,相较于目前常用的 RabbitMQ、Kafka 等消息中间件,事件总线在以下几方面占较大优势:
适应性与灵活性: 事件总线适用于动态变化的系统,如微服务架构,能够更好地应对不断变化的需求,并且事件总线提供友好直观的界面和简化的设置,使得事件流的创建、管理和监控变得简单。对于 RabbitMQ、Kafka 等消息中间件,它们更适用于可预测通信模式的系统,如大规模数据处理,不太适用于频繁变化的情景。
可伸缩性和弹性:事件总线它提供了优秀的弹性扩展能力,使您能够根据需求调整资源,满足业务需求,而不会牺牲性能或稳定性。相比之下 RabbitMQ、Kafka 等消息中间件在处理大规模数据流时可能需要更多的管理和配置工作。
安全性和管理:在数据安全性和管理方面,事件总线提供了多样的措施,包括数据加密、访问控制和身份验证。这些功能有助于保护敏感数据免受威胁。相对而言,RabbitMQ、Kafka 等中间件可能需要追加配置信息以及借助第三方工具来实现类似的安全措施。
对于未来的发展,事件总线产品无论是从灵活性和易用性、可伸缩性和安全性,都展现出了独特的优势。
06
总结
本文探讨了一个重要概念:事件总线。我们从概念、使用场景以及实际应用示例出发,揭示了事件总线在构建灵活的事件驱动架构方面的价值。无论是在智能家居系统的安全监控还是网上商城的订单处理系统中,事件总线都展现了其在实时数据处理和实时响应方面的强大能力。
无论您是寻求更好的性能、更简化的操作还是更高的数据安全性,事件总线产品将成为您不可或缺的工具。目前智汇云事件总线产品正在紧锣密鼓的开发中,上线以后欢迎大家体验、提出宝贵的意见和建议。