34.Redis如何支撑秒杀

秒杀是一个典型的活动场景,遍布各种电商促销活动中,Redis也是经常被用来支持秒杀活动

整个秒杀分为了很多环节,包含秒杀前 秒杀中 秒杀后,对于不同的环节,Redis的功能并不相同,某些环境甚至并不起作用

首先需要说明的是秒杀场景下的常见问题

1.瞬时访问量非常高

需要支持大量的并发请求秒杀系统时,需要使用Redis拦截大部分的请求,避免大量请求直接发给数据库,压垮数据库

2.读多写少

秒杀场景下,查验商品商品是否还有库存,有库存的时候,才能进行库存扣减和下单操作

库存检查操作是典型的键值对查询,Redis天然支持

然后是秒杀活动中只有一定数额的商品,只有库存满足之后才能进行下单,这还要求读到的数据和实际数据具有原子性

其次是Redis在秒杀场景的哪些环节可以发挥作用

首先是进入后端前,这一步必然和Redis毫无关系,不过前端可以考虑利用CDN进行减负

其次是进入了后端,参加秒杀,这一步,大量的用户点击秒杀的按钮,产生大量的并发请求查询库存,一旦请求到达的时候还有库存,就触发生成订单的操作

所以查询库存和检查库存的操作,就交给Redis来做,而且需要保证具有原子性

之后就是秒杀之后,秒杀之后,还存在用户刷新商品页,以及抢到的用户支付订单等操作,不过这类操作往往在其他的模块,一般都能支撑

那么我们就主要说一下秒杀中,Redis的主要参与在于查询库存剩余和修改库存剩余

图片

Redis如何原子性支撑上述两个操作呢?

之前说过,Redis一是支持原子性操作,二是支持Lua脚本来原子性执行

对于原子性操作,其实还是使用incr这样的操作执行,我们拿Lua脚本顺便说一下原子性操作如何执行

首先在Redis上,我们先使用一个Hash类型的键值对来保存库存的这两个库存

id:total:N

id:ordered:N

一个hash中两个field,分别表示总库存量和已秒杀量

那么Lua脚本主要就要原子性的查询总库存量和已秒杀量,最后修改已秒杀量

#获取商品库存信息

local counts = redis.call(“HMGET”, KEYS[1], “total”, “ordered”);

#将总库存转换为数值

local total = tonumber(counts[1])

#将已被秒杀的库存转换为数值

local ordered = tonumber(counts[2])

#如果当前请求的库存量加上已被秒杀的库存量仍然小于总库存量,就可以更新库存

if ordered + k <= total then

#更新已秒杀的库存量

redis.call(“HINCRBY”,KEYS[1],”ordered”,k)

return k;

end

retur n 0

对于上面的脚本,我们可以在Redis客户端,使用EVAL来执行这个脚本

客户端根据返回值,确定秒杀是成功还是失败了,返回是k,就表示成功了如果是0,就是失败了

上面就时Redis扣减库存的核心流程

除了上面的原子操作,在实际的秒杀之前,还可以考虑使用分布式锁来支持秒杀操作,这样库存查验和扣减的原子性和锁加一起,双重保险

最后给出一个小tip:使用切片集群中不同的实例来分别保存分布式锁和商品库存信息,使用这种方式保存后,秒杀请求就会访问分布式锁的实例,没有拿到锁,就不会查询商品实例,就减轻压力了

总结一下:

Redis在秒杀过程中,负责了对库存查验和库存扣减的操作,这两个操作需要保证原子性,而Redis可以利用Lua和自身的原子操作来支持秒杀需求的

最后一个小问题

如果将一个商品的库存,交给4个实例的集群来保存,每个保存四分之一,秒杀请求分发到不同的实例进行处理,可以吗?

这会造成一些意外的操作,比如长连接导致的请求的都是一个实例,不建议这么做

发表评论

邮箱地址不会被公开。 必填项已用*标注