使用 Redis 缓存处理竞争条件

Handling race conditions with Redis Cache

我正在研究一个问题,我有一个包含字符串键和计数器的对象。每次访问对象时,计数器都会递减。这带来了竞争条件的问题。为了处理这个问题,我使用同步关键字

编写了以下代码
try {
            option = cartRepository.findById(url);
            Rate rate = option.get();
            synchronized (this) {
                if(rate.getRate() > 0) {
                    rate.decRate(1);
                    allow = true;
                    cartRepository.save(rate);
                } 
            }
        } catch(NoSuchElementException e) {
            cartRepository.save(new Rate(url, 5));
            allow = true;
        } 

我想知道,redis 本身是否具有每次访问该键时递增或递减计数器的功能。 文档有点混乱。它确实讨论了键的自动递增。但我猜,它不是用钥匙和计数器创建一个两部分的钥匙。但是每次创建和保存新对象时都会自动增加一个键。

你可以用一把钥匙来保持计数器并使用INCR命令来改变计数器的值。此命令returns执行操作后的计数器值。

您可以在Spring Data Redis 中使用ValueOperations 来发出此命令。这是示例代码:

StringRedisTemplate rt = new StringRedisTemplate(factory);

ValueOperations<String, String> valueOps = rt.opsForValue();

valueOps.set(KEY, "10");
System.out.println(valueOps.get(key));
System.out.println(valueOps.increment(KEY, 1));
System.out.println(valueOps.increment(KEY, 1));
System.out.println(valueOps.increment(KEY, -1));

输出将是:

10
11
12
11

如果您有很多计数器需要维护,您可以将它们存储在 hashmap 中并使用 HINCRBY command. With Spring Data Redis you can use HashOperations to manipulate values in the hash. See this page 以获得更详细的解释。

感谢 Sergie,这是工作代码

public boolean isAllowed(String url, String maxRate) {
        logger.info("URL is "+url);
        boolean allow = false;
        ValueOperations<String, String> valueOps = redisTemplate.opsForValue();
        String rate = valueOps.get(url);
        logger.info("RATE "+rate);
        if(rate == null) {
            valueOps.set(url, maxRate, 5, TimeUnit.SECONDS);
            allow = true;
        } else {
            valueOps.decrement(url, 1);
            if(Integer.parseInt(rate) > 0) {
                allow = true;
            }
        }
        return allow;
    }