Spring Data 教程 - Redis

Tri發表於2020-06-18

1. Redis簡介

Redis(Remote Dictionary Server ),即遠端字典服務,是一個開源的使用ANSI C語言編寫、支援網路、可基於記憶體亦可持久化的日誌型、Key-Value 資料庫,並提供多種語言的API。Redis 是一個高效能的key-value資料庫。 redis的出現,在部分場合可以對關聯式資料庫起到很好的補充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客戶端,使用很方便。

Redis支援主從同步。資料可以從主伺服器向任意數量的從伺服器上同步,從伺服器可以是關聯其他從伺服器的主伺服器。這使得Redis可執行單層樹複製。存檔可以有意無意的對資料進行寫操作。由於完全實現了釋出/訂閱機制,使得從資料庫在任何地方同步樹時,可訂閱一個頻道並接收主伺服器完整的訊息釋出記錄。同步對讀取操作的可擴充套件性和資料冗餘很有幫助。

redis的key都是字串String型別,它的value是多樣化的,如下圖:

redis資料型別 ENCODING返回的編碼 底層對應的資料結構
string int long型別的整數
string embstr embstr編碼的簡單動態字串
string raw 簡單動態字串
list ziplist 壓縮列表
list linkedlist 雙向連結串列
hash ziplist 壓縮列表
hash ht 字典
set intset 整數集合
set ht 字典
zset ziplist 壓縮列表
zset skiplist 跳錶

2. Redis的五種資料型別

2.1 字串物件(String)

字串物件的模型:

redis底層提供了三種不同的資料結構實現字串物件,根據不同的資料自動選擇合適的資料結構。這裡的字串物件並不是指的純粹的字串,數字也是可以的。

  • int:當資料是long型別的整數字符串時,底層使用long型別的整數實現。這個值會直接儲存在字串物件的ptr屬性中,同時OBJECT ENCODING為int。

  • raw:當資料為長度大於44位元組的字串時,底層使用簡單動態字串實現,說到這裡就不得不提下redis的簡單隨機字串(Simple Dynamic String,SDS),SDS有三個屬性,free,len和buf。free存的是還剩多少空間,len存的是目前字串長度,不包含結尾的空字元。buf是一個list,存放真實字串資料,包含free和空字元。針對SDS本文不做詳細介紹,歡迎點選SDS瞭解。

  • embstr:當資料為長度小於44位元組的字串時,底層使用embstr編碼的簡單動態字串實現。相比於raw,embstr記憶體分配只需要一次就可完成,分配的是一塊連續的記憶體空間。

2.2 列表物件(List)

列表物件的模型:

redis中的列表物件經常被用作訊息佇列使用,底層採用ziplist和linkedlist實現。大家使用的時候當作連結串列使用就可以了。

  • ziplist

    列表物件使用ziplist編碼需要滿足兩個要求,一是所有字串長度都小於設定值值64位元組(可以在配置檔案中修改list-max-ziplist-value欄位改變)。二是所存元素數量小於設定值512個(可以在配置檔案中修改list-max-ziplist-entries欄位改變)。ziplist類似與python中的list,佔用一段連續的記憶體地址,由此減小指標記憶體佔用。

    zlbytes:佔記憶體總數

    zltail:到尾部的偏移量

    zllen:內部節點數

    node:節點

    zlend:尾部標識

    previous_entry_length:前一節點的長度

    encoding:資料型別

    content:真實資料

    遍歷的時候會根據zlbytes和zltail直接找到尾部節點nodeN,然後根據每個節點的previous_entry_length反向遍歷。增加和刪除節點會導致其他節點連鎖更新,因為每個節點都儲存了前一節點的長度。

  • linkedlist

    linkedlist有三個屬性,head,tail和len。head指向連結串列的頭部,tail指向連結串列的尾部,len為連結串列的長度。

2.3 雜湊型別物件(Hash)

雜湊型別物件的模型:

redis的value型別hash型別,其實就是map型別,就是在值的位置放一個map型別的資料。大家想詳細瞭解一下,可以參考一下這篇文章:https://www.jianshu.com/p/658365f0abfc

2.4 集合物件(Set)

集合物件型別的模型:

Set型別的value保證每個值都不重複。

