• Redis悲观锁解决高并发抢红包的问题

    悲观锁是一种利用数据库内部机制提供的锁的方法,也就是对更新的数据加锁,这样在并发期间一旦有一个事务持有了数据库记录的锁,其他的线程将不能再对数据进行更新了,这就是悲观锁的实现方式。

    首先在 RedPacket.xml 中增加一个 id 为 getRedPacketForUpdate 的 SQL,修改为下面的代码:

    <!-- 查询红包具体信息 -->
    <select id="getRedPacketForUpdate" parameterType="long"
        resultType="com.pojo.RedPacket">
        select id, user_id as userId, amount, send_date as sendDate, total,
            unit_amount as unitAmount, stock, version, note from T_RED_PACKET
            where id =#{id} for update
    </select>

    注意,在 SQL 中加入的 for update 语句,意味着将持有对数据库记录的行更新锁(因为这里使用主键查询,所以只会对行加锁。如果使用的是非主键查询,要考虑是否对全表加锁的问题,加锁后可能引发其他查询的阻塞),那就意味着在高并发的场景下,当一条事务持有了这个更新锁才能往下操作,其他的线程如果要更新这条记录,都需要等待,这样就不会出现超发现象引发的数据一致性问题了。

    再插入一条新记录到数据库里,如下面的代码所示。

    insert  into `t_red_packet`(`id`,`user_id`,`amount`,`send_date`,`total`,`unit_amount`,`stock`,`version`,`note`)
    values (1,1,'200000.00','2019-07-29 16:35:20',20000,'10',20000,0,'20万元金额,2万个小红包,每个10元');

    还是以 20 万元的红包,每个 10 元,共两万个红包为例。插入这条数据后,获取其编号,同时在 RedPacketDao 中加入对应的查询方法。

    /**
      * 使用 for update语句加锁
      *
      * @param id红包id
      * @return 红包信息
      */
    public RedPacket getRedPacketForUpdate(Long id);

    接下来,将 UserRedPacketServiceImpl 代码中的”RedPacket redPacket = redPacketDao.getRedPacket(redPacketId);“的代码修改为以下代码:

    Redpacket redpacket = redpacketDao.getRedPacketForUpdate(redpacketId);

    做完这些修改后,再次进行测试,便能够得到如图 1 所示的结果。

    悲观锁测试结果
    图 1  悲观锁测试结果

更多...

加载中...