Redission 分布式集合 MAP

765 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情

Redission 分布式集合 MAP

映射(Map)

分布式映射结构的 RMap 对象实现了java.util.concurrent.ConcurrentMap接口和java.util.Map接口。与HashMap不同的是,RMap保持了元素的插入顺序。该对象的最大容量受Redis限制,最大元素数量是4 294 967 295个。

基本使用

  • put 返回值:先前关联值
  • putIfAbsent 不存在就插入,返回值:如果键是哈希中的新键并且设置了值,则为null。如果键已存在于哈希中且尚未进行更改,则返回上一个值
  • remove 移除键
  • fastPut 工作速度比put(Object,Object)快,但不返回与键关联的前一个值
  • fastRemove 工作速度比put(Object,Object)快,但不返回与键关联的值
@Test
public void test16(){
    RMap<String, Object> map = redisson.getMap("anyMap");

    // put 返回值:先前关联值
    Object prevObject = map.put("123", 11);
    System.out.println(prevObject); // null

    // putIfAbsent 不存在就插入,返回值:如果键是哈希中的新键并且设置了值,则为null。如果键已存在于哈希中且尚未进行更改,则返回上一个值
    Object currentObject = map.putIfAbsent("123", 11);
    System.out.println(currentObject); // 11

    // remove 移除键
    Object obj = map.remove("123");
    System.out.println(obj);

    // 在不需要旧值的情况下可以使用fast为前缀的类似方法
    // fastPut 工作速度比put(Object,Object)快,但不返回与键关联的前一个值
    map.fastPut("321", 22);
    // fastRemove 工作速度比put(Object,Object)快,但不返回与键关联的值
    map.fastRemove("321");
}

普通字段锁&读写锁

  • getLock 获取锁
  • lock 上锁
  • unlock 解锁
  • getReadWriteLock 获取读写锁
  • readLock().lock() 上读锁
  • readLock().unlock() 解读锁
@Test
public void test17() throws InterruptedException {
    RMap<String, Object> map = redisson.getMap("anyMap");

    // getLock 获取锁
    String k = "kk";
    RLock keyLock = map.getLock(k);


    // lock 上锁
    keyLock.lock();
    try {
        Object v = map.get(k);
        // 其他业务逻辑
    } finally {
        // unlock 解锁
        keyLock.unlock();
    }

    // getReadWriteLock 获取读写锁
    RReadWriteLock rwLock = map.getReadWriteLock(k);

    // readLock().lock() 上读锁
    rwLock.readLock().lock();
    try {
        Object v = map.get(k);
        // 其他业务逻辑
    } finally {
        // readLock().unlock() 解读锁
        rwLock.readLock().unlock();
    }

}

元素淘汰(Eviction)

元素淘汰(Eviction)机制的映射类允许针对一个映射中每个元素单独设定 有效时间最长闲置时间

RMapCache Java对象在基于RMap的前提下实现了针对单个元素的淘汰机制。同时仍然保留了元素的插入顺序。由于RMapCache是基于RMap实现的,使它同时继承了java.util.concurrent.ConcurrentMap接口和java.util.Map接口。Redisson提供的Spring Cache整合以及JCache正是基于这样的功能来实现的。

目前的Redis自身并不支持散列(Hash)当中的元素淘汰,因此所有过期元素都是通过org.redisson.EvictionScheduler实例来实现定期清理的。为了保证资源的有效利用,每次运行最多清理300个过期元素。任务的启动时间将根据上次实际清理数量自动调整,间隔时间趋于1秒到1小时之间。比如该次清理时删除了300条元素,那么下次执行清理的时间将在1秒以后(最小间隔时间)。一旦该次清理数量少于上次清理数量,时间间隔将增加1.5倍。

  • ttl key\value条目的生存时间。如果0则无限存储
  • maxIdleTime 键\值输入的最大空闲时间。如果为0,则最大空闲时间不会影响条目过期
