redis 库存扣减

前言

本文内容基于单机redis操作,暂不探讨集群及数据分片。
分别采用redis 数字incrementlistpush pop操作扣减库存。

1.不加锁操作

1.1 数据准备

设置key=100,向list push100条简单数据作为库存。示例:

redisTemplate.opsForValue().set("key","100");

for (int i = 0;i < 100; i++) {
    redisTemplate.opsForList().rightPush("keyList",String.valueOf(i));
}

atomicInteger = new AtomicInteger(0); // 记录key的扣减次数
keyList = new AtomicInteger(0); // 记录keyList扣减次数

redis 库存扣减_第1张图片

redis 库存扣减_第2张图片

1.2 并发请求

可以用消息队列或者其他方法测试,由于作者最近相关的项目,所以就用了消息队列。
如图,设置1500个线程并发访问(Jmeter)
redis 库存扣减_第3张图片

1.3 分别处理

redis 库存扣减_第4张图片

queue队列内使用简单数据类型String,只做简单的验证,queue1使用list操作,并且记录了有效扣减库存次数。预期目标为key=0,keyList为空。

1.4 执行结果

key的结果出错,多扣减了4次
redis 库存扣减_第5张图片

keyList内容为空
redis 库存扣减_第6张图片
两个queue的扣减次数分别为
redis 库存扣减_第7张图片

可见,不加锁的情况下redis 简单的数据增减是有问题的。

2.加锁操作

经多次验证,list的操作没有问题,下面主要验证加锁操作。

2.1数据准备

操作请参考1.1

2.2 修改key所在queue的代码

此处我们采用双检索的方式,避免全部走锁代码,稍微提升下并发性。

public void excute(String msg){
    int key = Integer.valueOf(redisTemplate.opsForValue().get("key").toString());
    if(0 < key){

		// 加锁,保证系统内同时只有一个线程能够进入代码块操作
        synchronized (Object.class){
            key = Integer.valueOf(redisTemplate.opsForValue().get("key").toString());
            if(0 < key){
                redisTemplate.opsForValue().increment("key",-1);
                System.out.println("key 执行次数:"+atomicInteger.addAndGet(1));
            }
        }
        
    }
}

2.3 并发访问

参考1.2

2.4 结果

本次key的操作是没有问题的
redis 库存扣减_第8张图片

3. 结论

高并发下,如果有资源性限制的话,必须进行访问控制,虽然list也能够达到库存控制的目的,但就程序设计上,并不是理想的解决方案,为了程序的可维护性,还是使用redis的数字操作,并且进行加锁。

你可能感兴趣的:(redis)