Play with Redis! Very powerful Redisson distributed collection, write 60% less code

2024.06.05

1 Introduction

Redisson is a Redis Java client and real-time data platform. It provides a more convenient and simpler way to use Redis. Redisson objects provide separation of concerns, allowing you to focus on data modeling and application logic.

In Java, we are provided with a wealth of collection classes, such as List, Set, Map, etc. These collection classes are very powerful and effective tools in stand-alone applications or single JVM processes. However, in a distributed system, data needs to be shared and synchronized between multiple JVM processes or nodes. To achieve this goal, Redisson provides many distributed collection implementations, such as RList, RSet, RMap, etc. These collection classes can seamlessly share data between multiple Redis nodes. By using Redisson, developers can add, delete, modify and query data in a distributed system just like using traditional Java collection classes, without having to worry about data synchronization and consistency between different nodes.

2. Practical Examples

2.1 Map Collection

The Java distributed Map object based on Redis implements the ConcurrentMap interface. This object is completely thread-safe.

  • RMap Class Relationship
public interface RMap<K, V> extends ConcurrentMap<K, V>, ...{}
  • 1.

All subsequent operations are based on the RedissonClient object

@Resource
private RedissonClient redisson ;
  • 1.
  • 2.
  • Synchronous data storage
RMap<String, User> map = redisson.getMap("user-list");
User preValue = map.put("1", new User(2L, "张三2", 22)) ;
User value = map.putIfAbsent("2", new User(2L, "李四", 33));
  • 1.
  • 2.
  • 3.
  • Fast data storage

If you do not need to return the previous value (old value), it is recommended to use the corresponding fast* method

RMap<String, User> map = redisson.getMap("user-list");
map.fastPut("1", new User(2L, "张三2", 22));
map.fastPutIfAbsent("2", new User(2L, "李四", 33));
map.fastRemove("1") ;
  • 1.
  • 2.
  • 3.
  • 4.

The above operation will not return the old value before the corresponding key.

  • Asynchronous data storage
RFuture<User> f1 = map.putAsync("1", new User(2L, "张三2", 22)) ;
RFuture<Boolean> f2 = map.fastPutAsync("2", new User(2L, "李四", 33)) ;
RFuture<Long> f3 = map.fastRemoveAsync("2") ;
  • 1.
  • 2.
  • 3.

The above operations correspond to the Redis data structure.

HASH Data StructureHASH Data Structure

  • Key binding Lock in Map collection

As we know above, the data stored in Map is a hash data structure. We can bind each key to the corresponding Lock/ReadWriteLock/Semaphore/CountDownLatch.

