springboot2.2.X手冊:是時候用Lettuce替換Jedis操作Redis緩存了

目錄

Redis介紹及Mencached對比

Jedis與Lettuce對比

引入包

編寫配置類

編寫工具類

編寫配置文件

編寫測試類

測試結果


上一篇:springboot2.2.X手冊:基於OSS解決文件存儲(一年9元^^,賺了)

Redis介紹及Mencached對比

Redis全稱是遠程字典服務,是一個Key-Value的存儲系統,相比於很早之前一直使用的mencached,不單單提供了更多的類型支持。

數據類型上:mencached只支持簡單的key-value存儲,不支持持久化,不支持複製,不支持枚舉,但是redis在數據結構上支持list、set、sorted set、hash,同時提供持久化與複製的功能。

內存機制上:mencached是進行全內存存儲,就是所有的數據一直在內存中,但是redis並不是,它在進行內存存儲的時候,根據swappability = age*log(size_in_memory)進行計算,計算出哪些值超過閾值,從而確定哪些值需要存儲到磁盤,然後清除掉內存中的值。所以很多時候,Redis的存儲是可以超過機器內存的值。

性能上:並不是說Redis性能一定比mencached更好,這裏有個區間,在100k以上的數據中,mencached的性能高於Redis。

高可用上:mencached是全內存的緩存機制的,並不支持集羣拓展,需要自己在客戶端通過一致性哈希來實現,不過現在很少人這麼做了,現在一般都是採用Redis Cluster來實現。

springboot2.2.X手冊:是時候用Lettuce替換Jedis操作Redis緩存了

 

Jedis與Lettuce對比

這兩個都是用於提供連接Redis的客戶端。

Jedis是直接連接Redis,非線程安全,在性能上,每個線程都去拿自己的 Jedis 實例,當連接數量增多時,資源消耗階梯式增大,連接成本就較高了。

Lettuce的連接是基於Netty的,Netty 是一個多線程、事件驅動的 I/O 框架。連接實例可以在多個線程間共享,當多線程使用同一連接實例時,是線程安全的。

springboot2.2.X手冊:是時候用Lettuce替換Jedis操作Redis緩存了

 

引入包

	<!-- redis核心包 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>

		<!-- 需要引入redis依賴包commons-pool -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-pool2</artifactId>
		</dependency>

編寫配置類

/**
 * All rights Reserved, Designed By 溪雲閣
 * Copyright:    Copyright(C) 2016-2020
 */

package com.module.boots.redis;

import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;

