How KeyDB Transforms Redis into a Multi‑Threaded Database
This article explains how KeyDB, a Redis fork, redesigns the single‑threaded architecture into a multi‑threaded model with worker threads, per‑thread connection management, a custom fastlock mechanism, and an active‑replica mode that enables writable replicas and conflict‑resolution using timestamps.
Thread Model
KeyDB splits Redis's original single main thread into a main thread and multiple worker threads. Each worker is an I/O thread that listens on the port, accepts connections, reads data and parses the protocol. It uses SO_REUSEPORT so several threads can bind the same port, and each worker is bound to a CPU using SO_INCOMING_CPU. After parsing, each thread operates on memory data protected by a global lock. The main thread is also a worker (index 0) and performs tasks only the main thread can do, such as serverCron.
ServerCron tasks include:
Statistics processing
Client connection management
Database resize and reshard
AOF handling
Replication synchronization
Cluster mode tasks
Connection Management
In Redis all connections are handled by a single thread. KeyDB assigns each worker its own set of connections, storing them in thread‑local lists. A connection must be created, used, and destroyed within the same thread. Each connection gains an iel field indicating the handling thread.
KeyDB maintains three structures for connection management: clients_pending_write: per‑thread list of connections awaiting synchronous data transmission. clients_pending_asyncwrite: per‑thread list for asynchronous transmission. clients_to_close: global list of connections that need to be closed asynchronously.
Because some Redis APIs (e.g., pub/sub) involve different threads, data destined for a non‑local thread is placed in the asynchronous queue.
Lock Mechanism
KeyDB implements a spin‑lock‑like mechanism called fastlock. The core structures are:
struct ticket{
uint16_t m_active; // unlock +1
uint16_t m_avail; // lock +1
};
struct fastlock{
volatile struct ticket m_ticket;
volatile int m_pidOwner; // thread ID holding the lock
volatile int m_depth; // recursion depth
};Atomic operations (__atomic_load_2, __atomic_fetch_add, __atomic_compare_exchange) compare m_active and m_avail to acquire the lock. fastlock offers try_lock (fails immediately) and lock (busy‑wait with periodic sched_yield). In KeyDB, each client has a dedicated lock; if try_lock fails, the operation is deferred to the next event‑loop iteration.
Active‑Replica
KeyDB supports an active‑replica mode where replicas can be writable. Each replica has a UUID to avoid circular replication. Incremental commands are wrapped in an rreplay API with the local UUID. Keys and values carry a timestamp version (current time shifted left 20 bits plus a 44‑bit counter) for conflict detection; a write is rejected if a newer version already exists.
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.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
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.
