Redis+分布式+秒杀

聊一下MySQL

关于mysql关系型数据库的一些分析:

1、从性能上:如果我们碰到需要执行耗时特别久,并且执行结果不是很频繁变动的SQL语句,我们就没有必要每次都去查询数据库,因为每次操作数据库都很耗时。

2、从并发上:在大并发的情况下(比如618秒杀活动,你敢让千万级的请求直接打到数据库上吗?)所有的请求直接访问数据库,数据库就由可能造成宕机。

Redis

针对第一种情况,可以将不那么频繁变换的结果放入基于缓存的数据库中,每次查询就去缓存中读数据,这样就提升了查询效率,也减轻了数据库的压力。

针对第二种情况,可以让这些高并发的请求从数据库中分离出来,不去直接访问数据库,另外找一个适合高并发的来处理请求。

那就是Redis!!!

是什么?

  • 基于内存的K/V存储中间件(关于K/V你还能想到谁?)
  • NoSQL(非关系型)数据库

干啥用?

  • 缓存

(将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率)

  • 设置定时过期数据
  • 分布式锁

(在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。可以使用 Redis 自带的 SETNX 命令实现分布式锁)

  • 抽奖功能
  • 实现排行榜
  • .......

面试题:1、redis常见的数据结构有哪些?

2、Redis持久化了解么?

秒杀场景

618预热早已开启,其中必不可少的秒杀是一个非常典型的活动场景。比如,在双 11、618 等电商促销活动中,都会有秒杀场景。秒杀场景的业务特点是限时限量,业务系统要处理瞬时的大量高并发请求。在秒杀活动中,大量用户在同一时间窗口内抢购限量商品,这往往会给系统带来极高的并发压力。使用传统的关系型数据库来处理并发请求往往会导致性能瓶颈和系统崩溃。而Redis作为一种高性能的内存数据库,能够快速处理大量的读写请求,非常适合用于秒杀场景。

秒杀的特征一:瞬时并发访问量非常高。一般数据库只能支撑千级别的并发请求,而redis的并发处理能力能达到万级别,甚至更高。在秒杀时,我们需要使用redis拦截大部分请求,避免大量请求直接发送给数据库,造成数据库宕机。

秒杀的特征二:读多写少,都是简单的查询操作。对于简单的查询操作并且结果相对固定我们可以先将其查询出来放入redis中,减轻数据库的压力。秒杀活动中只有少部分用户能成功下单,所以,商品库存查询操作(读操作)要远多于库存扣减和下单操作(写操作)。

秒杀的特征三:实时性和响应时间要求。 秒杀活动通常是实时进行的,用户希望尽快得知抢购结果。系统需要具备高性能和低延迟,及时响应用户的请求。

三个阶段

秒杀前

秒杀前,用户会不断的刷新商品详情页,那么就会导致对详情页的请求量急剧增加。这个阶段的应对方案,一般是尽量把商品详情页的页面元素静态化,然后使用 CDN 或是浏览器把这些静态化的元素缓存起来。这样一来,秒杀前的大量请求可以直接由 CDN 或是浏览器缓存服务,不会到达服务器端了。

秒杀时

用户点击秒杀按钮---->大量并发请求查询库存--->有库存(下单){没有则返回,可能会点击其他秒杀按钮,继续查询库存}--->库存扣减---->生成实际订单---->后续处理(物流、订单支付等)

这个阶段的操作是:库存查验、库存扣减、订单处理。而最大的并发力都在库存查验操作上。这个阶段为了支撑大量高并发的库存查验请求,我们需要在这个环节使用 Redis 保存库存量,这样一来,请求可以直接从 Redis 中读取库存并进行查验。

为什么订单处理操作可以在数据库中执行?而库存扣减操作不能在数据库中执行?

1、在数据库中进行库存扣减操作会带来额外的开销。我们已经事先在redis中保存好了库存量,如果交给数据库来处理扣减,那么就需要数据库将最新的值再同步到redis中,这样反而繁琐也带来了额外的开销。

2、可能会出现超售的情况。数据库的处理速度很慢,而秒杀又是一个快速高并发的场景,可能数据库更新完一个库值,秒杀就结束了,可能会导致用户查询到旧的库存值,再进行下单,出现超售的情况。

所以需要在redis中把库存扣减和库存查验两个操作一气呵成,实现原子操作。

秒杀后

用户量减少,服务器完全可以独立进行订单处理等操作。

实现

我们可以使用Redis的原子操作和分布式锁来支撑秒杀时的场景。(本文介绍分布式锁)

简单聊聊分布式

简单理解:类比卫生间的隔间:

  1. 隔离性: 卫生间的隔间将每个使用者隔离开来,使每个人都能独立使用空间,不会相互干扰。同样,分布式系统中的各个节点也是相互隔离的,它们可以独立运行,不会相互影响。
  2. 资源共享: 卫生间的隔间提供共享的基础设施,如水源和排水管道等。类似地,分布式系统中的节点可以共享一些资源,如共享存储、共享数据库等。
  3. 容错性: 如果一个卫生间隔间出现故障或需要维护,其他隔间仍然可以继续使用。同样,分布式系统中的节点之间可以具备容错能力,如果某个节点发生故障,其他节点可以接替其工作。
  4. 扩展性: 如果需要更多的卫生间隔间,可以增加更多的隔间来满足需求。同样,分布式系统可以通过增加更多的节点来实现扩展性,以处理更大的负载和流量。

Redis分布式锁(秒杀场景)

先让客户端向 Redis 申请分布式锁,只有拿到锁的客户端才能执行库存查验和库存扣减。这样一来,大量的秒杀请求就会在争夺分布式锁时被过滤掉。而且,库存查验和扣减也不用使用原子操作了,因为多个并发客户端只有一个客户端能够拿到锁,已经保证了客户端并发访问的互斥性。大部分秒杀请求本身就会因为抢不到锁而被拦截。

为啥要实现分布式锁:

  • 在有限的资源情况下,控制同一时间(段)只有某些线程(用户/服务器)能访问到资源。(比如秒杀时)
  • 单个锁只对单个jvm有限(好比用厕所只能用专属于自己的哪个)

关键:怎么保证同一时间内只有一个服务器抢到锁。

核心思想是:先来的人先把数据改成自己的标识(ip)后来的人发现标识已存在,就抢锁失败,继续等待,等先来的人执行方法结束,把标识清空,其他的人继续抢锁。(类比抢厕所/上厕所 )

注意事项:

  • 要注意解锁(你不可能一直占着厕所吧!!!)。
  • 一定要注意加上过期时间(否则如果因为服务器挂掉,锁就会一直存在了)。

使用分布式锁可能存在的问题

1、锁提前过期:分布式锁设置的时间提前过期,会导致其他方法加入一块同时执行。(好比你拉*没拉完,外面大哥把厕所门给撬开了)导致多个方法同时执行了。

2、还有可能导致连锁效应(解锁时不小心释放掉别人的锁)同样导致多个方法同时执行。

3、解锁的时候,判断好了是自己的锁,但是当要执行解锁的时候,就在这个时刻,自己的锁过期了(不需要删了)有其他的方法见缝插针进来了,导致A删了B的锁。(就像是你用完厕所,你自己的门锁时间过期开了,你手里还有把解锁的“钥匙”,结果你打开了旁边大哥的门)这里其实是破坏了原子性。(需要保证A的操作是原子性的,不能有其他操作打断。(也就是说A的上锁和解锁中间不能有其他的锁操作)跟操作系统的PV操作好像)。

4、锁提前过期,任务还没执行完,可以进行续期。(就像你上厕所,外面大哥等了你10min,结果你还没完事,然后你跟大哥说再给我10min)进行续期。

关于redis在秒杀场景中的应用还有很多,这只是一丢丢~

场景题

1、如何防止超卖问题?

2、如何保证订单的顺序和可靠性?

3、如何解决重复下单的问题?

4、做了什么限流削峰的措施?

5、如果缓存中的数据突然失效,导致请求全部打到了数据库,怎么办?

6、秒杀系统面临的问题有哪些?

........

博客参考

实践篇(11)Redis支撑秒杀场景的关键技术

知识星球

热门相关:帝少的专属:小甜心,太缠人      最强反套路系统   战神   豪门重生盛世闲女