那些你不得不知的抢购业务要点

栏目: 数据库 · 发布时间: 6年前

内容简介:刚入门的产品小朋友们,或许你们只知道有抢购、团购、闪购等名词,但是你们有具体了解过这其中的业务要点是什么吗?下面就跟着我来具体了解一下吧!案例:马上就到双十一了,上级给我安排了一个秒杀抢购的活动,让我设计一个方案,那我应该如何下手呢?

刚入门的产品小朋友们,或许你们只知道有抢购、团购、闪购等名词,但是你们有具体了解过这其中的业务要点是什么吗?下面就跟着我来具体了解一下吧!

那些你不得不知的抢购业务要点

案例:马上就到双十一了,上级给我安排了一个秒杀抢购的活动,让我设计一个方案,那我应该如何下手呢?

页面上面的设计,这里我就不多说了,各大网站上都有很多案例了!

那些你不得不知的抢购业务要点

现在我重点来讲一下需要注意的几点:

一、超卖问题

假如你的库存有10,现在3个用户来购买,a用户购买3个,b用户购买5个,c用户购买3个,合起来就是准备购买11个。

如果三个用户是同时并发购买,会出现怎样的情况呢?

每个用户进行减库存的时候,数据库都会去修改一下数据,如下:update goods set amount=amount-购买数量 where goods_id=xxx。

mysql会锁定这一行数据(使用innodb存储引擎),数据库加的是排他锁。根据排他锁的特点:其他线程不能读、不能写此行数据。

排他锁情况下,那么其他用户就是等待状态了。

  1. a用户执行update的时候,锁定库存数据。update执行完毕后,减去了3个后,mysql自动释放锁。
  2. b用户执行,减去了5个。此时,已经卖掉8个库存了,库存数为2了。
  3. 但是c用户接着执行,Update goods set amount=amount-1 where goods_id=xxx。

结果库存数量变成-1了。

思考:把库存数量字段的类型,设计成正数类型,不允许出现负数,会怎么样呢?

测验结果:数据库会直接报错,通不过。

解决办法:只有库存数量,大于或等于购买数量的时候,才能去减库存。其他情况,提示信息,库存不足。

那些你不得不知的抢购业务要点

二、并发的问题

为了更好的理解并发和同步,我们需要先明白两个重要的概念: 同步和异步。

同步和异步的区别和联系

  • 所谓 同步 ,可以理解为在执行完一个函数或方法之后,一直等待系统返回值或消息,这时程序是出于阻塞的,只有接收到返回的值或消息后才往下执行其它的命令。
  • 异步 ,执行完函数或方法后,不必阻塞性地等待返回值或消息,只需要向系统委托一个异步过程,那么当系统接收到返回值或消息时,系统会自动触发委托的异步过程,从而完成一个完整的流程。

同步在一定程度上可以看做是单线程,这个线程请求一个方法后就待这个方法给他回复,否则他不往下执行。 异步 在一定程度上可以看做是多线程的,请求一个方法后,就不管了,继续执行其他的方法。

如何处理并发和同步?

首先需要明白,锁机制有两个层面:

  • 一种是代码层次上的,如:java中的同步锁,典型的就是同步关键字synchronized,这里我不在做过多的讲解;
  • 另外一种是数据库层次上的,比较典型的就是悲观锁和乐观锁。这里我们重点讲解的就是悲观锁(传统的物理锁)和乐观锁。

悲观锁(Pessimistic Locking):

悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自 外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。

悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能 真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系 统不会修改数据)。

第一种问题中描述的超卖现象,其实是并发抢购时出现的情况。用到的是数据库内带的加排他锁方式,阻止了其他线程读取、访问数据,这样等待的时间就比较长。而业界一般的解决是使用乐观锁的办法来解决:使用数据库的乐观锁是通用解决办法。

乐观锁:

相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。

如:一个金融系统,当某个操作员读取用户的数据,并在读出的用户数据的基础上进 行修改时(如更改用户帐户余额),如果采用悲观锁机制,也就意味着整个操作过程中(从操作员读出数据、开始修改直至提交修改结果的全过程,甚至还包括操作员中途去煮咖啡的时间),数据库记录始终处于加锁状态。可以想见,如果面对几百上千个并发,这样的情况将导致怎样的后果。

乐观锁机制在一定程度上解决了这个问题。

通俗说就是: 修改数据的时候,不给数据加锁。

乐观锁意思是不锁定表的情况下,利用业务的控制来解决并发问题,这样即保证数据的并发可读性又保证保存数据的排他性,保证性能的同时解决了并发带来的脏数据问题。

所以很多情况下都会采用 乐观锁 来解决业务上的问题。

高并发的解决方法主要有以下几点:

(1)前台优化

  1. 减少http请求——css文件合并  ,js文件合并;
  2. 压缩js,css文件;
  3. 使用雪碧图;
  4. 懒加载(只加载看到的第一屏内容,下拉之后看到其他的内容);
  5. 预加载(只加载默认图);
  6. cdn 加速。

(2)服务端优化:

  1. 页面静态化;
  2. 负载均衡、集群;
  3. 分布式;
  4. 使用队列。

(3)MySQL优化:

  1. 查询优化,能单表的单表
  2. 查询一条数据使用limit
  3. 生成查询缓存
  4. 使用索引
  5. 多表查询使用id进行关联
  6. 数据库分表
  7. 数据库分区
  8. 数据库集群
  9. 要查询的字段避免使用*号,指定需要的字段
  10. 避免使用%前缀的模糊查询
  11. 避免使用负向查询
  12. 避免使用or查询
  13. 避免使用子查询
  14. 避免使用 MySQL 自带函数
  15. 不要是rand
  16. 有顺序的读取
  17. 设置合适的数据类型
  18. 避免使用text类型
  19. 避免使用null

