当用户抢购时,就会生成订单并保存到tb_voucher_order这张表中,而订单表如果使用数据库自增ID就存在一些问题:
场景分析二:随着我们商城规模越来越大,mysql的单表的容量不宜超过500W,数据量过大之后,我们要进行拆库拆表,但拆分表了之后,他们从逻辑上讲他们是同一张表,所以他们的id是不能一样的,于是乎我们需要保证id的唯一性。
全局ID生成器,:是一种在分布式系统下用来生成全局唯一ID的工具,一般要满足下列特性:
序列号:32bit,秒内的计数器,支持每秒产生2^32个不同ID代码实现:
下单时需要判断两点:
下单核心逻辑分析:
当用户开始进行下单,我们应当去查询优惠卷信息,查询到优惠卷信息,判断是否满足秒杀条件
if(voucher.getStock()<1){//库存不足returnResult.fail("库存不足!");}//5,扣减库存booleansuccess=seckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id",voucherId).update();if(!success){//扣减库存returnResult.fail("库存不足!");}if(voucher.getStock()<1){//库存不足returnResult.fail("库存不足!");}//5,扣减库存booleansuccess=seckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id",voucherId).update();if(!success){//扣减库存returnResult.fail("库存不足!");}假设线程1过来查询库存,判断出来库存大于1,正准备去扣减库存,但是还没有来得及去扣减,此时线程2过来,线程2也去查询库存,发现这个数量一定也大于1,那么这两个线程都会去扣减库存,最终多个线程相当于一起去扣减库存,此时就会出现库存的超卖问题。
悲观锁:
悲观锁可以实现对于数据的串行化执行,比如syn,和lock都是悲观锁的代表,同时,悲观锁中又可以再细分为公平锁,非公平锁,可重入锁,等等
乐观锁:
乐观锁:会有一个版本号,每次操作数据会对版本号+1,再提交回数据时,会去校验是否比之前的版本大1,如果大1,则进行操作成功,这套机制的核心逻辑在于,如果在操作过程中,版本号只比原来大1,那么就意味着操作过程中没有人对他进行过修改,他的操作就是安全的,如果不大1,则数据被修改过,当然乐观锁还有一些变种的处理方式比如cas
乐观锁的典型代表:就是cas,利用cas进行无锁化机制加锁,var5是操作前读取的内存值,while中的var1+var2是预估值,如果预估值==内存值,则代表中间没有被人修改过,此时就将新值去替换内存值
其中dowhile是为了在操作失败时,再次进行自旋操作,即把之前的逻辑再操作一次。
intvar5;do{var5=this.getIntVolatile(var1,var2);}while(!this.compareAndSwapInt(var1,var2,var5,var5+var4));returnvar5;课程中的使用方式:
修改方法一:
booleansuccess=seckillVoucherService.update().setSql("stock=stock-1")//setstock=stock-1.eq("voucher_id",voucherId).eq("stock",voucher.getStock()).update();//whereid=?andstock=以上逻辑的核心含义是:只要我扣减库存时的库存和之前我查询到的库存是一样的,就意味着没有人在中间修改过库存,那么此时就是安全的,但是以上这种方式通过测试发现会有很多失败的情况,失败的原因在于:在使用乐观锁过程中假设100个线程同时都拿到了100的库存,然后大家一起去进行扣减,但是100个人中只有1个人能扣减成功,其他的人在处理时,他们在扣减时,库存已经被修改过了,所以此时其他线程都会失败方法二:之前的方式要修改前后都保持一致,但是这样我们分析过,成功的概率太低,所以我们的乐观锁需要变一下,改成stock大于0即可
booleansuccess=seckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id",voucherId).update().gt("stock",0);//whereid=andstock>0知识小扩展:
针对cas中的自旋压力过大,我们可以使用Longaddr这个类去解决
Java8提供的一个对AtomicLong改进后的一个类,LongAdder
大量线程并发更新一个原子性的时候,天然的问题就是自旋,会导致并发性问题,当然这也比我们直接使用syn来的好
所以利用这么一个类,LongAdder来进行优化
如果获取某个值,则会对cell和base的值进行递增,最后返回一个完整的值