Implementing Distributed Locks with Redis and Redisson in Spring Boot
This article explains the challenges of high‑concurrency inventory deduction, demonstrates why simple synchronized locks fail in distributed environments, and walks through multiple solutions—from basic SETNX locks to Redisson’s advanced distributed lock implementation—highlighting pitfalls, expiration handling, and trade‑offs with alternatives like Zookeeper.
In high‑concurrency scenarios such as flash sales, multiple clients may simultaneously read the same stock value from Redis, causing the final stock count to be incorrect. The article first illustrates this problem with a simple Spring Boot controller that reads and decrements a Redis key.
package com.wangcp.redisson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping(value = "/duduct_stock")
public String deductStock(){
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if(stock > 0){
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("扣减成功,剩余库存:" + realStock);
} else {
System.out.println("扣减失败,库存不足");
}
return "end";
}
}Because the synchronized keyword only provides a JVM‑level lock, it cannot guarantee consistency when the application is deployed across multiple instances behind a load balancer. The article then shows a synchronized version and explains its limitation in a clustered environment.
@RequestMapping(value = "/duduct_stock")
public String deductStock(){
synchronized (this){
// same stock logic as above
}
return "end";
}To achieve true distributed locking, the article introduces the SETNX command as a basic lock, followed by an improved version that adds a try‑finally block to avoid deadlocks when exceptions occur.
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "wangcp");
if(!result){
return "error_code";
}
// business logic
finally {
stringRedisTemplate.delete(lockKey);
}Even with expiration time, the lock can be released by another request if the original holder crashes before releasing it. The article therefore presents a Redisson‑based solution that automatically handles lock renewal and safe release.
package com.wangcp.redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@Autowired
private RedissonClient redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping(value = "/duduct_stock")
public String deductStock(){
String lockKey = "product_001";
RLock redissonLock = redisson.getLock(lockKey);
try {
redissonLock.lock();
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if(stock > 0){
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("扣减成功,剩余库存:" + realStock);
} else {
System.out.println("扣减失败,库存不足");
}
} finally {
redissonLock.unlock();
}
return "end";
}
}The underlying Redisson implementation uses a Lua script to guarantee atomicity when acquiring the lock, and a background task that periodically renews the lock’s TTL, preventing premature expiration.
private void scheduleExpirationRenewal(final long threadId){
// schedule a timer that calls a Lua script to extend the TTL
}Despite these improvements, the article points out remaining issues such as lock loss during master‑slave failover and the fact that a distributed lock serializes concurrent requests, potentially hurting throughput. It discusses the CAP theorem, suggesting Zookeeper for stronger consistency at the cost of lower QPS, while Redis (or Redisson) offers higher performance with occasional tolerable inconsistencies.
Finally, the article summarizes practical recommendations: use unique lock values to avoid releasing others’ locks, choose appropriate expiration times, consider lock renewal strategies, and evaluate whether a distributed lock is truly needed for a given use case.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