redis中的集合物件底層有兩種實現方式,分別有整數集合和hashtable。當所有元素都是整數且元素數小於512(可在配置檔案中set-max-intset-entries欄位配置)時採用整數集合實現,其餘情況都採用hashtable實現。hashtable請移駕上文連結查閱,接下來介紹整數集合intset。intset有三個屬性,encoding:記錄數字的型別,有int16,int32和int64等,length:記錄集合的長度,content:儲存具體資料。具體結構如下圖:

2.5 有序集合物件

有序集合物件(zset)和集合物件(set)沒有很大區別,僅僅是多了一個分數(score)用來排序。

redis中的有序集合底層採用ziplist和skiplist跳錶實現,當所有字串長度都小於設定值值64位元組(可以在配置檔案中修改list-max-ziplist-value欄位改變),並且所存元素數量小於設定值512個(可以在配置檔案中修改list-max-ziplist-entries欄位改變)使用ziplist實現,其他情況均使用skiplist實現,跳躍表的實現原理這裡偷個懶,給大家推薦一篇寫的非常好的部落格,點選檢視跳躍表原理

3. Redis的安裝

可以去官網或者中文網下載Redis。redis的windows版本現在已經不更新了,所以我們安裝redis的6.0.3版本,這個版本支援的東西很多,在此次教程中,我們只對redis的五種資料型別做解釋和學習。

官網:https://redis.io/

中文網:https://www.redis.net.cn/

本教程安裝的redis版本為6.0.3版本,redis使用C語言編寫的,CentOS7的gcc自帶版本為4.8.5,而redis6.0+需要的gcc版本為5.3及以上,所以需要升級gcc版本。

下載Linux版本的tar.gz包,解壓以後進入解壓產生的包:

cd redis-6.0.3

發現沒有bin目錄,這裡需要通過make進行安裝。

# 先檢查gcc的環境 
gcc -v 
# 檢視gcc版本 
yum -y install centos-release-scl 
# 升級到9.1版本 
yum -y install devtoolset-9-gcc devtoolset-9-gcc- c++ devtoolset-9-binutils 

scl enable devtoolset-9 bash 
#以上為臨時啟用,如果要長期使用gcc 9.1的話: 
echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile 
# 進入redis解壓檔案 
make 
# 6.0的坑,gcc版本 9.0 以上
# 等待完畢

執行完make操作之後,就可以在redis目錄看到src目錄了。進入src目錄後就可以看到redis-serverredis-cli

這裡建議將Redis的配置檔案複製,保留一份原生的配置檔案。

redis的配置大家可以在網上搜一下常用的配置,在這裡給大家推薦一個常用的配置,比較詳細:

https://blog.csdn.net/ymrfzr/article/details/51362125

到這裡redis就可以啟動並且正常訪問了。

注意:一定要將redis的IP地址繫結註釋掉,允許所有的IP地址訪問,不然我們從Windows訪問就訪問不了。

註釋掉下面的這一行:

同時關閉Redis的服務保護模式,將protected-mode設定為no。如下:

