Part 1: Translating Redis Data Structures Beyond Basic Caching

Why People Start with Redis and the Challenge It Brings

When developers chase lightning-fast data access, the journey almost always starts in RAM. Redis has become the go-to choice here - thanks to its simplicity: a few core data types and a lightweight command set. But as applications grow beyond basic caching into large-scale, distributed systems, a challenge emerges:

How do you preserve Redis-like speed while also gaining distributed SQL, ACID transactions, and parallel computation at a large scale?

The Redis Design

To understand that challenge, it helps to look at what the Redis website states,

“Redis is a data structure server. At its core, Redis provides a collection of native data types that help you solve a wide variety of problems.” 

Redis was designed from day one to be a data structure server rather than a general-purpose cache or distributed compute platform. Redis implements Hash, Set, Sorted Set, List etc. natively in its single-threaded event loop, which makes operations on them atomic, predictable, and blazing fast. So HMSET, SADD, ZADD, LPUSH etc. are not abstractions; they are core commands built into the Redis server. This design is why Redis shines for use cases like caching, session management and leaderboards.

The GridGain Design

GridGain was designed with a different set of primary use cases and hence different architecture. You get the responsiveness that developers love from Redis, but now with the power of distributed SQL, ACID transactions, co-located compute, and parallel data processing across the cluster. In other words, GridGain lets you model Redis-style data structures when you need them - while also giving you the flexibility to run complex analytics and transactional workloads at scale. So even though Redis and GridGain are built for different purposes, “in-memory” and “fast data access” makes the comparison unavoidable.

5 Signs You’ve Outgrown Your In-Memory Computing Platform

Is your in-memory platform straining under scale, costs, or complexity? Discover the 5 critical signs that signal it's time to upgrade to a unified, high-performance platform like GridGain.

Mapping Redis to GridGain

Since GridGain version 8 and 9 differ significantly in their architecture and classes, I prefer to show you how to map Redis structure in these 2 versions separately. In this blog, let’s take some examples of how to model Redis data structures in GridGain 8.

Simplest first- String:

SET user:1 "Anuja"

GET user:1

 

GridGain way in Java:

cache.put("user:1", "Anuja");

cache.get("user:1");

 

Hash:

HSET user:1 firstName "Anuja"

HSET user:1 lastName “Kumar”

HGETALL user:1

 

In GridGain, you can achieve this using object mapping as well as SQL.

IgniteCache<Integer, User> cache = ignite.cache("userCache");

User user = new User(1, "Anuja", "Kumar");

cache.put(user.getUserId(), user);

//Get the complete user from cache

User u = cache.get(1);

 

Note: You might feel userId being used as a key as well as a field in the User POJO as a repetition. It has its own advantages like being able to selectively fetch some fields as shown below. If userId is not in the POJO, you will need to map the cache key(userId) using QueryEntity.

// Get selected field/column instead of the whole object

SqlFieldsQuery sql = new SqlFieldsQuery("SELECT firstName FROM User WHERE userId = ?"

).setArgs(1);

 

List and Set:

RPUSH queueA "one"

RPUSH queueA "two"

LPOP queueA

 

GridGain 8 provides IgniteQueue and IgniteSet, implementations of java.util.concurrent.BlockingQueue and java.util.Set interface respectively, which also support all operations from the java.util.Collection interface.

//Create unbounded queue

IgniteQueue<String> queueCache = ignite.queue("queueA", 0, new CollectionConfiguration());

// RPUSH equivalent. It adds to tail

queueCache.put("one");

queueCache.put("two");

//LPOP equivalent. Removes from head and gives the value

String value = queueCache.poll();

 

Redis Set:

SADD setA “one” “two”

SISMEMBER setA “three”

 

GridGain modelling of set:

IgniteQueue<String> setCache = ignite.queue("setA", 0, new CollectionConfiguration());

setCache.add(“one”);

setCache.add(“two”);

setCache.contains(“three”);  //Returns 1 if exists, else returns 0.

 

Sorted Set:

ZADD leaderboard 100 "player1"

ZADD leaderboard 200 "player2"

ZRANGE leaderboard 0 -1 WITHSCORES

 

Similar to the Hashset modelling, we create a POJO with a player name and a score. Since we will be using the ORDER BY clause in the query, it is critical to index the score as well.

IgniteCache<String, LeaderboardEntry> zsetCache = ignite.cache("LeaderboardCache");

zsetCache.put("player1", new LeaderboardEntry(100, "player1"));

zsetCache.put("player2", new LeaderboardEntry(200, "player2"));

// ZRANGE 0 -1 WITHSCORES equivalent. We can use LIMIT clause to get “Top N”

SqlFieldsQuery query = new SqlFieldsQuery("SELECT player, score FROM LeaderboardEntry ORDER BY score DESC");

List<List<?>> results = cache.query(query).getAll();

 

Redis Streams are essentially an append-only log data structure with commands for producing, consuming, acknowledging, and grouping messages. ​​XADD auto-generates a unique ID for you if you specify the * character as the ID argument; and this is the commonly used way of adding to the stream. 

XADD mystream * field value

 

There can be multiple field-value pairs in each entry of a stream. We can mimic this XADD behaviour in GridGain8, where the key is the stream entry ID (time-sequence) and the value is the message.

IgniteCache<String, String> streamCache = ignite.getOrCreateCache("streamCache");

String id = System.currentTimeMillis() + "-" + UUID.randomUUID();

streamCache.put(id, "value");

 

We can pass min, max of time or - and + to indicate min and max, in which case we get all the entries of the stream from XRANGE command.

XRANGE mystream - +

 

We can mimic this using a query. We can even pass the timestamps in the WHERE clause.

SqlFieldsQuery query = new SqlFieldsQuery( "SELECT _key, _val FROM streamCache ORDER BY _key"); 

 

If you want the Redis-like behaviour of XREAD to read new events as they arrive, GridGain Continuous Queries can be used. They are stateful: they let you subscribe to changes in a cache, effectively giving you a live stream of updates tied directly to your data.

ContinuousQuery<String, String> query = new ContinuousQuery<>();

query.setLocalListener(events -> {

    for (CacheEntryEvent<? extends String, ? extends String> e : events) {

        System.out.println("New message: " + e.getKey() + " => " + e.getValue());

    }

});

 

Conclusion

Ultimately, these examples show that anything a developer can do with Redis core data structures, can be achieved with GridGain 8. In the next part of this series, we will see how to model the Redis data structures in GridGain 9.

Try GridGain for free
Stop guessing and start solving your real-time data challenges: Try GridGain for free to experience the ultra-low latency, scale, and resilience that powers modern enterprise use cases and AI applications.
Share this

Sections