【并发】详解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();
        }
        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对象来获得不同的线程客户端来模拟多个客户端。

【并发】详解redis的incr、decr命令_第1张图片

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

二、官方介绍

【并发】详解redis的incr、decr命令_第2张图片
但是从官方介绍上其实也不太清楚该内存含义,如果按照官方介绍,我还是以为incr是 +1 、 +1 、+1进行执行。

三、猜想

因为写了一个测试文件,验证了incr并不是+1、+1这样的实现的,因此猜测
在使用 DECRBY 命令减少库存时,每个命令执行时会读取当前键的值,然后对这个值进行减少操作,最后将减少后的结果保存回 Redis 中。如果有多个客户端同时执行 DECRBY 命令,那么 Redis 会按照客户端发送命令的顺序执行这些命令,并且在执行命令时会保证读取到的值是最新的,从而确保每个客户端都在最新的库存量上进行减少操作。

也就是incr会。 读值+1 、 读值+1 ,然后redis会将读值+1这个结果重新执行一个类型set命令将对应的key的value覆盖成最新的。

因此在多线程下 可以存在多个客户端读的值会相同,而不是读的是最新的值。

四、总结

如果有人明确incr \decr底层是怎么样的一个实现逻辑请留言,麻烦谢谢了

你可能感兴趣的:(redis,java,数据库)