秒杀活动:

秒杀场景一般会在电商网站或(APP/小程序)举行一些活动或者节假日在12306网站上抢票时遇到。对于一些稀缺或者特价商品,一般会在约定时间点对其进行限量销售,因为这些商品的特殊性,会吸引大量用户前来抢购,并且会在约定的时间点同时在秒杀页面进行抢购。

秒杀场景特点:

秒杀时大量用户会在同一时间同时进行抢购,网站瞬时访问流量激增。

秒杀一般是访问请求数量远远大于库存数量,只有少部分用户能够秒杀成功。

秒杀业务流程比较简单,一般就是下订单减库存。

秒杀架构设计理念:

限流: 鉴于只有少部分用户能够秒杀成功,所以要限制大部分流量,只允许少部分流量进入服务后端。

削峰:对于秒杀系统瞬时会有大量用户涌入,所以在抢购一开始会有很高的瞬间峰值。高峰值流量是压垮系统很重要的原因,所以如何把瞬间的高流量变成一段时间平稳的流量也是设计秒杀系统很重要的思路。实现削峰的常用的方法有利用缓存和消息中间件等技术。

异步处理:秒杀系统是一个高并发系统,采用异步处理模式可以极大地提高系统并发量,其实异步处理就是削峰的一种实现方式。

内存缓存:秒杀系统最大的瓶颈一般都是数据库读写,由于数据库读写属于磁盘IO,性能很低,如果能够把部分数据或业务逻辑转移到内存缓存,效率会有极大地提升。

可拓展:当然如果我们想支持更多用户,更大的并发,最好就将系统设计成弹性可拓展的,如果流量来了,拓展机器就好了。像淘宝、京东等双十一活动时会增加大量机器应对交易高峰。

设计思路:

将请求拦截在系统上游,降低下游压力:秒杀系统特点是并发量极大,但实际秒杀成功的请求数量却很少,所以如果不在前端拦截很可能造成数据库读写锁冲突,甚至导致死锁,最终请求超时。

充分利用缓存:利用缓存可极大提高系统读写速度。

消息队列:消息队列可以削峰,将拦截大量并发请求,这也是一个异步处理过程,后台业务根据自己的处理能力,从消息队列中主动的拉取请求消息进行业务处理。

前端方案 :浏览器端(js)

页面静态化:将活动页面上的所有可以静态的元素全部静态化,并尽量减少动态元素。通过CDN来抗峰值。

禁止重复提交:用户提交之后按钮置灰,禁止重复提交

用户限流:在某一时间段内只允许用户提交一次请求,比如可以采取IP限流

后端方案

利用redis实现简单的秒杀系统

Redis是一个分布式缓存系统,支持多种数据结构,我们可以利用Redis轻松实现一个强大的秒杀系统。

利用redis记录读取实时库存。一旦库存不足,立即抛出异常。反馈给前端。如果库存足够,通过rpc调用通知订单微服务系统生成订单。得到订单系统的生成成功的反馈以后,在秒杀微服务系统中生成一个本地订单记录,在数据库增销量减库存。

Redis 提供了 INCR/DECR/SETNX 命令,把RMW三个操作转变为一个原子操作

Redis 是使用单线程串行处理客户端的请求来操作命令,所以当 Redis 执行某个命令操作时,其他命令是无法执行的,这相当于命令操作是互斥执行的