@Test
public void test18(){
    RMapCache<String, Object> map = redisson.getMapCache("anyMap");
    // ttl key\value条目的生存时间。如果0则无限存储
    // maxIdleTime 键\值输入的最大空闲时间。如果为0,则最大空闲时间不会影响条目过期
    // 有效时间 ttl = 10分钟
    map.put("key1", "a", 10, TimeUnit.MINUTES);
    // 有效时间 ttl = 10分钟, 最长闲置时间 maxIdleTime = 10秒钟
    map.put("key1", "b", 10, TimeUnit.MINUTES, 10, TimeUnit.SECONDS);

    // 有效时间 = 3 秒钟
    map.putIfAbsent("key2", "a", 3, TimeUnit.SECONDS);
    // 有效时间 ttl = 40秒钟, 最长闲置时间 maxIdleTime = 10秒钟
    map.putIfAbsent("key2", "b", 40, TimeUnit.SECONDS, 10, TimeUnit.SECONDS);
}

本地缓存(LocalCache)

本地缓存(Local Cache)也叫就近缓存(Near Cache)。这类映射的使用主要用于在特定的场景下,映射缓存(MapCache)上的高度频繁的读取操作,使网络通信都被视为瓶颈的情况。Redisson与Redis通信的同时,还将部分数据保存在本地内存里。这样的设计的好处是它能将读取速度提高最多 45倍 。 所有同名的本地缓存共用一个订阅发布话题,所有更新和过期消息都将通过该话题共享。

LocalCachedMapOptions options = LocalCachedMapOptions.defaults()
      // 用于淘汰清除本地缓存内的元素
      // 共有以下几种选择:
      // LFU - 统计元素的使用频率,淘汰用得最少(最不常用)的。
      // LRU - 按元素使用时间排序比较,淘汰最早(最久远)的。
      // SOFT - 元素用Java的WeakReference来保存,缓存元素通过GC过程清除。
      // WEAK - 元素用Java的SoftReference来保存, 缓存元素通过GC过程清除。
      // NONE - 永不淘汰清除缓存元素。
     .evictionPolicy(EvictionPolicy.NONE)
     // 如果缓存容量值为0表示不限制本地缓存容量大小
     .cacheSize(1000)
      // 以下选项适用于断线原因造成了未收到本地缓存更新消息的情况。
      // 断线重连的策略有以下几种:
      // CLEAR - 如果断线一段时间以后则在重新建立连接以后清空本地缓存
      // LOAD - 在服务端保存一份10分钟的作废日志
      //        如果10分钟内重新建立连接,则按照作废日志内的记录清空本地缓存的元素
      //        如果断线时间超过了这个时间,则将清空本地缓存中所有的内容
      // NONE - 默认值。断线重连时不做处理。
     .reconnectionStrategy(ReconnectionStrategy.NONE)
      // 以下选项适用于不同本地缓存之间相互保持同步的情况
      // 缓存同步策略有以下几种:
      // INVALIDATE - 默认值。当本地缓存映射的某条元素发生变动时,同时驱逐所有相同本地缓存映射内的该元素
      // UPDATE - 当本地缓存映射的某条元素发生变动时,同时更新所有相同本地缓存映射内的该元素
      // NONE - 不做任何同步处理
     .syncStrategy(SyncStrategy.INVALIDATE)
      // 每个Map本地缓存里元素的有效时间,默认毫秒为单位
     .timeToLive(10000)
      // 或者
     .timeToLive(10, TimeUnit.SECONDS)
      // 每个Map本地缓存里元素的最长闲置时间,默认毫秒为单位
     .maxIdle(10000)
      // 或者
     .maxIdle(10, TimeUnit.SECONDS);
RLocalCachedMap<String, Object> map = redisson.getLocalCachedMap("test", options);

Object prevObject = map.put("123", 1);
Object currentObject = map.putIfAbsent("323", 2);
Object obj = map.remove("123");

// 在不需要旧值的情况下可以使用fast为前缀的类似方法
map.fastPut("a", 1);
map.fastPutIfAbsent("d", 32);
map.fastRemove("b");

