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的五種資料型別做解釋和學習。
本教程安裝的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-server
和redis-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. 專案地址
本專案傳送門:
- GitHub ---> spring-data-redis
- Gitee ---> spring-data-redis
此教程會一直更新下去,覺得博主寫的可以的話,關注一下,也可以更方便下次來學習。
- 作者:Butterfly-Tri
- 出處:Butterfly-Tri個人部落格
- 版權所有,歡迎保留原文連結進行轉載?