水果商城网站模板,网站做seo推广方案,和平天津网站建设,如何做全景素材网站文章目录 超卖问题解决方式什么是库存超卖问题#xff1f;乐观锁和悲观锁的定义超卖问题解决方式一、悲观锁1.jvm单机锁2.通过使用mysql的行锁#xff0c;使用一个sql解决并发访问问题3.使用mysql的悲观锁解决4. 使用redis分布式锁来解决 二、乐观锁解决1.版本号2. CAS法乐观锁和悲观锁的定义超卖问题解决方式一、悲观锁1.jvm单机锁2.通过使用mysql的行锁使用一个sql解决并发访问问题3.使用mysql的悲观锁解决4. 使用redis分布式锁来解决 二、乐观锁解决1.版本号2. CAS法Compare and Switch or Set。 超卖问题解决方式
什么是库存超卖问题 库存超卖是指多个请求同时减少库存时库存数量变为负数的情况。例如某商品库存数量为10同时有两个请求减少库存数量假设两个请求同时查询库存数量为1然后各自减少库存数量最后库存数量变为-1这就是超卖问题。 乐观锁和悲观锁的定义
悲观锁 认为线程安全问题一定会发生因此在操作数据之前先获取锁确保线程串行执行。例如Synchronized、Lock都属于悲观锁。 优点简单粗暴 缺点性能一般 乐观锁 认为线程安全问题不一定会发生因此不加锁只是在更新数据时去判断有没有其它线程对数据做了修改。如果没有修改则认为是安全的自己才更新数据。如果已经被其它线程修改说明发生了安全问题此时可以重试或异常。 优点性能好 缺点存在成功率低的问题
超卖问题解决方式
一、悲观锁
1.jvm单机锁 认为线程问题一定会发生因此在操作数据库之前先获取锁确保线程串行执行例如synchronized关键字和ReentrantLock可重入锁。 2.通过使用mysql的行锁使用一个sql解决并发访问问题
通过sql扣减库存
?xml version1.0 encodingUTF-8?
!DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.ht.atp.plat.mapper.WmsStockMapperupdate idcheckAndReduceStockupdate wms_stockset stock_quantity (stock_quantity - #{reduceStock})where id 1 and (stock_quantity - #{reduceStock}) 0;/update
/mapper3.使用mysql的悲观锁解决
使用for update加锁查询库存
注意
需要注意的是该查询库存和更新库存的操作必须放在同一个本地事务中否则悲观锁将失效。悲观锁只有在本次操作全部完成事务提交之后才会释放锁。如果不在同一个事务中锁提前释放去更新库存还是会存在并发的问题。
4. 使用redis分布式锁来解决
基于redis实现分布式锁 重要指令set lock_name unique_value NX PX 解释 unique_value表示客户端编码一定要具有唯一性在解锁的时候需要验证value和加锁的一致才允许删除key 解决超时问题第一个进程加锁释放了但还未执行业务逻辑第二个进程就获取了锁 NX若key不存在就设置成功若存在就返回true PX指定过期时间 基于redisson开源框架实现
RLock lock redisson.getLock(huaweiLock);
//只有一个客户端可以成功加锁
lock.lock(); //执行业务逻辑
检查库存
创建订单
扣减库存
更新redis//释放锁其他客户端可以尝试加锁
lock.unlock();
redis分布式锁的缺点: redis分布式锁 锁住的资源是同一个商品的库存多用户同时下单的时候会基于分布式锁串行化处理导致没法同时处理同一个商品的大量下单的请求。 redis分布式锁优化 其实说出来也很简单相信很多人看过java里的ConcurrentHashMap的源码和底层原理应该知道里面的核心思路就是分段加锁 Java 8中新增了一个LongAdder类也是针对Java7以前的AtomicLong进行的优化解决的是CAS类操作在高并发场景下使用乐观锁思路会导致大量线程长时间重复循环。 LongAdder中也是采用了类似的分段CAS操作失败则自动迁移到下一个分段进行CAS的思路 其实这就是分段加锁。你想假如你现在iphone有1000个库存那么你完全可以给拆成20个库存段要是你愿意可以在数据库的表里建20个库存字段比如stock_01stock_02类似这样的也可以在redis之类的地方放20个库存key。 总之就是把你的1000件库存给他拆开每个库存段是50件库存比如stock_01对应50件库存stock_02对应50件库存。 接着每秒1000个请求过来了好此时其实可以是自己写一个简单的随机算法每个请求都是随机在20个分段库存里选择一个进行加锁。 bingo这样就好了同时可以有最多20个下单请求一起执行每个下单请求锁了一个库存分段然后在业务逻辑里面就对数据库或者是Redis中的那个分段库存进行操作即可包括查库存 一旦对某个数据做了分段处理之后有一个坑大家一定要注意就是如果某个下单请求咔嚓加锁然后发现这个分段库存里的库存不足了此时咋办 这时你得自动释放锁然后立马换下一个分段库存再次尝试加锁后尝试处理。这个过程一定要实现。
redis分布式锁优化缺点 首先你得对一个数据分段存储一个库存字段本来好好的现在要分为20个分段库存字段 其次你在每次处理库存的时候还得自己写随机算法随机挑选一个分段来处理 最后如果某个分段中的数据不足了你还得自动切换到下一个分段数据去处理
二、乐观锁解决
1.版本号 给商品加上版本号字段如果查询到就让其version1,在修改执行的时候先判断版本号是不是正确的如果是让其版本号发生变化并执行扣减如果不是就说明当前商品已经卖出。 2. CAS法Compare and Switch or Set。 CAS流程如下 获取目标内存位置的当前值。检查当前值是否与预期值相等。如果相等则将新值写入目标内存位置否则放弃写入操作可能是重新读取当前值并重试整个CAS操作。 比如当前的订单系统中就可以使用查询到的库存作为预期值修改的时候进行判定如果是库存和第一次查询到的一样就执行不一样就取消执行这样就能够保证原子性 代码1 boolean success seckillVoucherService.update().setSql(stock stock -1) //set stick stock - 1.eq(voucher_id, voucherId).eq(stock, voucher.getStock()) // where id ? and stock ?.update();这种方法结果测试发现出错率较高。比如同时刻有100个请求第一个请求成功修改库存但剩余99个请求在乐观锁判断时都发现数据库的库存数据和原先获取的不一致导致无法通过事实上我们在修改数据时只需要判断库存是否大于零即可。 代码2 boolean success seckillVoucherService.update().setSql(stock stock -1) //set stick stock - 1.eq(voucher_id, voucherId).gt(stock,0).update();