抢红包系统搭建和超发现象,以及解决问题提高性能

以传统的数据库作为存储介质、以Redis+Lua来作为库存存储介质最后落到数据库

本次案例介绍抢红包的场景,模拟20万红包,分为400个小红包,每个红包500元,有1000千人并发同时抢夺,并讲解会出现超发和保证如何数据一致性问题,在高并发场景下还需要保证性能的问题。

在这里,首先查看红包库存,是否有,如果有,则更新库存减一,然后插入一条记录到抢红包信息表中,这3个操作作为一个事务,原子性来操作。下面也讲解高并发下事务的问题

第一种:在没有任何锁的情况下,就是仅仅对事务操作操作,数据库也没有使用任何悲观锁。在1000个高并发下,数据出现超发

现象总共400个,出现了405个。

 

第二种:在代码层上使用synchronized,因为synchronized是在jvm语义层面的,后来jdk版本中对其作了优化,偏向锁、轻量级锁、重量级锁,因为高并发下众多线程访问synchronized的方法,所以会转为重量级锁(悲观锁,只有一个线程去执行抢红包)性能降低,,可采用ReentrantLock可重入锁。

(使用Lock接口 ReenTrant来显示加锁)

(解决了数据不一致问题,但是在互联网下,其性能有待提高。)

第三种:悲观锁是利用数据库的内部加锁机制,也就是对更新的数据加锁,这样在并发期间一旦有一个事务持有数据库的记录,其他线程将不能在对数据进行更新操作了,

注意这里使用的SQL中加入了for update 语句,意味着将持有对数据库记录的行更新操作(因为这里使用主键查询id为主键,所以是行级锁。如果使用的非主键查询,则会对整个表进行加锁,可能引发其他查询的阻塞)。

mysql中的行级锁:1、只有通过索引检索条件,innodb才会使用行级锁,否则使用表锁。

                               2、即使查询不同行的记录,如果查询的索引建是相同的,则也会产生锁冲突。

                               3、 如果数据表建有多个索引时,可以通过不同的索引锁定不同的行。

(解决了数据不一致问题,但是在互联网下,其性能有待提高。使用悲观锁就会造成大量的线程被挂起和恢复,这将十分消耗资源,从用户态切换到内核态,cpu频繁切换上下文,所以导致性能降低,悲观锁也称为独占锁。)

为了克服悲观锁带来的性能问题,提高并发处理的能力,避免大量线程因为阻塞导致CPU频繁切换上下文,故提出了乐观锁机制,并且已经在企业中被大量的应用了。

第四种:乐观锁是一种不阻塞其他线程的并发机制,称为非阻塞锁,不使用数据库的锁机制,因为不阻塞其他线程,所以并不会引发线程频繁挂起和恢复,便能够提高并发能力,乐观锁使用的是CAS原理。

CAS原理阐述:compareAndSwap 比较并交换的底层原理机制,对于多个线程竞争相同的资源,先保存一个旧值(old value)当扣库存的时候,先比较当前数据库的值与旧值是否一致,如果一致则进行扣减,否则就认为已经被其他线程修改过了,就不再进行操作了,可以考虑重试或者放弃,多数采用重试的机制,这样就是一个可重入锁了,流程如下:

CAS不排斥并发,也不独占资源,但是会出现一个问题,ABA的问题,可以使用一个自增的version版本号来解决问题。

         第一阶段采用乐观锁实现

版本号自增避免ABA的问题,对于查询也不使用显示的for update语句,避免锁发生的冲突,即没有线程阻塞的问题。

性能是提高了不少,但是成功率降低了,失败率高,有时候会容忍这个失败,这取决于业务的需求,因为允许用户再次发起抢夺红包,比如微信抢红包也常常会发生错误返回,然后用户再次去抢。因为库存不变,不会导致超发。

为了克服这个问题,提高成功率,还会考虑可重入机制(重试机制)也就是因为版本号不一致而失败,可以重试尝试去请求抢红包,但是过多的查询会造成大量的sql执行,目前比较流行方案:一种是按照时间的重入。(比如在100毫秒,不成功的会循环成功为止,直到时间超时退出,返回失败)。另一种是按照次数,比如限定3次,失败后重试,知道达到次数退出。这两种都有助于提高抢红包的成功率。

            二阶段的乐观锁(优化)按照时间、按照次数来进行限定的重试机制,目的提高成功率,对于使用乐观锁,操作业务(先查询红包信息库存以及版本号,然后根据当前版本号与库中的版本号匹配,如一致,则添加抢红包的用户信息到库中),涉及查询-更新-插入,因为使用了乐观锁,所以业务操作可以不使用事务,将三种操作作为一个原子性的,因为如果在方法上加了事务的话,在更新语句的时候,其他线程在操作的时候,就会阻塞,知道前一个线程退出事务,才会抢到锁。如果不加事务,就会出现更新失败,不会回滚,则用户信息表多了或者少了,不够正确,这边可以使用异步化,比如消息队列。

按照时间重试机制,就要设定好时间,过长会导致查询次数增多,也不宜过短,根据业务时间定。

 

以下为Redis来实现抢红包

        reidis事先预备好数据 库存信息

性能远远超过乐观锁的20多秒。在这个普通请求中,并没有去操作任何数据库,而只是将数据存放在redis缓存中,且库存放在内存中比放入数据库存取效率更快,以下就是抢红包的流程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值