分布式环境并发场景下,如何操作抢红包(或者减少库存)

简介

在分布式场景高并发环境中,无论是抢红包还是减库存,其实本质上都是如何处理高并发中共享资源的问题,保证高并发资源分配的安全性

相互学习,如有错误还请指正~

思考

比如抢红包的场景,高并发访问数据库会给 mysql 带来较大压力,因此可以考虑把红包数据放在 redis 中,通过并发请求 redis 内存的红包数据,来快速响应请求

但是大量请求并发访问 redis 内存数据,如何保证这个资源的安全性呢?肯定要用到锁或者进行某种原子操作

lua 对 redis 的原子操作

逻辑思路:

  • 首先红包创建的时候,它会对应一个红包 id,创建时候预先知道它要分 10 个红包,那么就把总金额随机分成 10 个数字的数组预先 random 出来
  • 然后把红包 id 对应的金额数组,和红包 id 对应的 10 这个库存存到 redis 中,方便后续请求直接访问 redis
  • 注意 redis 中还需要维护一个 hashmap 用来判定一个用户是否领取过红包,如果用于领取过就把 userId 存进 map
  • 当多个请求并发进来(这里使用 lua 原子操作,保证线程安全)
    • 判定用户是否领过:线程 A 会线判定 map 中用户是否领取过,如果领取过直接返回,如果没有领取过就开始判定库存
    • 判定库存是否充足:如果库存 <= 0,那么直接返回红包被抢光,如果库存 > 0,就把库存减少一,然后 map 中添加上当前 userId,金额 list 弹出一个红包金额
  • 判定如果 redis 中库存为 0 就需要写入库存(同时需要考虑到红包可能还有剩余,因此也需要有一种定时机制把 redis 红包领取记录同步到 mysql)
  • 后续业务操作
  • 最终把金额 list 弹出的金额返回给前端

其他解决方式

  • 分布式锁

    对于一些大型打公司可能有分布式锁调用的以来包,这样写起来比较方便

    操作:先把红包库存数据导到 redis 中,然后兵法请求过来查询 redis,在查询前先有分布式锁的代码来看是否要阻塞,如果阻塞,等在运行的运行玩之后释放就可以了,锁住的内容包括查询 redis 红包库存是否充足,以及 redis 内存中改库存的操作。锁释放之后,可以判定红包库存如果为 0 就把 redis 记录同步到 mysql 中(也需要有一种定制机制定时同步 redis 到 mysql,因为可能存在红包领取不完),再可以进行后续业务操作。分布式锁里头的实现本质也是用了 lua 脚本保证 redis 多个操作的原子性

    特点:万一某个 server 实例宕机了,锁就没法释放,其他人就用抢红包锁住了,所以需要设置一个分布式锁的超时时间

  • 悲观锁:(不推荐,但是实际企业开发中可能有人在用)

    实际开发中,偷懒的程序员可能比较喜欢用,因为也最简单方便

    操作:其实就是事务操作中,进行 select for update(它只能在事务中使用)先锁住数据,直到事务提交才会释放锁

    特点:没有解决大量请求打向 mysql 的问题,而且完全串行执行性能很差,还需要注意一旦索引失效,行级锁就变成了表级锁(其他操作表里其他数据的操作也被锁住了)

  • 乐观锁

    操作:为了解决悲观锁效率问题,使用乐观锁,即 A 线程先看下版本号为 1,这个时候把 mysql 红包库存 - 1 且判定条件是 version == 1,但是突然发现 version == 2 了,这是因为有线程 B 并发执行完了并且把 version 再 ++ 了,这时候 A 线程执行数据库失败,返回 err 给到用户,并且线程 A 事务会回滚,这会到大量失败,并且大量回归操作也给 mysql 带来压力,但我们可以再稍微优化一下,就是引入重试机制,如果线程 A 操作数据库更新失败了,按照限定时间内重试或者设置重试次数,这样可以减少很多用户抢红包(扣库存)失败的返回

    特点:适合更新的并发操作不多的场景。存在出现部分并发用户被返回数据库操作 err 的情况

一些问题

  • 数据怎么从 mysql 存入到 redis 呢?
    对于促销来讲,我们再开发一个定时任务,把符合秒杀促销条件的商品存储到 redis 中,促销前定时执行触发。对于抢红包场景,原代码中加上,当用户创建出红包的时候,就把红包库存以及红包 random 金额数组存储到 redis 中
  • 什么场景会去查找 redis 而不是直接访问 mysql 呢?
    高并发访问数据库时候考虑上层加一个 redis
  • redis 如和同步到 mysql?什么时机
    可以通过定时任务方式定时同步到 mysql
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

abcnull

您的打赏是我创作的动力之一

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值