Redis使用進階

Redis之使用教程(Java版)java

博客地址 https://blog.piaoruiqing.com/blog/2019/06/11/redis使用進階git

關鍵詞

  • Jedis: redis java客戶端實現.
  • Lettuce: redis java客戶端實現, 基於netty.
  • spring-data-redis: Spring針對redis的封裝, 配置簡單, 提供了與Redis存儲交互的抽象封裝, 十分優雅, 也極具擴展性. 可集成Jedis、Lettuce等redis客戶端. springboot2.0後官方默認集成的客戶端從Jedis改成Lettuce.

前言

本文將針對使用Java集成Redis進行講解, JedisLettuce的使用僅做簡單描述, springredis集成及使用將做爲主要講解內容.github

Jedis

github.com/xetorthio/j…redis

引入依賴:spring

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.3</version>
</dependency>
複製代碼

Jedis是對redis命令的封裝, 使用上基本與redis-cli無異, 操做string的使用示例以下:docker

/** jedis pool */
private static final JedisPool POOL = new JedisPool(new JedisPoolConfig(), "test-redis-server", 6379);

// test Binary-safe strings with Jedis
try (Jedis jedis = POOL.getResource()){ 
    {   // SET mykey myvalue
        String result = jedis.set("mykey", "myvalue");
        LOGGER.info("cmd: SET mykey myvalue, result: {}", result);
    }
    {   // GET mykey
        String result = jedis.get("mykey");
        LOGGER.info("cmd: GET mykey, result: {}", result);
    }
    {   // KEYS my*
        Set<String> keys = jedis.keys("my*");
        LOGGER.info("cmd: KEYS my*, result: {}", JsonUtils.writeValueAsString(keys, true));
    }
    {   // EXISTS mykey
        Boolean result = jedis.exists("mykey");
        LOGGER.info("cmd: EXISTS mykey, result: {}", result);
    }
    {   // DEL mykey
        Long result = jedis.del("mykey");
        LOGGER.info("cmd: DEL mykey, result: {}", result);
    }
    {   // GET mykey
        String result = jedis.get("mykey");
        LOGGER.info("cmd: GET mykey, result: {}", result);
    }
}
複製代碼
  • JedisPool: Jedis並非線程安全的, 因此多線程狀況下不該共用Jedis實例, 但建立大量的Jedis會形成沒必要要的開銷甚至對性能產生較大影響, 故使用JedisPool來避免這些問題, 它是一個線程安全的網絡鏈接池. 可使用它可靠地建立多個Jedis實例, 完成後將Jedis實例回收到鏈接池中.
  • JedisPool.getResource: 從鏈接池獲取一個Jedis鏈接, 注意: Jedis使用完畢後須要調用Jedis.close方法釋放資源.(Jedis實現了AutoCloseable, 推薦使用try-with-resource的寫法)

Lettuce

lettuce.io/ubuntu

引入依賴:數組

<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
    <version>5.1.7.RELEASE</version>
</dependency>
複製代碼

Lettuce是一個可擴展的Redis客戶端,用於構建非阻塞的Reactive應用程序. 它基於Netty框架構建, 性能較高, 且支持不少redis的高級特性. 目前springboot2.0已將Lettuce做爲默認redis客戶端. 與上一小節對應, 操做string的使用示例以下:緩存

/** redis client */
private static final RedisClient CLIENT = RedisClient.create("redis://@test-redis-server:6379/0");

// test Binary-safe strings with Lettuce
try (StatefulRedisConnection<String, String> connection = CLIENT.connect()) {
    RedisCommands<String, String> commands = connection.sync();
    {   // SET mykey myvalue
        String result = commands.set("mykey", "myvalue");
        LOGGER.info("cmd: SET mykey myvalue, result: {}", result);
    }
    {   // GET mykey
        String result = commands.get("mykey");
        LOGGER.info("cmd: GET mykey, result: {}", result);
    }
    {   // KEYS my*
        List<String> keys = commands.keys("my*");
        LOGGER.info("cmd: KEYS my*, result: {}", JsonUtils.writeValueAsString(keys, true));
    }
    {   // EXISTS mykey
        Long result = commands.exists("mykey");
        LOGGER.info("cmd: EXISTS mykey, result: {}", result);
    }
    {   // DEL mykey
        Long result = commands.del("mykey");
        LOGGER.info("cmd: DEL mykey, result: {}", result);
    }
    {   // GET mykey
        String result = commands.get("mykey");
        LOGGER.info("cmd: GET mykey, result: {}", result);
    }
}
複製代碼

