栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

秒杀系统涉及问题全面分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

秒杀系统涉及问题全面分析

Redis的基本事务机制

Redis 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

Redis 事务的主要作用就是串联多个命令防止别的命令插队。

MultiExecDiscard

Multi:该命令其实是一个组队的过程,在输入Multi命令后,输入的命令会依次进入到命令队列,此时并不执行队列中任何一条语句的操作。

Exec:输入Exex命令后,Redis会亮命令队列中的命令依次执行,队列是先进先出。

Discard:在组队过程中放弃组队,清空命令队列。

两种常见错误:
1、组队错误:如果组队语句发生错误,在执行EXEC命令后会出现错误报告,整个命令队列的操作都会被取消。
2、执行错误:组队的语句并没有错误,但是存在逻辑的问题(比如:incr 字符串),在执行过程中会出现错误报告,但是队列中其他语句会正常执行,只会跳过错误的语句。

乐观锁

乐观锁(Optimistic Lock),每次取数据都乐观地认为其他线程不会修改数据,所以不会上锁。但是在更新的时候会判断一下在此期间有没有其他线程去更新这个数据,可以使用版本号等机制,一般用于多读,提高吞吐量。

Redis 利用***check-and-set*** 乐观锁机制实现事务。

Watch、Unwatch

在执行 multi 之前,先执行 watch key,可以对其进行监视。如果在事务执行中 key 被改变,那么当前事务将终端。
Unwatch 取消对所有 key 的监视。如果在执行 WATCH 命令之后,EXEC 命令或 DISCARD 命令先被执行,就不需要再执行 ***UNWATCH***命令 。

秒杀系统的问题 超卖

这是一个线程之间的并发问题:最简单的就是通过乐观锁解决。直接利用 Redis 中的 Multiexec 命令进行解决。

通常乐观锁的判断条件是锁是否被使用,因此乐观锁解决过程中会出现一个新问题,售出成功率低十分低,比如还有库存量为100,此时20位用户共同抢同一件商品只有一位用户可以抢购成功,这明显不符合逻辑。

这就是 少卖,乐观锁造成库存遗留的问题。

解决方法:这里的锁**(其实所谓的锁就是一个条件,判断线程是否能进入业务执行操作的条件)**通过自己实现,在库存拿货前一刻,查询库存剩余量是否大于零,若大于零则可以购买。(该方法是针对库存问题的解决)

拓展:有些情况下,数据是否变化 可能是判断是否能执行下一步的唯一条件,那么应该怎么解决呢?

方法一:分批加锁,实现分段锁(每次锁定的资源减少)。

比如:现在库存数量为100,分成十份把数据存入十张表中,每张表管理10个库存量。当用户进行抢购时,从不同的表(也就是不同入口),分别进行抢购,售卖的成功率就能大大提高。

方法二:加入悲观锁,这个方法比较容易理解,就是把业务逻辑设置成串行执行的模式,但是在秒杀系统中是不可取的,因为这个方法效率十分低,在实际中并不能满足开发需求,但是在某些特定情况,如下面的一人一单问题可能会用到。

连接超时

连接超时问题:一般可以通过连接池(需要用 synchronized 关键词进行锁住池)进行解决。

一人一单

这个问题最简单的思想就是在执行下单操作前,查询该用户是否已经购买过该商品(数据库中用户id是唯一的,并且在购买记录中商品号和用户id会一起存入订单中),如果发现已经有订单,则不允许用户下单。

但是实际情况并非如此,现在很多平台都可以多方登录,最基础的就是PC端和移动端同时登录账号进行抢购。用户此前确实尚未购买商品,但是由于多线程并发抢购,会导致出现一人多单的问题。

解决方法:通过加入悲观锁, 也就是在代码块前加入***synchronized*** 关键字。这把锁的判断条件可以设置为用户的id,也就是每个用户只允许通过一个线程抢购商品,由于 synchronized 的条件需要是类或者对象,因此把用户id通过toString()和intern方法执行操作作为锁对象。

注意:toString()底层原理是通过new关键字关键不同的字符串实例,而intern()方法是从常量池进行查找,并非new出新的字符串对象,因此确保了是同一个用户在不同环境中使用的是同一把锁。

集群模式的并发问题

集群模式会将服务部署到不同的服务器中,各个服务器使用的JVM不同,synchronized锁本质上是基于JVM的锁监视器实现的,因此在不同的JVM中,其实使用了不同的锁,因此要解决这个并发问题,需要实现跨JVM锁或者说是跨进程锁。

分布式锁

分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。
基本属性:互斥、高可用、高性能(高并发)、安全性(死锁问题)。
其他特性:可重用性、公平性等。

分布式锁的实现方法

MySQL数据库:利用mysql本身的互斥锁机制。
Redis数据库:利用setnx的互斥命令。
Zookeeper:利用节点的唯一性和有序性实现互斥。

基于Redis实现分布式锁

获取锁

SETNXEXPIRE

SET key value EX time NX

释放锁

手动释放:DEL

超时释放(业务超时或者宕机)

未完待续……
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/778283.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号