RMap<String, User> map = redisson.getMap("user-list") ;
RLock lock = map.getLock(key) ;
lock.lock() ;
try {
  System.out.printf("当前线程: %s, 当前时间: %d%n", Thread.currentThread().getName(), System.currentTimeMillis()) ;
  TimeUnit.SECONDS.sleep(3) ;
} finally {
  lock.unlock() ;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • Local Cache

Used to speed up read operations and avoid network round trips. It caches map entries on the Redisson side and performs read operations 45 times faster than ordinary implementations. The map object that supports local caching implements RLocalCachedMap, which extends the java.util.concurrent.ConcurrentMap interface. The object is fully thread-safe.

// 配置缓存策略
final LocalCachedMapOptions<String, User> LOCAL_CACHE = LocalCachedMapOptions.<String, User>defaults()
      // 缓存大小
      .cacheSize(200)
      // 缓存模式
      .storeMode(StoreMode.LOCALCACHE_REDIS)
      // 删除策略
      .evictionPolicy(EvictionPolicy.LRU) ;
// 获取指定key本地缓存      
RLocalCachedMap<String,User> localCachedMap = redisson.getLocalCachedMap("user-list", LOCAL_CACHE) ;
User user = localCachedMap.get("1") ;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

The local cache instance object also supports fast* and asynchronous methods, which will not be described here.

  • Event Listening

Redisson allows binding a listener to each RMap object, which allows tracking events on the data. As shown in the following table, listener classes and events

picturepicture

The following example:

RMap<String, User> map = redisson.getMap("user-list");
int deletedListener = map.addListener(new DeletedObjectListener() {
  @Override
  public void onDeleted(String name) {
    // ...
  }
});
int expredListener = map.addListener(new ExpiredObjectListener() {
  @Override
  public void onExpired(String name) {
    // ...
  }
});
int putListener = map.addListener(new MapPutListener() {
  @Override
  public void onPut(String name) {
    // ...
  }
});
int removeListener = map.addListener(new MapRemoveListener() {
  @Override
  public void onRemove(String name) {
    // ...
  }
});
// 删除监听器
map.removeListener(listenerId) ; // removeListener, putListener ...
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.

The above are common operations on Map collections.

2.2 Set Collection

The Redis-based Java Set object implements the java.util.Set interface. The object is completely thread-safe. The uniqueness of elements is maintained by comparing the element state. Redis limits the set size to 4 294 967 295 elements. Redis uses the serialized state to check the uniqueness of the value, rather than the hashCode()/equals() method of the value.

  • RSet Class Relationship
public interface RSet<V> extends Set<V>,...{}
  • 1.
  • Basic Operation
RSet<User> set = redisson.getSet("user-set");
set.add(new User(1L, "张三", 33)) ;
set.add(new User(2L, "李四", 55)) ;
  • 1.
  • 2.
  • 3.

The data structure used for storage in Redis is:

picture

RSet uses Set collection. Like RMap, RSet also supports synchronous and asynchronous data operations.

RFuture<Boolean> f1 = set.addAsync(new User(1L, "张三", 33)) ;
RFuture<Boolean> f2 = set.addAsync(new User(2L, "李四", 55)) ;
  • 1.
  • 2.
  • Binding Lock Operation
RSet<User> set = redisson.getSet("user-set") ;
RLock lock = set.getLock(new User(1L, "张三", 33)) ;
lock.lock() ;
try {
  // ...
} finally {
  lock.unlock() ;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • Deleting a policy

The current Redis implementation does not have set value removal. Therefore, expired data is purged by org.redisson.eviction.EvictionScheduler. It removes 300 expired entries at a time. If the clean task removes 300 items each time, it will be executed once a second (minimum execution delay). But if the current expired value is less than the previous one, the execution delay will increase by 1.5 times.

RSetCache<User> set = redisson.getSetCache("user-set") ;
set.add(new User(3L, "阴阳路", 66), 180L, TimeUnit.SECONDS) ;
  • 1.
  • 2.
  • Event Listening

Like Map, Set also has corresponding event listeners. For details, see the corresponding description in Map.

  • Set sorting

The Java distributed SortedSet based on Redis implements the java.util.SortedSet interface. This object is thread-safe. It uses a comparator to sort elements and maintain uniqueness. For string data types, it is recommended to use the LexSortedSet object to improve performance.

RSortedSet<Integer> set = redisson.getSortedSet("set-sort") ;
// 这里不可以写成lambda表达式:(o1, o2) -> Integer.compare(o1, o2)
set.trySetComparator(new Comparator<Integer>() {
  @Override
  public int compare(Integer o1, Integer o2) {
    return o1 > o2 ? 1 : (o1 < o2 ? -1 : 0)  ;
  }
}) ;
set.add(3) ;
set.add(1) ;
set.add(2) ;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

The following two keys are generated in redis:

picturepicture

The corresponding value of set-sort is:

picturepicture

2.3 List Collection

The Java distributed List object based on Redis implements the java.util.List interface. It stores elements in the order they were inserted. It has Async, Reactive, and RxJava3 interfaces. Redis limits the list size to 4 294 967 295 elements.

  • RList Class Relationship
public interface RList<V> extends List<V>, ... {}
  • 1.
  • Basic Operation
RList<User> list = redisson.getList("user-list");
User user = new User(1L, "张三", 10);
list.add(user) ;
User ret = list.get(0) ;
System.out.println("ret = " + ret) ;
list.remove(user) ;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • Event Listening
RList<User> list = redisson.getList("user-list") ;
list.addListener(new ExpiredObjectListener() {
  @Override
  public void onExpired(String name) {
    // ...
  }
}) ;
// 其它事件
/**
 * DeletedObjectListener
 * ListAddListener
 * ListInsertListener
 * ListSetListener
 * ListRemoveListener
 * ListTrimListener
 */
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

2.4 Queue

A Java distributed unbounded queue object based on Redis, which implements the java.util.Queue interface. The object is completely thread-safe. It has Async, Reactive and RxJava3 interfaces.

  • RQueue Class Relationship
public interface RQueue<V> extends Queue<V>, ... {}
  • 1.
  • Basic Operation
RQueue<User> queue = redisson.getQueue("user-queue");
queue.add(new User()) ;
// 获取但不删除
User u1 = queue.peek() ;
// 获取并删除
User u2 = queue.poll() ;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

Data structures used by redis:

picturepicture

  • Event Listening
RQueue<User> queue = redisson.getQueue("user-queue") ;
queue.addListener(new ExpiredObjectListener() {
  @Override
  public void onExpired(String name) {
    // ...
  }
}) ;
// 其它事件
/**
 * ListAddListener
 * ListInsertListener
 * ListRemoveListener
 */
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

2.5 Blocking Queues

A Java distributed unbounded BlockingQueue object based on Redis, which implements the java.util.concurrent.BlockingQueue interface. The object is completely thread-safe. It has Async, Reactive and RxJava3 interfaces.

  • Class Relationship
public interface RBlockingQueue<V> extends BlockingQueue<V>, ... {}
  • 1.
  • Basic Operation
RBlockingQueue<User> queue = redisson.getBlockingQueue("user-blockqueue");
queue.offer(new User(1L, "哈哈", 22)) ;
// queue.offer(new User(2L, "嘿嘿", 33)) ;


User u1 = queue.peek() ;
User u2 = queue.poll() ;
// 这里会被阻塞,最多等待10s队列中有元素则直接返回
User u3 = queue.poll(10, TimeUnit.SECONDS) ;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

Corresponding data structure used by redis:

picturepicture

2.6 Bounded Blocking Queues

The general usage is the same as above:

RBoundedBlockingQueue<SomeObject> queue = redisson.getBoundedBlockingQueue("user-capacity-queue");
// 设置容量大小
queue.trySetCapacity(2);


queue.offer(new User(1L, "张三", 20));
queue.offer(new User(2L, "李四", 10));
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

Redisson provides many distributed queue implementations, such as double-ended queues, priority queues, etc., which are not shown here one by one.