Spring 集成

spring-data-redisSpring Data家族的一部分, 提供了簡單的配置以輕鬆訪問redis, 針對存儲操做提供了低級別和高級別的抽象, 將開發人員從基礎實現中解放出來.安全

引入依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.1.5.RELEASE</version>
</dependency>
複製代碼

使用spring-data-redis開發時, 可能最常使用的就是RedisTemplate, 因此在開始前咱們先了解下RedisTemplate:

  • RedisTemplate是一個簡化了Redis訪問的工具類.
  • 線程安全(thread-safe), 做爲單例使用便可.
  • 其實現圍繞execute方法, 支持callback, 它提供的RedisConnection處理方式不須要關心鏈接的聲明週期(簡言之就是不用建立也不用關鏈接)

使用方法很簡單, 首先在Configuration中定義StringRedisTemplate的Bean:

/** * StringRedisTemplate * @param factory * @return */
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {

    StringRedisTemplate template = new StringRedisTemplate(factory);
    StringRedisSerializer serializer = new StringRedisSerializer();	// (一)
    template.setKeySerializer(serializer);	// (二)
    template.setHashKeySerializer(serializer);
    template.setValueSerializer(serializer);
    template.setHashValueSerializer(serializer);

    return template;
}
複製代碼
  • (一): RedisSerializer: 對象到二進制數組序列化和反序列化接口, 序列化和反序列化key和value, StringRedisSerializerGenericJackson2JsonRedisSerializer都是其實現.

  • (二): KeySerializer用來序列化redis key, HashKeySerializer用來序列化redis hash數據結構的field. 請勿混淆.

固然, 不要忘記了application.yml中添加redis相關配置:

spring:
 redis:
 host: test-redis-server
 port: 6379
複製代碼

準備工做完成了, 如今就來體驗一下, 一樣地與前文對應, 操做string的使用示例以下: :

@Resource
private StringRedisTemplate stringRedisTemplate;

/** * test Binary-safe strings with RedisTemplate */
@Test
public void testStringRedisTemplateSimple() {
    {   // SET mykey myvalue
        stringRedisTemplate.opsForValue().set("mykey", "myvalue");
    }
    {   // GET mykey
        String result = stringRedisTemplate.opsForValue().get("mykey");
        LOGGER.info("cmd: GET mykey, result: {}", result);
    }
    {   // KEYS my*
        Set<String> keys = stringRedisTemplate.keys("my*");
        LOGGER.info("cmd: KEYS my*, result: {}", JsonUtils.writeValueAsString(keys, true));
    }
    {   // EXISTS mykey
        Boolean result = stringRedisTemplate.hasKey("mykey");
        LOGGER.info("cmd: EXISTS mykey, result: {}", result);
    }
    {   // DEL mykey
        Boolean result = stringRedisTemplate.delete("mykey");
        LOGGER.info("cmd: DEL mykey, result: {}", result);
    }
    {   // GET mykey
        String result = stringRedisTemplate.opsForValue().get("mykey");
        LOGGER.info("cmd: GET mykey, result: {}", result);
    }
}
複製代碼
  • opsForValue: 獲取Binary-safe strings的操做類ValueOperations( 即spring對redis操做的一個封裝類. 一樣地, 對hashset等也有其對應的封裝HashOperationsSetOperations等).
[版權聲明]
本文發佈於 樸瑞卿的博客, 容許非商業用途轉載, 但轉載必須保留原做者 樸瑞卿 及連接: blog.piaoruiqing.com. 若有受權方面的協商或合做, 請聯繫郵箱: piaoruiqing@gmail.com.

進階

劃分應用緩存

不一樣應用的緩存能夠簡單地經過key的前綴來劃分

讓咱們來思考這樣一個問題, 若是咱們想要對不一樣應用(服務)的緩存進行劃分, 以便於管理和維護, 應該如何實現?

或許增長前綴是一個不錯的想法, 但若是每次編碼中都須要將前綴prefix拼接到key中, 一方面增長了工做量, 另外一份面也增長了出錯的風險, 若是忘記拼接了怎麼辦. 對, 也許你也想到了, 前文提到RedisSerializerspring-data-redis對象到二進制數組序列化和反序列化接口, 用來序列化和反序列化key和value, 咱們能夠從這裏作文章:

public interface RedisSerializer<T> {
	/** * Serialize the given object to binary data. * * @param t object to serialize. Can be {@literal null}. * @return the equivalent binary data. Can be {@literal null}. */
	@Nullable
	byte[] serialize(@Nullable T t) throws SerializationException;

	/** * Deserialize an object from the given binary data. * * @param bytes object binary representation. Can be {@literal null}. * @return the equivalent object instance. Can be {@literal null}. */
	@Nullable
	T deserialize(@Nullable byte[] bytes) throws SerializationException;
}
複製代碼
  • serialize: 對象 -> byte數組.
  • deserialize: byte數組 -> 對象.

RedisTemplate對redis的操做和RedisSerializer都有必然的聯繫, 那麼能夠經過實現該接口並指定RedisTemplateKeySerializer來實現增長前綴的功能. 如此一來, 增長前綴的操做就從業務中剝離出來, 對於調用方來講, 徹底是透明的, 還算優雅, 具體實現以下:

/** * generic redis key serializer * @author piaoruiqing * @date: 2019-06-11 22:37 */
public class GenericRedisKeySerializer implements RedisSerializer<Object> {

    private final Charset charset;
    private String prefix;
    private int index;
    
    public GenericRedisKeySerializer(String prefix) {
        this(prefix, StandardCharsets.UTF_8);
    }

    public GenericRedisKeySerializer(String prefix, Charset charset) {
        Assert.notNull(charset);
        Assert.notNull(prefix);
        this.charset = charset;
        this.prefix = prefix + ":";
        index = this.prefix.length();
    }

    @Override
    public String deserialize(byte[] bytes) {
        
        if (null == bytes) {
            return null;
        }
        String key = new String(bytes, charset);
        if (key.indexOf(prefix) == 0) {
            return key.substring(index, key.length());
        }
        return key;
    }

    @Override
    public byte[] serialize(Object key) {

        if (null == key) {
            return null;
        }
        String string = key.toString();
        if (!string.startsWith(prefix)) {
            string = prefix + string;
        }
        return string.getBytes(charset);
    }
}
複製代碼

將前文的StringRedisTemplate稍做修改:

@Value("${spring.application.name:undefined}")
private String applicationName;
/** * StringRedisTemplate * @param factory * @return */
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {

    StringRedisTemplate template = new StringRedisTemplate(factory);
// StringRedisSerializer serializer = new StringRedisSerializer();
    GenericRedisKeySerializer serializer = new GenericRedisKeySerializer(applicationName);
    template.setKeySerializer(serializer);
    template.setHashKeySerializer(serializer);
    template.setValueSerializer(serializer);
    template.setHashValueSerializer(serializer);
    return template;
}
複製代碼
  • StringRedisSerializer替換爲自定義的GenericRedisKeySerializer並指定前綴爲應用名

體驗一下:

stringRedisTemplate.opsForValue().set("mykey", "myvalue");
String result = stringRedisTemplate.opsForValue().get("mykey");  // "myvalue"
複製代碼

鏈接到redis查看key, 已經帶有前綴了

root@ubuntu:/home/ubuntu# docker exec -it redis redis-cli
127.0.0.1:6379> keys *
1) "redis-simple:mykey"
複製代碼

自定義序列化

RedisTemplate默認使用JDK序列化JdkSerializationRedisSerializer, 咱們能夠指定使用其餘方式的序列化, 好比JSON、protostuff

前文已經描述瞭如何自定義key的序列化方式, value的序列化配置與其相同, 都是實現RedisSerializer並在建立RedisTemplate時指定, 就不重複貼代碼了.

經常使用的序列化方式有幾種:

  • JDK: 默認, 比較方便, 可序列化全部的類, 但速度慢且佔空間較大.
  • JSON: 性能好, 輸出內容比較易讀.
  • Protostuff: 性能很高, 速度快且佔用空間較小.

結語

本文針對redis講解了redis java客戶端的使用、與spring集成以及進階使用, 後續將針對Redis的其餘使用技巧進行講解, 敬請關注.

系列文章

  1. Redis入門
  2. Redis使用進階
  3. Redis分佈式鎖

參考文獻

[版權聲明]
本文發佈於 樸瑞卿的博客, 容許非商業用途轉載, 但轉載必須保留原做者 樸瑞卿 及連接: blog.piaoruiqing.com. 若有受權方面的協商或合做, 請聯繫郵箱: piaoruiqing@gmail.com.
相關文章
相關標籤/搜索