map.clearLocalCache();

数据分片(Sharding)

数据分片(Sharding)类仅适用于Redis集群环境下,因此带有数据分片(Sharding)功能的映射也叫集群分布式映射。它利用分库的原理,将单一一个映射结构切分为若干个小的映射,并均匀的分布在集群中的各个槽里。这样的设计能使一个单一映射结构突破Redis自身的容量限制,让其容量随集群的扩大而增长。在扩容的同时,还能够使读写性能和元素淘汰处理能力随之成线性增长。

接口名称 中文名称RedissonClient 对应的构造方法本地缓存功能 Local Cache数据分片功能 Sharding元素淘汰功能 Eviction
RMap 映射getMap()  NoNoNo
RMapCache 映射缓存getMapCache()  NoNoYes
RLocalCachedMap 本地缓存映射getLocalCachedMap()  YesNoNo
RLocalCachedMap Cache 本地缓存映射缓存 仅限于Redisson PRO版本getLocalCachedMapCache()YesNoYes
RClusteredMap 集群分布式映射存 仅限于Redisson PRO版本getClusteredMap()NoYesNo
RClusteredMapCache 集群分布式映射缓存存 仅限于Redisson PRO版本getClusteredMapCache()NoYesYes
RClusteredLocal CachedMap 集群分布式本地缓存映射存 仅限于Redisson PRO版本getClusteredLocal CachedMap()YesYesNo
RClusteredLocal CachedMapCache 集群分布式本地缓存映射缓存存 仅限于Redisson PRO版本getClusteredLocal CachedMapCache()YesYesYes

映射监听器(Map Listener)

为所有实现了RMapCacheRLocalCachedMapCache接口的对象提供了监听以下事件的监听器:

事件 | 监听器
元素 添加 事件 | org.redisson.api.map.event.EntryCreatedListener
元素 过期 事件 | org.redisson.api.map.event.EntryExpiredListener
元素 删除 事件 | org.redisson.api.map.event.EntryRemovedListener
元素 更新 事件 | org.redisson.api.map.event.EntryUpdatedListener

@Test
public void test21(){
    RMapCache<String, Integer> map = redisson.getMapCache("myMap");

    int updateListener = map.addListener(new EntryUpdatedListener<Integer, Integer>() {
        @Override
        public void onUpdated(EntryEvent<Integer, Integer> event) {
            event.getKey(); // 字段名
            event.getValue(); // 新值
            event.getOldValue(); // 旧值
            // ...
        }
    });

    int createListener = map.addListener(new EntryCreatedListener<Integer, Integer>() {
        @Override
        public void onCreated(EntryEvent<Integer, Integer> event) {
            event.getKey(); // 字段名
            event.getValue(); // 值
            // ...
        }
    });

    int expireListener = map.addListener(new EntryExpiredListener<Integer, Integer>() {
        @Override
        public void onExpired(EntryEvent<Integer, Integer> event) {
            event.getKey(); // 字段名
            event.getValue(); // 值
            // ...
        }
    });

    int removeListener = map.addListener(new EntryRemovedListener<Integer, Integer>() {
        @Override
        public void onRemoved(EntryEvent<Integer, Integer> event) {
            event.getKey(); // 字段名
            event.getValue(); // 值
            // ...
        }
    });

    map.removeListener(updateListener);
    map.removeListener(createListener);
    map.removeListener(expireListener);
    map.removeListener(removeListener);
}

LRU有界映射

以LRU为驱逐策略的分布式LRU有界映射对象。顾名思义,分布式LRU有界映射允许通过对其中元素按使用时间排序处理的方式,主动移除超过规定容量限制的元素。

RMapCache<String, String> map = redisson.getMapCache("map");
// 尝试将该映射的最大容量限制设定为10
map.trySetMaxSize(10);

// 将该映射的最大容量限制设定或更改为10
map.setMaxSize(10);

map.put("1", "2");
map.put("3", "3", 1, TimeUnit.SECONDS);