/**
 * Redis配置
 * @author:溪雲閣
 * @date:2020年5月24日
 */
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {

    /**
     * 採用FastJson進行key/value序列化
     * @author 溪雲閣
     * @param redisConnectionFactory
     * @return RedisTemplate<String,Object>
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {

        final RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 使用fastjson序列化
        final FastJsonRedisSerializer<?> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        // value值的序列化採用fastJsonRedisSerializer
        template.setValueSerializer(fastJsonRedisSerializer);
        template.setHashValueSerializer(fastJsonRedisSerializer);
        // key的序列化採用StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        // 開啓事務
        template.setEnableTransactionSupport(true);
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

}

編寫工具類

package com.module.boots.redis;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

/**
 * redis工具類
 * @author:溪雲閣
 * @date:2020年5月24日
 */
@Component
public class RedisUtils {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 指定緩存失效時間
     * @param key 鍵
     * @param time 時間(秒)
     * @return
     */
    public boolean expire(String key, long time) {
        if (time > 0) {
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
            return true;
        } else {
            throw new RuntimeException("超時時間小於0");
        }
    }

    /**
     * 根據key 獲取過期時間
     * @param key 鍵 不能爲null
     * @return 時間(秒) 返回0代表爲永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判斷key是否存在
     * @param key 鍵
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 刪除緩存
     * @param key 可以傳一個值 或多個
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

    // ============================String=============================

    /**
     * 普通緩存獲取
     * @param key 鍵
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通緩存放入
     * @param key 鍵
     * @param value 值
     * @return true成功 false失敗
     */
    public boolean set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
        return true;
    }

    /**
     * 普通緩存放入並設置時間
     * @param key 鍵
     * @param value 值
     * @param time 時間(秒) time要大於0 如果time小於等於0 將設置無限期
     * @return true成功 false 失敗
     */
    public boolean set(String key, Object value, long time) {
        if (time > 0) {
            redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
        } else {
            this.set(key, value);
        }
        return true;
    }

    /**
     * 遞增
     * @param key 鍵
     * @param by 要增加幾(大於0)
     * @return
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("遞增因子必須大於0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 遞減
     * @param key 鍵
     * @param by 要減少幾(小於0)
     * @return
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("遞減因子必須大於0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    // ================================Map=================================
    /**
     * HashGet
     * @param key 鍵 不能爲null
     * @param item 項 不能爲null
     * @return 值
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 獲取hashKey對應的所有鍵值
     * @param key 鍵
     * @return 對應的多個鍵值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     * @param key 鍵
     * @param map 對應多個鍵值
     * @return true 成功 false 失敗
     */
    public boolean hmset(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
        return true;
    }

    /**
     * HashSet 並設置時間
     * @param key 鍵
     * @param map 對應多個鍵值
     * @param time 時間(秒)
     * @return true成功 false失敗
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        redisTemplate.opsForHash().putAll(key, map);
        if (time > 0) {
            expire(key, time);
        }
        return true;
    }

    /**
     * 向一張hash表中放入數據,如果不存在將創建
     * @param key 鍵
     * @param item 項
     * @param value 值
     * @return true 成功 false失敗
     */
    public boolean hset(String key, String item, Object value) {
        redisTemplate.opsForHash().put(key, item, value);
        return true;
    }

    /**
     * 向一張hash表中放入數據,如果不存在將創建
     * @param key 鍵
     * @param item 項
     * @param value 值
     * @param time 時間(秒)  注意:如果已存在的hash表有時間,這裏將會替換原有的時間
     * @return true 成功 false失敗
     */
    public boolean hset(String key, String item, Object value, long time) {
        redisTemplate.opsForHash().put(key, item, value);
        if (time > 0) {
            expire(key, time);
        }
        return true;

    }

    /**
     * 刪除hash表中的值
     * @param key 鍵 不能爲null
     * @param item 項 可以使多個 不能爲null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判斷hash表中是否有該項的值
     * @param key 鍵 不能爲null
     * @param item 項 不能爲null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash遞增 如果不存在,就會創建一個 並把新增後的值返回
     * @param key 鍵
     * @param item 項
     * @param by 要增加幾(大於0)
     * @return
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash遞減
     * @param key 鍵
     * @param item 項
     * @param by 要減少記(小於0)
     * @return
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

    // ============================set=============================
    /**
     * 根據key獲取Set中的所有值
     * @param key 鍵
     * @return
     */
    public Set<Object> sGet(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 根據value從一個set中查詢,是否存在
     * @param key 鍵
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    /**
     * 將數據放入set緩存
     * @param key 鍵
     * @param values 值 可以是多個
     * @return 成功個數
     */
    public long sSet(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }

    /**
     * 將set數據放入緩存
     * @param key 鍵
     * @param time 時間(秒)
     * @param values 值 可以是多個
     * @return 成功個數
     */
    public long sSetAndTime(String key, long time, Object... values) {
        final Long count = redisTemplate.opsForSet().add(key, values);
        if (time > 0)
            expire(key, time);
        return count;
    }

    /**
     * 獲取set緩存的長度
     * @param key 鍵
     * @return
     */
    public long sGetSetSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    /**
     * 移除值爲value的
     * @param key 鍵
     * @param values 值 可以是多個
     * @return 移除的個數
     */
    public long setRemove(String key, Object... values) {
        final Long count = redisTemplate.opsForSet().remove(key, values);
        return count;
    }

    // ===============================list=================================

    /**
     * 獲取list緩存的內容
     * @param key 鍵
     * @param start 開始
     * @param end 結束  0 到 -1代表所有值
     * @return
     */
    public List<Object> lGet(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 獲取list緩存的長度
     * @param key 鍵
     * @return
     */
    public long lGetListSize(String key) {
        return redisTemplate.opsForList().size(key);
    }

    /**
     * 通過索引 獲取list中的值
     * @param key 鍵
     * @param index 索引  index>=0時, 0 表頭,1 第二個元素,依次類推;index<0時,-1,表尾,-2倒數第二個元素,依次類推
     * @return
     */
    public Object lGetIndex(String key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }

    /**
     * 將list放入緩存
     * @param key 鍵
     * @param value 值
     * @param time 時間(秒)
     * @return
     */
    public boolean lSet(String key, Object value) {
        redisTemplate.opsForList().rightPush(key, value);
        return true;
    }

    /**
     * 將list放入緩存
     * @param key 鍵
     * @param value 值
     * @param time 時間(秒)
     * @return
     */
    public boolean lSet(String key, Object value, long time) {
        redisTemplate.opsForList().rightPush(key, value);
        if (time > 0) {
            expire(key, time);
        }
        return true;
    }

    /**
     * 將list放入緩存
     * @param key 鍵
     * @param value 值
     * @param time 時間(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        redisTemplate.opsForList().rightPushAll(key, value);
        return true;
    }

    /**
     * 將list放入緩存
     * @param key 鍵
     * @param value 值
     * @param time 時間(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        redisTemplate.opsForList().rightPushAll(key, value);
        if (time > 0) {
            expire(key, time);
        }
        return true;
    }

    /**
     * 根據索引修改list中的某條數據
     * @param key 鍵
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean lUpdateIndex(String key, long index, Object value) {
        redisTemplate.opsForList().set(key, index, value);
        return true;
    }
}

編寫配置文件

# redis地址
spring.redis.host: 127.0.0.1
# redis端口號
spring.redis.port: 6379
# redis密碼,如果沒有不用填寫,建議還是得有
spring.redis.password: 123456
# 最大活躍連接數,默認是8
spring.redis.lettuce.pool.maxActive: 100
# 最大空閒連接數 ,默認是8
spring.redis.lettuce.pool.maxIdle: 100
# 最小空閒連接數 ,默認是0
spring.redis.lettuce.pool.minIdle: 0

編寫測試類

/**
 * All rights Reserved, Designed By 溪雲閣
 * Copyright:    Copyright(C) 2016-2020
 */

package com.boots.redis;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.module.boots.redis.RedisUtils;

/**
 * redis單元測試
 * @author:溪雲閣
 * @date:2020年5月24日
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class BootsRedisApplicationTest {

    @Autowired
    private RedisUtils redisUtils;

    /**
     * 執行前插入值
     * @author 溪雲閣
     * @throws Exception void
     */
    @Before
    public void setUp() throws Exception {
        redisUtils.set("aaa", "121212");
        System.out.println("插入值成功");
    }

    /**
     * 執行獲取值
     * @author 溪雲閣
     * @throws Exception void
     */
    @Test
    public void test() throws Exception {
        System.out.println(redisUtils.get("aaa"));
    }

}

測試結果

springboot2.2.X手冊:是時候用Lettuce替換Jedis操作Redis緩存了

 

springboot2.2.X手冊:是時候用Lettuce替換Jedis操作Redis緩存了

 

--END--

作者:@溪雲閣

如需要源碼,轉發,關注後私信我。

部分圖片或代碼來源網絡,如侵權請聯繫刪除,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章