Synchronization Across Distributed Servers: DB vs Redis vs Actor
In distributed systems, one of the most common and tricky challenges is ensuring synchronized access to shared resources across multiple server instances. For example, when a user initiates a money transfer, updates a shared task list, or interacts with a counter, the request may hit any instance in your cluster. Unless synchronized properly, this can result in race conditions, inconsistent state, or data corruption. Traditional Synchronization Techniques Developers typically reach for one of two well-known approaches: 1. Database Locking Using pessimistic locking (SELECT ... FOR UPDATE) or JPA's @Lock(PESSIMISTIC_WRITE): Simple and widely supported Ties up database connections Increases contention and slows down unrelated queries 2. Redis Locks Using a distributed lock pattern (e.g., Redlock via Redisson): Offloads lock state from the DB Requires Redis infrastructure Needs careful retry/backoff handling to be safe and reliable Both methods require additional complexity and infrastructure to maintain, especially at scale. A Simpler Alternative: Clustered Sharded Actors Thanks to spring-boot-starter-actor, there's a third option: actor-based synchronization with no external system required. The library integrates Apache Pekko cluster sharding into Spring Boot, enabling you to model shared entities as singleton actors across your cluster. If two concurrent requests targeting the same logical ID (like user-123) arrive at different servers, the actor system ensures that: Only one instance of the actor is created for that ID All messages for that ID are routed to and handled by that actor, sequentially This eliminates the need for locks or external coordination. Real-World Comparison: DB vs Redis vs Actor We compared three implementations of a synchronized counter increment: db: Uses pessimistic DB locking redis: Uses Redis-based distributed lock actor: Uses spring-boot-starter-actor’s sharded actor system Test Setup 3 Spring Boot instances (ports 8080, 8081, 8082) Each instance received 3,000 concurrent POST requests (total: 9,000) Each method was benchmarked independently using Apache Bench (ab) Final value was retrieved via: GET /counter/{method}/{counterId} Results Summary Method Counter Final Value External Infra Notes DB Lock 9,000 MySQL High DB load; Redis 9,000 Redis Required retry logic; slower throughput under load Actor 9,000 None High throughput, simple implementation Although many requests technically "failed" at the HTTP layer (due to thin responses or benchmarking artifacts), the counter logic worked perfectly in all cases. Why the Actor Model Is Effective Using actors for synchronization offers several advantages: Correctness: Each logical ID maps to a single actor that serializes access Simplicity: No retry logic or lock expiration handling Infrastructure-free: No Redis or lock coordination service needed Scalability: Naturally distributed across nodes without contention This model fits naturally into workloads where each entity (e.g., user, session, document) can be isolated and updated independently. How to Get Started Refer to the examples. Summary Feature DB Lock Redis Lock Actor External Infra Required Yes Yes No Lock Coordination Overhead High Medium Low Error-Prone Retry Logic No Yes No Throughput Under Load Degrades Moderate Stable Code Complexity Low Medium Low Horizontal Scalability Limited Good Excellent If you're building a distributed Spring Boot system and need reliable, low-latency synchronization without operational burden, consider giving the actor model a try. Source Code You can find the complete working example, including the benchmarking script, on GitHub:

In distributed systems, one of the most common and tricky challenges is ensuring synchronized access to shared resources across multiple server instances.
For example, when a user initiates a money transfer, updates a shared task list, or interacts with a counter, the request may hit any instance in your cluster. Unless synchronized properly, this can result in race conditions, inconsistent state, or data corruption.
Traditional Synchronization Techniques
Developers typically reach for one of two well-known approaches:
1. Database Locking
Using pessimistic locking (SELECT ... FOR UPDATE
) or JPA's @Lock(PESSIMISTIC_WRITE)
:
- Simple and widely supported
- Ties up database connections
- Increases contention and slows down unrelated queries
2. Redis Locks
Using a distributed lock pattern (e.g., Redlock via Redisson):
- Offloads lock state from the DB
- Requires Redis infrastructure
- Needs careful retry/backoff handling to be safe and reliable
Both methods require additional complexity and infrastructure to maintain, especially at scale.
A Simpler Alternative: Clustered Sharded Actors
Thanks to spring-boot-starter-actor
, there's a third option: actor-based synchronization with no external system required.
The library integrates Apache Pekko cluster sharding into Spring Boot, enabling you to model shared entities as singleton actors across your cluster.
If two concurrent requests targeting the same logical ID (like user-123
) arrive at different servers, the actor system ensures that:
- Only one instance of the actor is created for that ID
- All messages for that ID are routed to and handled by that actor, sequentially
This eliminates the need for locks or external coordination.
Real-World Comparison: DB vs Redis vs Actor
We compared three implementations of a synchronized counter increment:
-
db
: Uses pessimistic DB locking -
redis
: Uses Redis-based distributed lock -
actor
: Uses spring-boot-starter-actor’s sharded actor system
Test Setup
- 3 Spring Boot instances (ports 8080, 8081, 8082)
- Each instance received 3,000 concurrent POST requests (total: 9,000)
- Each method was benchmarked independently using Apache Bench (
ab
) - Final value was retrieved via:
GET /counter/{method}/{counterId}
Results Summary
Method | Counter Final Value | External Infra | Notes |
---|---|---|---|
DB Lock | 9,000 | MySQL | High DB load; |
Redis | 9,000 | Redis | Required retry logic; slower throughput under load |
Actor | 9,000 | None | High throughput, simple implementation |
Although many requests technically "failed" at the HTTP layer (due to thin responses or benchmarking artifacts), the counter logic worked perfectly in all cases.
Why the Actor Model Is Effective
Using actors for synchronization offers several advantages:
- Correctness: Each logical ID maps to a single actor that serializes access
- Simplicity: No retry logic or lock expiration handling
- Infrastructure-free: No Redis or lock coordination service needed
- Scalability: Naturally distributed across nodes without contention
This model fits naturally into workloads where each entity (e.g., user, session, document) can be isolated and updated independently.
How to Get Started
Refer to the examples.
Summary
Feature | DB Lock | Redis Lock | Actor |
---|---|---|---|
External Infra Required | Yes | Yes | No |
Lock Coordination Overhead | High | Medium | Low |
Error-Prone Retry Logic | No | Yes | No |
Throughput Under Load | Degrades | Moderate | Stable |
Code Complexity | Low | Medium | Low |
Horizontal Scalability | Limited | Good | Excellent |
If you're building a distributed Spring Boot system and need reliable, low-latency synchronization without operational burden, consider giving the actor model a try.
Source Code
You can find the complete working example, including the benchmarking script, on GitHub: