Backend Development 7 min read

Mastering Redis Optimistic Lock in Spring Boot: A Hands‑On Guide

This tutorial explains how Redis optimistic locking works, demonstrates the WATCH, MULTI, EXEC, and DISCARD commands with live examples, and shows how to integrate the mechanism into a Spring Boot application using retry logic to handle concurrent updates safely.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Redis Optimistic Lock in Spring Boot: A Hands‑On Guide

1. Introduction

Redis optimistic lock is a concurrency control method that assumes concurrent transactions do not interfere and can operate on their own data without acquiring locks. It works by watching keys and aborting the transaction if a watched key changes before execution.

2. Redis client demonstration

WATCH

<code>127.0.0.1:6379> help watch
WATCH key [key ...]
summary: Observe given keys to determine execution of MULTI/EXEC block
since: 2.2.0
group: transactions</code>

MULTI

<code>127.0.0.1:6379> help multi
MULTI -
summary: Mark the start of a transaction block
since: 1.2.0
group: transactions</code>

After the MULTI command, subsequent commands are queued and will only be executed when EXEC is called or discarded with DISCARD.

EXEC

<code>127.0.0.1:6379> help exec
summary: Execute all commands issued after MULTI
since: 1.2.0
group: transactions</code>

DISCARD

<code>127.0.0.1:6379> help discard
summary: Discard all commands issued after MULTI
since: 2.0.0
group: transactions</code>

MULTI / EXEC / DISCARD demo

<code>127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET a 1
QUEUED
127.0.0.1:6379(TX)> INCR a
QUEUED
127.0.0.1:6379(TX)> SET b 1
QUEUED
127.0.0.1:6379(TX)> EXEC
1) OK
2) (integer) 2
3) OK</code>

All commands are queued after MULTI and executed sequentially by EXEC.

WATCH demo

Open two terminal windows. In the first window execute WATCH, MULTI and queue commands without calling EXEC. In the second window increment the watched key, causing the transaction in the first window to fail when EXEC is finally called.

<code># Window 1
127.0.0.1:6379> SET version 1
OK
127.0.0.1:6379> WATCH version
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET a 1
QUEUED
127.0.0.1:6379(TX)> SET b 1
QUEUED
# (do not EXEC yet)

# Window 2
127.0.0.1:6379> INCR version
(integer) 2

# Back to Window 1
127.0.0.1:6379(TX)> EXEC
(nil)
127.0.0.1:6379> KEYS *
1) "version"</code>

The EXEC returns nil because the watched key was modified, demonstrating the optimistic lock behavior.

3. Application in Spring Boot

Dependency

<code>&lt;dependency&gt;
  &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
  &lt;artifactId&gt;spring-boot-starter-data-redis&lt;/artifactId&gt;
&lt;/dependency&gt;</code>

Redis configuration (application.yml)

<code>spring:
  redis:
    host: localhost
    port: 6379
    password:
    lettuce:
      pool:
        maxActive: 8
        maxIdle: 100
        minIdle: 10
        maxWait: -1</code>

Code implementation

<code>@Resource
private StringRedisTemplate stringRedisTemplate;

// Retry when IllegalStateException occurs, up to 3 attempts
@Retryable(maxAttempts = 3, value = {IllegalStateException.class})
public List&lt;Object&gt; watch() {
  List&lt;Object&gt; ret = stringRedisTemplate.execute(new SessionCallback&lt;List&lt;Object&gt;&gt;() {
    @Override
    public List&lt;Object&gt; execute(RedisOperations opt) throws DataAccessException {
      // watch the key
      opt.watch("v");
      // start transaction block
      opt.multi();
      opt.opsForValue().increment("v");
      // execute queued commands
      return opt.exec();
    }
  });
  // If the transaction was discarded, ret will be null/empty, trigger retry
  if (ret == null || ret.isEmpty()) {
    System.err.printf("Retry...%n");
    throw new IllegalStateException("Data was modified");
  }
  return ret;
}
</code>

Summary: By using Redis's WATCH mechanism together with Spring Boot's transaction support and a retry framework, you can implement an optimistic lock that safely handles concurrent updates. The lock aborts the transaction if the watched key changes, requiring a retry that may affect performance, so it should be used after careful consideration of the business scenario.

JavatransactionRedisSpring Bootoptimistic lock
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.