Mastering Distributed Locks: Database, Redis, and Zookeeper Implementations Compared

This article explains three practical ways to implement distributed locks—using a relational database, Redis cache, and Zookeeper—detailing their underlying mechanisms, Java code examples, performance trade‑offs, and a side‑by‑side comparison of strengths and weaknesses.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Mastering Distributed Locks: Database, Redis, and Zookeeper Implementations Compared

Overview

Distributed locks are essential for coordinating access to shared resources in a distributed system. The article presents three common implementation approaches: (1) database‑based locks, (2) cache‑based locks using Redis, and (3) Zookeeper‑based locks, each accompanied by Java code samples and practical considerations.

1. Database‑Based Distributed Lock

Pessimistic lock : Uses SELECT ... FOR UPDATE to acquire an exclusive row lock. The lock column must be indexed; otherwise, the whole table may be locked.

Optimistic lock : Relies on a version field and CAS semantics. The lock is represented by an incrementing version number; if an UPDATE fails due to version mismatch, a conflict is detected. This technique is often used in flash‑sale or seckill scenarios.

2. Redis‑Based Distributed Lock

Key commands : SETNX key value – sets the key only if it does not exist. EXPIRE key seconds – sets a timeout to avoid deadlocks. DEL key – releases the lock.

Implementation idea :

Attempt to acquire the lock with SETNX and immediately set an expiration.

Store a randomly generated UUID as the lock value for later verification.

If acquisition fails, retry until a configurable timeout expires.

When releasing, compare the stored UUID and delete the key only if it matches.

Sample code (Java) :

public class DistributedLock {
    private final JedisPool jedisPool;
    public DistributedLock(JedisPool jedisPool) { this.jedisPool = jedisPool; }
    public String lockWithTimeout(String lockName, long acquireTimeout, long timeout) {
        Jedis conn = null; String retIdentifier = null;
        try {
            conn = jedisPool.getResource();
            String identifier = UUID.randomUUID().toString();
            String lockKey = "lock:" + lockName;
            int lockExpire = (int) (timeout / 1000);
            long end = System.currentTimeMillis() + acquireTimeout;
            while (System.currentTimeMillis() < end) {
                if (conn.setnx(lockKey, identifier) == 1) {
                    conn.expire(lockKey, lockExpire);
                    return identifier;
                }
                if (conn.ttl(lockKey) == -1) { conn.expire(lockKey, lockExpire); }
                Thread.sleep(10);
            }
        } catch (JedisException | InterruptedException e) { e.printStackTrace(); }
        finally { if (conn != null) conn.close(); }
        return retIdentifier;
    }
    public boolean releaseLock(String lockName, String identifier) {
        Jedis conn = null; boolean retFlag = false;
        try {
            conn = jedisPool.getResource();
            String lockKey = "lock:" + lockName;
            while (true) {
                conn.watch(lockKey);
                if (identifier.equals(conn.get(lockKey))) {
                    Transaction tx = conn.multi();
                    tx.del(lockKey);
                    if (tx.exec() != null) { retFlag = true; }
                }
                conn.unwatch();
                break;
            }
        } catch (JedisException e) { e.printStackTrace(); }
        finally { if (conn != null) conn.close(); }
        return retFlag;
    }
}

A test harness creates 50 threads that each call seckill(), which obtains the lock, decrements a shared counter, and releases the lock. The ordered output confirms correct locking.

3. Zookeeper‑Based Distributed Lock

Zookeeper provides a hierarchical namespace where only one ephemeral sequential node can exist under a given path, enabling a lock protocol:

Create a parent node /mylock.

Each contender creates an ephemeral sequential child node.

The node with the smallest sequence number holds the lock.

Other contenders watch the next‑smaller node; when it disappears, they re‑evaluate.

The Apache Curator library simplifies this pattern. The InterProcessMutex class implements the lock with acquire() and release() methods.

Sample code (Java with Curator) :

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;

public class ZkLock implements DistributionLock {
    private String zkAddress = "zk_address";
    private static final String root = "package_root";
    private CuratorFramework zkClient;
    private final String LOCK_PREFIX = "/lock_";

    @Bean
    public DistributionLock initZkLock() {
        if (StringUtils.isBlank(root)) { throw new RuntimeException("zookeeper 'root' can't be null"); }
        zkClient = CuratorFrameworkFactory.builder()
                .connectString(zkAddress)
                .retryPolicy(new RetryNTimes(2000, 20000))
                .namespace(root)
                .build();
        zkClient.start();
        return this;
    }

    public boolean tryLock(String lockName) {
        String path = LOCK_PREFIX + lockName;
        try {
            Stat stat = zkClient.checkExists().forPath(path);
            if (stat == null) {
                zkClient.create().creatingParentsIfNeeded()
                        .withMode(CreateMode.EPHEMERAL)
                        .forPath(path, "1".getBytes());
                return true;
            }
        } catch (Exception e) { /* handle */ }
        return false;
    }

    public void release(String lockName) {
        String path = LOCK_PREFIX + lockName;
        try { zkClient.delete().guaranteed().deletingChildrenIfNeeded().forPath(path); }
        catch (Exception e) { /* handle */ }
    }
}

Advantages: high availability, re‑entrancy, and built‑in failure handling. Drawbacks: higher latency than Redis because each lock operation requires a write to the Zookeeper leader and propagation to followers.

4. Comparative Analysis

Database lock – Simple but suffers from poor performance, risk of table‑level locking, and high CPU usage due to polling.

Redis lock – Fast and widely used; however, lock release may fail and expiration handling can be tricky.

Zookeeper lock – Offers strong consistency and reliability, but performance is lower than Redis because of extra write overhead.

Complexity ranking (low to high): Database < Database < Redis < Zookeeper. Performance ranking (high to low): Redis > Zookeeper ≥ Database. Reliability ranking (high to low): Zookeeper > Redis > Database.

Conclusion

Choosing a distributed lock solution depends on the specific requirements of latency, consistency, and operational complexity. Redis provides the best performance for most high‑throughput scenarios, while Zookeeper excels in reliability and strong consistency, and database locks are suitable only for simple or legacy environments.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavadatabaseconcurrencyRedisZooKeeperDistributed Lock
Java Architect Essentials
Written by

Java Architect Essentials

Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.

0 followers
Reader feedback

How this landed with the community

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.