This article explores Valkey, an open-source Redis fork, as a powerful in-memory data store for enhancing application performance and scalability. It delves into advanced caching strategies like lazy loading and write-through, and demonstrates how Valkey's data structures can solve common distributed system problems such as the thundering herd problem in real-time analytics, rate limiting, and session management. The discussion emphasizes architectural decisions and trade-offs when integrating an in-memory cache layer to reduce database load and achieve sub-millisecond latencies.
Read original on InfoQ CloudValkey is an open-source, Redis-compatible fork managed under the Linux Foundation/CNCF, ensuring its long-term open-source commitment. It functions as an in-memory data store, offering significantly faster data retrieval compared to disk-based storage. The primary motivation for adopting an in-memory cache like Valkey is to reduce latency (achieving sub-millisecond response times) and alleviate pressure on primary data sources like relational databases (e.g., RDS) or NoSQL databases (e.g., DynamoDB). By offloading read traffic to a cache layer, applications can scale more effectively and cost-efficiently.
Implementing a cache layer involves choosing appropriate strategies based on data consistency and performance requirements. Two fundamental approaches are highlighted:
For invalidating cache entries, especially when using lazy loading, an asynchronous mechanism can be employed. For example, using database change data capture (CDC) (like DynamoDB Streams triggering AWS Lambda) to invalidate or update Valkey entries ensures that changes in the source of truth are reflected in the cache without directly coupling the write path.
Thundering Herd Problem
The thundering herd problem occurs in a distributed system when a popular cache entry expires, leading to many concurrent client requests simultaneously hitting the slower backend database. This can overwhelm the database, cause increased latency, and potentially lead to service degradation or outages.
Valkey provides mechanisms to mitigate the thundering herd problem. One effective strategy involves distributed locking within the cache. When a hot item's cache entry expires, the first client to request it acquires a lock (e.g., using `SET resource_key 'locked' NX PX timeout`). Subsequent clients encountering the locked status will wait for the lock to be released, or for the item to be repopulated by the client holding the lock. Once the data is refreshed from the origin database and stored back in Valkey, the lock is released, and all waiting clients can then retrieve the updated data directly from the cache. This prevents a stampede on the backend database.
CLIENT A: GET my_hot_key # Returns NIL
CLIENT A: SETNX lock:my_hot_key "locked" EX 10 # Acquires lock
# CLIENT A fetches from DB, updates cache
CLIENT A: SET my_hot_key "new_value"
CLIENT A: DEL lock:my_hot_key # Releases lock
CLIENT B: GET my_hot_key # Returns NIL
CLIENT B: SETNX lock:my_hot_key "locked" EX 10 # Fails to acquire lock, waits or retries
# ... later ...
CLIENT B: GET my_hot_key # Returns "new_value"Beyond general caching, Valkey's rich data structures enable more complex system design patterns. It's highly effective for implementing distributed rate limiting using data structures like sorted sets or counters to track request counts within time windows. For real-time analytics and leaderboards, sorted sets can maintain ordered data efficiently, allowing for quick retrieval of top performers. Furthermore, Valkey serves as an excellent choice for session stores, leveraging its fast key-value access to manage user sessions across distributed application instances.