RiaChoi Text Logo.
Lock Strategy in MSA

Redis

MSA

Lock

Redisson

Lock Strategy in MSA

Learn about Distributed Lock and Concurrency Control

Ria ChoiMay 15th, 2026

Intro

Computers operate at nanosecond-level speeds.

In computer world, multiple servers can attempt to access the same data simultaneously, which can lead to issues.

In this article, we’ll explore how distributed locks work in Redis, the limitations of SETNX, and why tools like Redisson are widely used for concurrency control in modern backend systems.

 

Index

  1. What is Distributed Lock?
  2. Distributed Lock Strategies
    1. SETNX
      1. How SETNX Works
      2. Issues with SETNX
  3. Redis Locking Strategies
    1. TTL Strategy
      1. What happens if the TTL is Too Short?
      2. What happens if the TTL is Too Long?
    2. Lock Release Strategy
    3. Summary
  4. Concurrency Control
    1. DB Pessimistic Lock
      1. My Project: DB Pessimistic Lock in Payment Processing
    2. DB Optimistic Lock
    3. Basic Redis Distributed Lock
    4. Redisson
  5. Glossary
  6. Conclusion

 

What is Distributed Lock? 🔒

Imagine you want to use the office bathroom shared with your coworkers.

There’s one important rule:

only one person can use the bathroom at a time.

So you come up with a simple system.
If someone is using the bathroom, they lock the door with a key so nobody else can enter until they are done.

This idea may sound ordinary in real life, but the same concept becomes very important in software systems.

  • The bathrooom represents a shared resource or database,
  • the door lock represents a lock mechanism,
  • and the key represents permission to access the resource.

→ The main goal is to prevent multiple users or servers from modifying the same data at the same time.

People waiting for the locks
Waiting for the locks
🤔💭 Let’s Imagine

Imagine a limited-edition premium sneaker release where 100 users send purchase requests simultaneously.

If concurrency is not handled correctly, an overselling issue can occur.

Distributed lock can prevent this overselling issue and race condition

 

Distributed Lock Strategies

Early developers used Redis’s SETNX command to create this locking mechanism.

SETNX SET if Not eXists: Only set the value if the key does not already exist

 

If the operation succeeds, Redis returns 1, meaning the lock was successfully acquired.
If it fails, Redis returns 0, meaning another process or server already holds the lock.

 

SETNX

How SETNX Works
Server A and Server B
Server A and Server B

Let’s say Server A and Server B are trying to perform the same task at the same time.

Both servers attempt to acquire a lock through Redis.

SETNX lock:order:123 true

 

Server A reaches Redis first and successfully creates the lock.

lock:order:123 = true

Successful → Returns 1

“Server A acquired the lock!”

 

Right after that, Server B attempts to acquire the same lock.

However, the key already exists because Server A is currently holding it.

Failed → Returns 0

“Someone is already using the resource. I cannot access it right now.”

 

Issues with SETNX
Server A dies holding the key
Server A dies holding the key

However, right after acquiring the lock, Server A suddenly crashed.

lock:order:123 = true

This key could be left alone forever and no server could access the key anymore.

 

To solve this problem, developers introduced an expiration time using Redis options:

SET lock:order:123 true NX EX 10
  • NX → Only create the key if it does not already exist
  • EX 10 → Automatically delete the key after 10 seconds
“Create the lock, but automatically release it after 10 seconds.”

This prevents dead locks caused by crashed servers holding the lock indefinitely.

Dead Lock A deadlock occurs when two or more processes keep waiting for each other indefinitely, causing the system to stop making progress.

 

Redis Locking Strategies

TTL Strategy ⏲️

TTL Time To Live: The amount of time a key or lock remains valid before it is automatically deleted.

 

What happens if the TTL is Too Short?
Lock expiration set to 3 seconds
Task takes 10 seconds to complete

After 3 seconds, the lock is automatically released.

However, Server A is still processing the task.

At that moment, Server B can acquire the lock.

As a result, both servers end up working at the same time.

 

Mutual Exclusion Failure

Mutual Exclusion Preventing more than one process or server from accessing a resource at the same time.