4. Spring Boot 整合 Redis

  • 4.1 搭建工程,引入依賴

    搭建工程的操作我這裡就不在寫出來了。直接上pom.xml

    <!--springboot父工程-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <dependencies>
        <!--springboot-web元件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.2.2.RELEASE</version>
        </dependency>
        <!--redis整合springboot元件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.3.0.RELEASE</version>
        </dependency>
        <!--lombok元件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>
    </dependencies>
    
  • 4.2 redis的配置

    專案的配置檔案,application.yml

    butterflytri:
      host: 127.0.0.1
    server:
      port: 8080 # 應用埠
      servlet:
        context-path: /butterflytri # 應用對映
    spring:
      application:
        name: redis # 應用名稱
      redis:
        host: ${butterflytri.host} # redis地址
        port: 6379 # redis埠,預設是6379
        timeout: 10000 # 連線超時時間(ms)
        database: 0 # redis預設情況下有16個分片,這裡配置具體使用的分片,預設是0
        jedis: # 使用連線redis的工具-jedis
          pool:
            max-active: 8 # 連線池最大連線數(使用負值表示沒有限制) 預設 8
            max-wait: -1 # 連線池最大阻塞等待時間(使用負值表示沒有限制) 預設 -1
            max-idle: 8 # 連線池中的最大空閒連線 預設 8
            min-idle: 0 # 連線池中的最小空閒連線 預設 0
    

    另外還有額外的配置類RedisConfig.java

    package com.butterflytri.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    /**
     * @author: WJF
     * @date: 2020/5/24
     * @description: RedisConfig
     */
    
    @Configuration
    public class RedisConfig {
    
        /**
         * redis鍵值對的值的序列化方式:通用方式
         * @return RedisSerializer
         */
        private RedisSerializer redisValueSerializer() {
            return new GenericJackson2JsonRedisSerializer();
        }
    
        /**
         * redis鍵值對的健的序列化方式:所有的健都是字串
         * @return RedisSerializer
         */
        private RedisSerializer redisKeySerializer() {
            return new StringRedisSerializer();
        }
    
        @Bean("redisTemplate")
        public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(redisConnectionFactory);
            redisTemplate.setKeySerializer(redisKeySerializer());
            redisTemplate.setValueSerializer(redisValueSerializer());
            return redisTemplate;
        }
    
    }
    
  • 4.3 redisTemplate的使用

    value型別的值的CRUD:

    ValueServiceImpl.java

    package com.butterflytri.service.impl;
    
    import com.butterflytri.service.ValueService;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    
    /**
     * @author: WJF
     * @date: 2020/5/27
     * @description: ValueServiceImpl
     */
    @Service
    public class ValueServiceImpl implements ValueService {
    
        @Resource
        private RedisTemplate<String, Object> redisTemplate;
    
        @Override
        public void addValue(String key, Object value) {
            redisTemplate.opsForValue().set(key,value);
        }
    
        @Override
        public Object get(String key) {
            return redisTemplate.opsForValue().get(key);
        }
    
        @Override
        public Object update(String key, Object newValue) {
            return redisTemplate.opsForValue().getAndSet(key,newValue);
        }
    
        @Override
        public void delete(String key) {
            redisTemplate.delete(key);
        }
    }	
    

    List型別的值的CRUD:

    這裡我加了列舉型別用來控制增加的位置,因為List型別對應的是連結串列。

    ListServiceImpl.java

    package com.butterflytri.service.impl;
    
    import com.butterflytri.enums.OpsType;
    import com.butterflytri.service.ListService;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    /**
     * @author: WJF
     * @date: 2020/5/28
     * @description: ListServiceImpl
     */
    @Service
    public class ListServiceImpl implements ListService {
    
        @Resource
        private RedisTemplate<String, Object> redisTemplate;
    
        @Override
        public void addList(String key, List<Object> list, OpsType type) {
            switch (type) {
                case RIGHT:
                    redisTemplate.opsForList().rightPushAll(key, list);
                    break;
                case LEFT:
                    redisTemplate.opsForList().leftPushAll(key, list);
                    break;
                default:
                    throw new RuntimeException("type不能為null");
            }
        }
    
        @Override
        public void add(String redisKey, Object value, OpsType type) {
            switch (type) {
                case RIGHT:
                    redisTemplate.opsForList().rightPush(redisKey, value);
                    break;
                case LEFT:
                    redisTemplate.opsForList().leftPush(redisKey, value);
                    break;
                default:
                    throw new RuntimeException("type不能為null");
            }
        }
    
        @Override
        public List<Object> get(String key) {
            return redisTemplate.opsForList().range(key, 0, -1);
        }
    
        @Override
        public Object update(String key, Object value, Integer index) {
            Object obj = redisTemplate.opsForList().index(key, index);
            redisTemplate.opsForList().set(key,index,value);
            return obj;
        }
    
        @Override
        public void delete(String key) {
            redisTemplate.delete(key);
        }
    
        @Override
        public void deleteValue(String redisKey, OpsType type) {
            switch (type) {
                case RIGHT:
                    redisTemplate.opsForList().rightPop(redisKey);
                    break;
                case LEFT:
                    redisTemplate.opsForList().leftPop(redisKey);
                    break;
                default:
                    throw new RuntimeException("type不能為null");
            }
        }
    }
    

    Hash型別的值的CRUD:

    hash型別是我們使用最常用的型別。

    HashServiceImpl.java:

    package com.butterflytri.service.impl;
    
    import com.butterflytri.service.HashService;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.Map;
    
    /**
     * @author: WJF
     * @date: 2020/5/28
     * @description: HashServiceImpl
     */
    @Service
    public class HashServiceImpl implements HashService {
    
        @Resource
        private RedisTemplate<String, Object> redisTemplate;
    
        @Override
        public void addHashAll(String key, Map<String, Object> value) {
            redisTemplate.opsForHash().putAll(key, value);
        }
    
        @Override
        public void addHash(String redisKey, String key, Object value) {
            redisTemplate.opsForHash().put(redisKey, key, value);
        }
    
        @Override
        public Object get(String redisKey, String key) {
            return redisTemplate.opsForHash().get(redisKey, key);
        }
    
        @Override
        public Object update(String redisKey, String key, Object value) {
            Object obj = this.get(redisKey, key);
            this.delete(redisKey,key);
            redisTemplate.opsForHash().put(redisKey, key, value);
            return obj;
        }
    
        @Override
        public void delete(String redisKey, String key) {
            redisTemplate.opsForHash().delete(redisKey, key);
        }
    
        @Override
        public void deleteAll(String redisKey) {
            redisTemplate.delete(redisKey);
        }
    }
    

    Set的值的CRUD:

    package com.butterflytri.service.impl;
    
    import com.butterflytri.service.SetService;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.Set;
    
    /**
     * @author: WJF
     * @date: 2020/5/28
     * @description: SetServiceImpl
     */
    @Service
    public class SetServiceImpl implements SetService {
    
        @Resource
        private RedisTemplate<String, Object> redisTemplate;
    
    
        @Override
        public void addAll(String key, Set<Object> set) {
            redisTemplate.opsForSet().add(key,set);
        }
    
        @Override
        public void add(String key, Object value) {
            redisTemplate.opsForSet().add(key,value);
        }
    
        @Override
        public Set<Object> findAll(String key) {
            return redisTemplate.opsForSet().members(key);
        }
    
        @Override
        public void deleteValue(String key, Object value) {
            redisTemplate.opsForSet().remove(key,value);
        }
    
        @Override
        public void delete(String key) {
            redisTemplate.delete(key);
        }
    }
    

    ZSet型別的值的CRUD:

    package com.butterflytri.service.impl;
    
    import com.butterflytri.service.SortedSetService;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.LinkedHashSet;
    
    /**
     * @author: WJF
     * @date: 2020/5/28
     * @description: SortedSetServiceImpl
     */
    @Service
    public class SortedSetServiceImpl implements SortedSetService {
    
        @Resource
        private RedisTemplate<String, Object> redisTemplate;
    
        @Override
        public void add(String key, String value, Double score) {
            redisTemplate.opsForZSet().add(key, value, score);
        }
    
        @Override
        public LinkedHashSet<Object> findAll(String key) {
            return (LinkedHashSet<Object>) redisTemplate.opsForZSet().range(key,0,-1);
        }
    
        @Override
        public Long count(String key, Double scoreFrom, Double scoreTo) {
            return redisTemplate.opsForZSet().count(key,scoreFrom,scoreTo);
        }
    
        @Override
        public LinkedHashSet<Object> findByScore(String key, Double scoreFrom, Double scoreTo) {
            return (LinkedHashSet<Object>) redisTemplate.opsForZSet().rangeByScore(key,scoreFrom,scoreTo);
        }
    
        @Override
        public Long rank(String key, Object value) {
            return redisTemplate.opsForZSet().rank(key,value);
        }
    
        @Override
        public void remove(String key, String value) {
            redisTemplate.opsForZSet().remove(key,value);
        }
    
        @Override
        public void delete(String key) {
            redisTemplate.delete(key);
        }
    
    }
    

    redis的Java客戶端有很多,在這裡我們使用的是jedis,還有一個很好的Java語言的客戶端叫lettuce,大家可以去了解一下,Spring從不重複造輪子,只會簡化輪子的使用,redisTemplate就是一個超級簡單的使用實現。到這裡redis整合Spring Boot 就結束了。

5. 專案地址

本專案傳送門:

此教程會一直更新下去,覺得博主寫的可以的話,關注一下,也可以更方便下次來學習。


相關文章