【并发】详解redis的incr、decr命令

一、前言

redis是一个单线程的服务,那么所有的命令肯定会排队被redis执行,redis提供的命令都是原子性的,百度搜索incr\decr就是说将对应的key+1,key-1的值重新set到redis中,而且很多都是认为incr\decr原子性的,那么现在就有一个问题,如果redis的key:a, value:100,那么100个线程并发执行decr操作,那么对应的key的value是不是应该为0?

按照百度的说法,应该是。-1、-1、-1、-1进行排队等待redis执行,那么总有100的-1,那么value也是100,那么值肯定也是减去100了,最后的值是0?

这样的话,是不是incr\decr可以做秒杀业务,不需要采用锁机制呢?

我们动手试试了下

package redisStockNumber;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @author : fangcong
 * @date : 2023/3/22 15:22
 * @description : 类作用
 **/
public class RedisUtils {


    private static JedisPool jedisPool;

    static {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(20000);
        jedisPool = new JedisPool(jedisPoolConfig,"127.0.0.1", 6379);
        jedisPool.getResource().set("testNumber", ""+2024);
    }

    public static void main(String[] args) {
        int number = 2024;
        for(int i = 0; i < number ; i++){
            Thread thread
                    = new Thread(() -> {
                Jedis currentJedis = jedisPool.getResource();
                currentJedis.decrBy("testNumber", 1);
            });
            thread.start();
        }
		// sleep堵塞等到所有的子线程执行完成。
		  try {
            Thread.sleep(60 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }	
        Jedis jedis = jedisPool.getResource();
        String testNumber = jedis.get("testNumber");
        System.out.println(testNumber);
    }

}

对应pom

     <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.6.0</version>
        </dependency>

引入一个jedis客户端来操作redis ,我们需要注意这里的jedis是一个非线程安全的类,因此可以使用jedispool对象来获得不同的线程客户端来模拟多个客户端。

我们得到的结果是0,这就说明了incr\decr是我们上述说的那种原理,在多个客户端并发下是线程安全的。

二、为什么不能使用incr\decr来做下单操作呢?

比如我们的库存量为1000?

那么如果并发数刚刚有1000个人来购买,那么并发执行1000次,这样库存量减去1000,就剩余0了。

但是如果有1200个人并发,那么就会并发执行1200次,那么库存量就为 -200了,那肯定就是超卖了,因此需要在decr之前进行判断库存量,如果库存量大于0,则进行下单。

Integer num = redis.getKey(a);
if(num > 0){
redis.decr(a,1);
}

但是这里有一个问题,还是之前说的,如果有1200个人并发,那么同时拿到的num不是最新的,因此需要加锁保证这里的num必须是最新的。

RLock lock =  redisson.tryLock(a);
lock.lock();
try{
	if(num > 0){
		redis.decr(a,1);
	}
 } finally {
        //解锁
        System.out.println("解锁..."+Thread.currentThread().getId());
        lock.unlock();
}

或者使用lua脚本,将业务逻辑封装成lua脚本来保证其执行原子性。

这里是先判断再去执行decr肯定有问题的啊,应该先执行decr再去判断就没问题了

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值