Mutual Exclusion Failure A situation where multiple processes or servers enter the critical section simultaneously, even though only one should have access.

 

What happens if the TTL is Too Long?

If the TTL is set too long, users may continue seeing payment failure or lock timeout errors for an extended period of time.

For example, if a server crashes while holding a lock with a 1-minute TTL, no other server can process the payment until the lock expires.

As a result, users could be stuck seeing payment errors for an entire minute, even though the original server is no longer working.

Loading makes the user mad
Loading makes the user mad

 

Lock Release Strategy

SET lock:sneakers true NX EX 3

Let’s say a lock is configured to expire after 3 seconds, and Server A acquires the lock first.

However, Server A takes 5 seconds to complete its logic.
As a result, Redis automatically releases the lock after 3 seconds because the TTL has expired.

lock:sneakers released

Right after the lock is released, Server B successfully acquires the same lock.

SERVER B -> lock:sneakers = true

But Server A, unaware that its lock has already expired, finishes processing and executes DEL as part of the normal unlock process.

DEL lock:sneakers

The lock no longer belongs to Server A.
It now belongs to Server B.

 

As a result, Server A accidentally deletes Server B’s lock.

 

❗ Race Condition Occurs

Server B believes it is safely processing data within the lock system.
However, because its lock was unexpectedly deleted, Server C can now enter the critical section as well.

This completely breaks mutual exclusion.

 

⭐ Solution

 

To solve this problem, each server stores a unique UUID value inside the lock.

SET lock:sneakers "A_UUID_1234" NX EX 3 - O

 

When unlocking, the server must first verify ownership of the lock.

Now Server A checks:

“Does the current UUID still match mine?”
  • If the UUID matches → delete the lock
  • If the UUID does not match → do not delete it

This ensures that a server can only release its own lock.

 

Summary
  • Store a UUID when creating the lock
  • Verify the UUID before releasing the lock
  • Perform verification and deletion atomically using a Lua Script

 

To take the bathroom reference I used at the beginning of this post, this means everyone in the office gets to keep their unique key and can only break their own key.

 

Concurrency Control

Concurrency control is the process of preventing conflicts when multiple servers or users attempt to access or modify the same data at the same time.

It involves various locking strategies designed to ensure data consistency and safe concurrent processing.

 

DB Pessimistic Lock

“Do not allow anyone else to modify this data until my transaction is finished.”

 

How it works

While Server A reads the data and holds the lock,
Server B must wait until the lock is released.

Advantages
  • Extremely safe and reliable
Disadvantages
  • Very slow because every request must wait in line
  • Risk of deadlocks

Deadlock occurs when multiple transactions wait for each other indefinitely, causing the system to stop progressing.

Common Use Cases
  • Bank transfers
  • Account transactions
  • Core financial systems
Code Example
SELECT * FROM product
WHERE id = 1
FOR UPDATE;

 

My Project: DB Pessimistic Lock in Payment Processing

I personally participated in a project where we used a pessimistic locking mechanism during payment processing.

We built a food ordering application called TodayEats, and one of the biggest concerns was preventing duplicate payments when multiple requests were sent simultaneously for the same order.

To solve this problem, we implemented a DB pessimistic lock.

Scenario

Imagine two payment requests are sent at the exact same time for the same order.

POST /api/v1/orders/{orderId}/payments

In this situation, we lock the order row directly in the database.

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select o from Order o where o.id = :orderId")
Optional<Order> findByIdForUpdate(UUID orderId);
@Transactional
public PaymentResponse createPayment(UUID orderId, UUID userId) {

    Order order = orderRepository.findByIdForUpdate(orderId)
            .orElseThrow(() -> new CustomException("Order not found."));

    if (order.isPaid()) {
        throw new CustomException("Order has already been paid.");
    }

    Payment payment = paymentRepository.save(
            Payment.create(order, userId, order.getTotalPrice())
    );

    order.markAsPaid();

    return PaymentResponse.from(payment);
}

The key part is this SQL query

SELECT *
FROM orders
WHERE id = ?
FOR UPDATE;
How it works

