尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

【高并发秒杀架构】(4)电商秒杀架构升级方案

【高并发秒杀架构】(4)电商秒杀架构升级方案 本文为「电商秒杀架构」系列第四篇结合其他电商公司的经验针对电商项目当前的秒杀系统从架构梳理、痛点分析出发系统梳理三类常见的架构升级方案Redis 垂直拆分方案、库存预分配方案、动态库存分配方案。一、秒杀系统当前的架构梳理先回顾一下当前秒杀系统的整体架构二、当前架构的痛点分析从系统扩容的角度来分析当前架构Redis 最容易成为性能瓶颈。因为秒杀应用可以基于 Nginx 反向代理进行水平扩展RocketMQ 也可以通过集群配置进行水平扩展而 Redis 则相对扩展比较困难。而对于 Redis为了保证缓存数据的读写性能当前架构中优先保证的是从 Redis 与秒杀应用部署在同一个服务器上这样可以最大限度提升缓存的读写性能。但是这也让 Redis 成为后续系统扩容的瓶颈。所以秒杀系统后续如果要进一步提升性能最需要调整的是其中 Redis 部分。为此我们先将当前秒杀系统中 Redis 部分的核心架构整理出来以便后续进行重点分析你可能第一眼就会看到这样单点的 Redis 服务显然是不符合高并发应用的集群化要求的。但是你要注意其实 Nginx 节点是可以和应用一起扩展的——后续可以部署多个 Nginx 节点然后同样给每个 Nginx 节点配置一个 Redis 从节点这样就可以进行整体扩容。像这样所以当前架构的核心痛点其实并不在于单点 Redis 的性能问题而是单点 Redis 的数据压力会比较大。当前我们电商项目中秒杀的商品还不太多数据比较少。但是如果后续秒杀商品多起来单点 Redis 的数据压力就会比较大这会极大地影响 Redis 的性能。三、常见改进方案3.1 Redis 垂直拆分方案集群化既然单点的 Redis 数据量不够那么最简单的想法就是将单机 Redis 升级成为集群通过集群机制扩充 Redis 的数据量升级成为 Redis 集群后Redis 的数据量问题得到了解决。但是要注意在秒杀场景下我们也不是简单升级完 Redis 集群就可以了。采用集群部署 Redis 后因为大部分的 Redis 客户端都是通过连接池实现的此时 Redis 的连接数就会逐渐成为瓶颈。一般的办法是通过中间件来减少连接数例如使用TwemProxy于 Redis 之间建立单链接交互并通过 Twemproxy 实现分片逻辑这样我们就可以水平扩展出更多的 Twemproxy 来增加连接数此时遇到的问题就是 Twemproxy 实例众多应用维护、配置困难需要在这之上做负载均衡。比如通过LVS/HaProxy实现 VIP虚拟 IP就可以做到节点切换对应用透明、故障自动转移还可以通过实现内网 DNS 来做其负载均衡通过 Redis 的集群化部署多个 SKU 的数据最终会分散到各个 Redis 节点当中我们就可以解决 Redis 数据量太大的问题。并且基于 Redis 集群的方式Redis 服务的可用性也得到了提高——我们可以通过再加入哨兵机制等方式保证少量 Redis 节点挂了后整个 Redis 集群不会出现问题。3.2 库存预分配方案通过 Redis 的集群化部署我们就可以解决 Redis 数据量太大的问题。但是与之前架构不同的是我们只能使用一致性算法实现 Redis 集群而这样就无法保证每个 Nginx 只读取本地的 Redis这样其实会对 Redis 的读写性能产生一定的影响所以在很多互联网企业中不会直接使用 Redis 的集群架构而是搭建多个 Redis 节点并通过库存预分配的方式自行控制 Redis 中的数据分布。例如自行搭建多级 Redis 缓存第一级 Redis 管理总的秒杀库存。在秒杀活动开始时增加一个库存预分配的程序将总库存分配到多个二级 Redis 中。然后在每个二级 Redis 上就可以搭建一个对应的 Nginx 加 APP 的秒杀应用每个应用就只访问对应的二级 Redis。而二级的 Redis 就可以还原成我们当前秒杀系统中的单点架构从而保证每个 Nginx 加 APP 的秒杀应用可以只访问本地的 Redis。通常建议都配置成11的组合这样可以尽量保证每个 Nginx 和秒杀应用都操作本地的 Redis也就不用再另外维护秒杀应用与二级 Redis 的对应关系这样调整后给整个秒杀架构带来了非常明显的优点通过预分配库存的方式可以让每个应用都有一套独立的库存数据各个应用之间处理库存时互不干扰从而大大降低并发量以应对高并发应用也就可以横向扩展。由于各个应用扣减库存的速度不一致这也可以一定程度上防止羊毛党。具体扩展多少个应用和预分配多少套库存数据要看两个纬度一个是并发量一个是活动的库存数据。不同热度的秒杀活动可以配备不同的资源在当前项目中只要在前端引导用户进入不同的静态页面即可。可以一定程度上解决热点商品的问题由于每个二级 Redis 里可以分配多个 SKU 的活动信息因此每个二级 Redis 对应的秒杀应用也可以承载多个商品的秒杀活动。这样即使出现某一个特别火热的热点商品也可以通过部署多个秒杀应用的方式增加热点商品的承载能力。但是这个架构也有它自己的不足之处前端秒杀页合理导航的问题在我们的秒杀系统中是通过往 OpenResty 中发布的静态页面作为秒杀的入口的。如果想要多个服务平均地承载同一个商品的秒杀服务就需要在前端进行合理的导航将不同的用户导入到不同的 OpenResty 对应的秒杀页面。而这时前端的导航很难与预分配的库存进行沟通——某一个二级 Redis 服务分配的库存很多但是前端导航很少这就造成了商品的浪费。服务出现问题时库存数据很难回收为了尽量让秒杀应用只读取本地的 Redis 数据二级 Redis 大概率还是会采用单点的方式进行部署。此时由于每个二级 Redis 中的库存数据并不完整如果二级 Redis 在活动中途出现崩溃那么基于这个 Redis 的库存数据就无法回滚会影响到整体的库存管理。虽然对于秒杀应用单节点的 Redis 可用性不是首要的但在纯粹单节点情况下至少活动数据可以通过日志管理即使出了问题也可以通过日志快速恢复后续可以进行返场等活动进行补救而采用预分配之后库存的管理是分散的中途出现问题库存的管理就会变得很复杂就无法恢复到整个集群全部正常时的场景。服务之间无法沟通造成的业务尴尬比如每个二级 Redis 上都分配全量的 SKU 信息只有这样才比较好实现但是只有二级 Redis 1 和二级 Redis 3 上承载了 SKU 1 的秒杀活动当这两个 Redis 上的库存扣减完成后就无法再继续扣减了而实际上二级 Redis 2 上还有很多 SKU 1 的库存就无法正常售卖。无法跨服务凑单如果秒杀活动一次可以抢购多个商品出现了某一个服务上的库存不够一个订单时也无法与其他服务进行沟通进行合理的凑单。比如每个二级 Redis 中还剩下 1 个库存但是某一个订单需要抢购 2 个商品总的库存是足够的却无法完成这个订单。3.3 动态库存分配方案之前库存预分配方案的核心问题在于库存只是在活动开始时进行分配而不能在活动扣减过程中进行灵活调整。所以要解决这个问题就需要独立出一个库存预分配的服务在扣减过程中灵活调整各个服务的库存具体机制如下将库存分配服务独立出来这样便于与其他秒杀应用进行沟通。在秒杀活动开始时进行库存预分配。这时不一定将所有库存全部分配完成可以在一级 Redis 中预留一部分库存。每个秒杀应用独立扣减库存当发现库存低于某一个阈值下限时不要等到库存扣没了才通知通知库存分配服务库存分配服务再访问各级 Redis对库存进行统一重分配。重分配时优先从一级 Redis 中分配库存这样的速度是比较快的。一级 Redis 中库存扣减完成后再协调二级 Redis 进行库存动态分配并且在分配过程中可以用一级 Redis 作为统一缓存进行协调。比如发现二级 Redis 2 上的 SKU 1 的库存就没有扣减过这时就可以直接将二级 Redis 2 上的 SKU 1 库存全部回收分配给其他二级 Redis。当其他二级 Redis 中的库存超过了某一个阈值上限时由于其他 Redis 上的库存扣减速度不一致所以不建议一次全部平均分配完预留一部分库存作为机动库存可以加快分配的速度将多出来的库存放回到一级 Redis 中。在秒杀活动快要结束或者是库存快要完全售完时比如所有二级 Redis 中的库存全都低于某一个临界点可以将剩余库存全部回收然后一起分配给某一个二级 Redis这样就可以在遇到大订单时努力进行最后的拼单。很多互联网企业都采用了这样一套架构。如果做得比较细致的话库存分配服务还可以用来监控各个秒杀应用的状态——如果发现某个秒杀应用挂了也可以及时回收其对应的二级 Redis 库存。而更进一步如果做到了对每个秒杀服务状态的精准把控那么甚至可以将二级 Redis 进一步改为秒杀应用的本地RocksDB缓存这样又可以再次提升秒杀应用的并发性能。总之独立出这个库存分配服务后就可以动态调整各个零散的二级 Redis 库存。这样既保证了每个秒杀应用可以只操作本地的 Redis 缓存最大限度增加应用的吞吐量又能保证这些独立的秒杀应用可以整体进行协调像一个整体进行工作。
返回列表