生成秒杀订单: 点击查看代码
//生成秒杀订单
public function createLimitOrder($storeId, $userId, $unionId, $subStoreId, $activityId, $skuId, $limitPrice, $selectedNum, $consigneeId, $reservationTime, $message)
{
//...
//通过redis 检查是否还有充足库存,如果不足,则抛出异常
$this->controlWithRedisStock($storeId, $activityInfo, $skuId, $selectedNum);
//...
//调用rpc 使order微服务系统生成订单,并返回orderId。
$orderId = $this->orderRpc->createOrder($storeId, $userId, $subStoreId, $activityInfo, $skuId, $activityPrice, $selectedNum, $consigneeId, $reservationTime, $message);
try {
Db::beginTransaction();
if ($orderId > 0) {
//记录本地秒杀订单,和订单系统的orderId绑定联系起来。
$id = $this->addLocalOrder($storeId, $userId, $orderId, $skuId, $selectedNum, $consigneeId, $activityInfo);
$this->addSales($storeId, $activityId, $skuId, $selectedNum); //做已经出售增加 (库存减少) //下单进行此操作
}
} catch (\Throwable $e) {
throw new ErrException(ExceptionParseData::parseData($e));
Db::rollback();
}
Db::commit(); return $orderId;
}
redis控制库存:点击查看代码
private function controlWithRedisStock($storeId, $activityInfo, $skuId, $selectedNum)
{
//redis 计数 (原子操作)---start---------------解决高并发问题------------------
if($activityInfo['inf'] == LimitCFG::STOCK_INF){ //无限库存
return true;
} $key = $this->getRedisStockKey($storeId, $activityInfo["id"], $skuId);
$redis_stock = $this->getOrSetRedisStockValByKey($key, $activityInfo['stock']);
if ($redis_stock <= 0) { //初步抵挡一下:这边的redis_stock 有可能不是最新的了。因为有一些请求已经同时通过了这一步,还没有来及更新它。
throw new ErrException(ExceptionCode::E12141); // ["12141", "库存不足了,无法下单"]
}
//这句是 原子性的 --- 程序到这里开始变成 串行()。
$redis_stock = $this->decRedisStockByKey($key, $selectedNum); if ($redis_stock < 0) {
//如果减多了再加回来
$this->addRedisStockByKey($key, $selectedNum);
throw new ErrException(ExceptionCode::E12141); // ["12141", "库存不足了,无法下单"]
}
return true;
//redis 计数 (原子操作)---end---------------
} public function getOrSetRedisStockValByKey($key, $dbStock)
{ $this->commonRedis->setNx($key, $dbStock); $redis_stock = $this->commonRedis->get($key);
// var_dump($redis_stock);
return $redis_stock;
} public function decRedisStockByKey($key, $selectedNum)
{
return $this->commonRedis->decrBy($key, $selectedNum); //原子操作,返回的是 减过之后的值。有用,后续还要判断 }

参考:[https://www.cnblogs.com/wangzhongqiu/p/6557596.html] [https://blog.csdn.net/HiJamesChen/article/details/109382666]

高并发解决方案之 redis原子操作(适用于秒杀场景)的更多相关文章

  1. Java 高并发解决方案(电商的秒杀和抢购)

    转载:https://blog.csdn.net/icangfeng/article/details/81201575 电商的秒杀和抢购,对我们来说,都不是一个陌生的东西.然而,从技术的角度来说,这对 ...

  2. PHP面试(二):程序设计、框架基础知识、算法与数据结构、高并发解决方案类

    一.程序设计 1.设计功能系统——数据表设计.数据表创建语句.连接数据库的方式.编码能力 二.框架基础知识 1.MVC框架基本原理——原理.常见框架.单一入口的工作原理.模板引擎的理解 2.常见框架的 ...

  3. java高级精讲之高并发抢红包~揭开Redis分布式集群与Lua神秘面纱

    java高级精讲之高并发抢红包~揭开Redis分布式集群与Lua神秘面纱 redis数据库 Redis企业集群高级应用精品教程[图灵学院] Redis权威指南 利用redis + lua解决抢红包高并 ...

  4. 高并发解决方案--负载均衡(HTTP,DNS,反向代理服务器)(解决大流量,高并发)

    高并发解决方案--负载均衡(HTTP,DNS,反向代理服务器)(解决大流量,高并发) 一.总结 1.什么是负载均衡:当一台服务器的性能达到极限时,我们可以使用服务器集群来提高网站的整体性能.那么,在服 ...

  5. 关于SQL SERVER高并发解决方案

    现在大家都比较关心的问题就是在多用户高并发的情况下,如何开发系统,这对我们程序员来说,确实是值得研究,最近找工作面试时也经常被问到,其实我早有去关心和了解这类问题,但一直没有总结一下,导致面试时无法很 ...

  6. 手把手让你实现开源企业级web高并发解决方案(lvs+heartbeat+varnish+nginx+eAccelerator+memcached)

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://freeze.blog.51cto.com/1846439/677348 此文凝聚 ...

  7. java并发编程与高并发解决方案

    下面是我对java并发编程与高并发解决方案的学习总结: 1.并发编程的基础 2.线程安全—可见性和有序性 3.线程安全—原子性 4.安全发布对象—单例模式 5.不可变对象 6.线程封闭 7.线程不安全 ...

  8. JAVA系统架构高并发解决方案 分布式缓存 分布式事务解决方案

    JAVA系统架构高并发解决方案 分布式缓存 分布式事务解决方案

  9. Redis 高并发解决方案

    针对大流量瞬间冲击,比如秒杀场景 redis前面可以加一层限流 sentinel / Hystrix redis高并发(读多写少)下缓存数据库双写误差: 1. 修改操作使用分布式锁(就是修改的时候加锁 ...

  10. Java分布式系统高并发解决方案

    对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了.而并发问题是绝大部分的程序员头疼的问题, 但话又说回来了,既然逃避不掉,那我们就坦然面对吧~今天就让我们一起来研 ...

随机推荐

  1. NDK 减少 so 库体积方法总结

    . 使用 strip 使用 NDK toolchain 可以把调试的 C++ 符号表(Symbol Table)中数据删除,我们一般我们打成 APK 会自动帮我们做这个工作,当然也可以手动设置: 手动 ...

  2. 01 流程控制之for循环

    '''1.什么是for循环 循环就是重复做某件事,for循环是python提供第二种循环机制2.为何要有for循环 理论上for循环能做的事情,while循环都可以做 之所以要有for循环,是因为fo ...

  3. esxi虚拟机定时创建快照

    1.vim-cmd vmsvc/getallvms  列出所有虚拟机信息 2.获取需要备份的虚拟机的Vmid 3.执行快照  vim-cmd vmsvc/snapshot.create Vmid $( ...

  4. file类型的input框获取文件

  5. android cannot generate view binders android.databinding.tool.util.LoggedErrorException

    错误: Cannot resolve type 'viewModel'错误: cannot generate view binders android.databinding.tool.util.Lo ...

  6. pip备份、安装requirements.txt中的包和anaconda的安装(linux)

    pip备份.安装requirements.txt中的包和anaconda的安装(linux)   1. 从已有的环境中,备份已经安装的package pip freeze > requireme ...

  7. dendrogram

    https://ww2.mathworks.cn/help/stats/dendrogram.html

  8. python 根据二维数组画出彩色图像

    方法:采用seaborn中的heatmap import seabornimport numpy as npimport pandas as pdimport matplotlib.pyplot as ...

  9. 为什么常用Formdata对象来上传图片

    一.上传的数据体格式Content-Type 1.application/x-www-form-urlencoded 2.application/json 3.multipart/form-data ...

  10. Blob下载

    下载方式 const aBlob = new Blob( array, options ); export function downLoadFile(data: ArrayBuffer, fileN ...