If Server A acquires the lock first, Server B must wait until Server A’s transaction is completed.

This ensures that only one payment can be processed successfully for the same order.

Server A:
Read order → Acquire lock → Payment success → Update order status to PAID

Server B:
Wait → Read updated order → Detect PAID status → Payment rejected

 

If you want to know more about this project,

please check out my blog posting below.

>> Today Eats Backend Spring Project Link

 

DB Optimistic Lock

“Assume conflicts will not happen, and process the task first.”

 

How it works

Optimistic looking usually uses a version column.

version column
version column

Server A reads:

version = 3

When updating the data:

UPDATE product
SET stock = 9,
    version = 4
WHERE id = 1
AND version = 3;

 

❗ But what if another server has already changed the version?

The update fails;

“Another server modified the data first. Reload the latest data and try again.”

Advantages
  • Very fast because no lock is held during processing
Disadvantages
  • If conflicts happen frequently, updates can fail repeatedly
  • Requires many retries in high-concurrency environments

 

Basic Redis Distributed Lock

Redis is single-threaded, so it naturally processes commands one at a time.

Spin lock happens

Spin lock means repeatedly asking Redis whether the lock is available when the server fails to acquire it.

Spin Lock
Spin Lock
Disadvantages
  • When traffic is high, this can put heavy pressure on Redis.
  • It is also difficult to implement correctly.
  • You have to handle many things manually.
TTL
UUID validation
Lua Script
Lock expiration
Retry logic
Exception handling

 

Redisson (RLock)

A library that makes Redis distributed locks much easier to use.

It is widely used in Java and Spring applications.

Redisson solves many of the common problems found in basic Redis lock implementations.

 

Pub/Sub-Based Event Waiting

With a basic Redis lock, servers usually rely on spin locks:

“Is the lock available now?”
“What about now?”
“Now?”

This causes continuous polling requests to Redis.

However, Redisson works differently.

Instead of constantly checking Redis, it waits for an event:

“Wake me up when the lock is released.”

 

Advantages
  • Less CPU waste
  • Less pressure on Redis
  • Better performance

 

Watchdog 🐶

In a basic Redis lock, the lock may expire before the task is finished.

TTL = 3 seconds
Task duration = 10 seconds

However, Redisson includes a Watchdog mechanism that automatically extends the TTL while the task is still running.

This helps prevent unexpected lock expiration during long-running processes.

 

Why Is Redisson Commonly Used for First-Come-First-Serve Coupon Systems? 🎫

Limited-time coupon events can receive thousands of requests simultaneously.

If a DB lock is used in this situation, the database can become overwhelmed.

However, Redis + Redisson provides:

  • Fast performance
  • High stability
  • Automatic TTL management
  • Efficient waiting mechanism
Lock Strategy Comparison
Lock Strategy Comparison

 

Glossary 🌟

  • Concurrency Control → Techniques used to prevent conflicts when multiple servers or users access the same data simultaneously
  • Distributed Lock → A locking mechanism used across multiple servers to safely control access to shared resources
  • Race Condition → An issue occurs when multiple processes modify the same data at the same time
  • Deadlock → A situation where processes wait for each other indefinitely
  • Spin Lock → Continuously retrying lock acquisition until the lock becomes available
  • TTL (Time To Live) → The duration before a lock or key automatically expires
  • Mutual Exclusion → Ensuring only one process can access a critical resource at a time
  • Pessimistic Lock → A lock strategy that blocks others until the current transaction finishes
  • Optimistic Lock → A lock strategy that assumes conflicts are rare and validates data changes later
  • UUID → A unique identifier used to verify lock ownership
  • Lua Script → A script executed atomically in Redis, commonly used for safe lock release
  • Watchdog → A mechanism in Redisson that automatically extends lock expiration while a task is running

 

💌 Conclusion

Distributed locks are essential for building safe and reliable high-concurrency systems in modern backend applications.

This learning journey was heavily inspired by and learned from Tutor Seok Jinhee at the Sparta Coding Club Boot Camp.

 

Share

Related contents

5 minutes Redis5 minutes Redis

May 13th, 2026