(4)代码优化:

  1. 用单引号代替双引号,双引号会查询变量;
  2. 避免使用require_once require_once会判断文件是否加载过;
  3. 使用静态方法代替普通方法,静态方法速度比普通方法快4倍;
  4. 变量使用完之后需要销毁;
  5. 尽量不要使用@;
  6. include用绝对路径,不要使用相对路径,相对路径会有查询的过程;
  7. 避免使用__SET __GET __AUTOLOAD;
  8. 循环的时候先确定循环次数,不要每次循环都要计算;
  9. 避免循环查库;
  10. 避免多层foreach嵌套;
  11. 避免使用递归 ,递归比较浪费资源。

三、下单和减库存要在一个事务中

如果不在一个事务内,可能出现两种现象:

  1. 订单入库失败、减库存成功。发现订单入库失败,减库存就不要继续进行下去了。
  2. 订单入库成功、减库存失败。实际下了20个订单,库存却没有减。数据不一致了。

四、设计虚拟库存和真实库存两套方案

有些人下单完后,最终并不会去付款。如果一下单就马上减库存,很多人下单,最终并不会去付款,可能导致库存数最后为0,别的用户无法下单了。而实际中仓库中却有库存在,这样库存数据是不准确的。

什么时候减库存?是下单完成减库存、还是付款完后减库存呢?

付款后,才减库存,可能出现的现象:用户下完单,接着去付款,结果库存不够了,这样用户体验很不好。 但是淘宝的设计是3天(大厂就是比较任性哦)

买家拍下商品后,“等待买家付款”的状态下系统会给予买家3天的时间进行付款,此时的付款动作是将钱款支付到支付宝公司。

那些你不得不知的抢购业务要点

此付款时间无法延长,若逾期未付款,交易将自动关闭,如您仍想购买,建议重新购买并及时付款。但是如果下完单就减库存,并能够保证用户下单只要付款,就一定能买到这个商品。这样的用户体验会较好。

具体技术实现办法: 下单后,马上减去库存。另外设置一个定时脚本,扫描超过30分未支付的订单,把订单中的商品数量返回到库存中去,订单关闭。

如唯品会的购物下单:

那些你不得不知的抢购业务要点

为什么使用虚拟库存和真实库存两套方案?

假设库存数是50,a订单购买了5个件商品,支付完毕,库存数减去5,库存数变成了45件。由于还没有发货,实际库存中还有50件商品,这样会出现混淆了。

使用两套库存记录方案是有必要的!

  1. 下单-操作虚拟库存数
  2. 商品发货出库-操作真实库存数

五、减少频繁读数据库的压力

用户每次点击一个商品详情页面,都要读取库存,判断:有没有库存。如果读库存走的是数据库判断,很多人来抢购的情况下,数据库的压力会很大。

假设是1万个用户同时访问抢购页面,数据库接受的访问次数是1万个并发。

用户还要进行刷新页面操作,由于每次刷新都会走数据库判断库存。数量会更大,数据库的压力就更大了。所以最好是,把库存总数,缓存在 redis 中去。

内存中缓存的库存数量,只用来做读判断,这样压力扛住了。而更改数据库的库存总数了,程序马上要把库存总数,同步到缓存中去。

系统抗压力问题:

  1. 如何限流?
  2. 如何防止恶意刷数据?

 防止限流就是写代码去阻止部分人进行频繁请求,为了识别是机器还是人工。加一些友好一点的验证码,这样不管是从体验上还是从系统的稳定性方面都是比较好的。

如下图淘宝做的验证:

那些你不得不知的抢购业务要点

滑块验证码方案,验证码后台针对用户产生的行为轨迹数据进行机器学习建模,结合访问频率、地理位置、历史记录等多个维度信息,快速、准确的返回人机判定结果。

攻与防技术都是在对抗中不断升级的,无解的验证码还不存在,但防的一方可以不断提升破解成本。应用选择滑块验证,也有部分因素是因为竞争激烈的互联网很看重用户体验。拖动毕竟是趣味性交互且容易完成,而图形验证码既容易被黑客攻破,对用户也并没有那么友好——肉眼识别无趣(可能还很艰难),键盘手动输入更浪费时间,体验不太好。

总结

本文介绍了产品在设计抢购闪购商品活动中应该注意的几点事项,在设计闪购活动时,会围绕如何处理并发、扣除库存、防止恶意刷数据等问题。

各公司可以先根据自己的业务情况来设计相对应的方案,然后再用成本计算法,反算开发时间与成本,这样既保证了项目进度,性能体验等也不错。

本文由 @香鱼 原创发布于人人都是产品经理。未经许可,禁止转载

题图来自 Pexels,基于 CC0 协议


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

编写高质量代码

编写高质量代码

秦小波 / 机械工业出版社华章公司 / 2011-12-28 / 59.00元

在通往“Java技术殿堂”的路上,本书将为你指点迷津!内容全部由Java编码的最佳实践组成,从语法、程序设计和架构、工具和框架、编码风格和编程思想等五大方面对Java程序员遇到的各种棘手的疑难问题给出了经验性的解决方案,为Java程序员如何编写高质量的Java代码提出了151条极为宝贵的建议。对于每一个问题,不仅以建议的方式从正反两面给出了被实践证明为十分优秀的解决方案和非常糟糕的解决方案,而且还......一起来看看 《编写高质量代码》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试