@[Toc]
一、Redis介紹
Redis是一種基于鍵值對(key-value)的NoSQL資料庫,與很多鍵值對 資料庫不同的是,Redis中的值可以是由string(字元串)、hash(哈希)、 list(清單)、set(集合)、zset(有序集合)、Bitmaps(位圖)、 HyperLogLog、GEO(地理資訊定位)等多種資料結構和算法組成,是以 Redis可以滿足很多的應用場景,而且因為Redis會将所有資料都存放在記憶體 中,是以它的讀寫性能非常驚人。不僅如此,Redis還可以将記憶體的資料利 用快照和日志的形式儲存到硬碟上,這樣在發生類似斷電或者機器故障的時 候,記憶體中的資料不會“丢失”。除了上述功能以外,Redis還提供了鍵過期、釋出訂閱、事務、流水線、Lua腳本等附加功能。總之,如果在合适的 場景使用好Redis,它就會像一把瑞士軍刀一樣所向披靡。
二、整合Redis
1、spring-boot-starter-data-redis介紹
Spring Boot 提供了對 Redis 內建的元件包:spring-boot-starter-data-redis,它依賴于 spring-data-redis 和lettuce。Spring Boot 1.0 預設使⽤的是 Jedis 用戶端,2.0 替換成了 Lettuce。
- Lettuce:是⼀個可伸縮線程安全的 Redis 用戶端,多個線程可以共享同⼀個 RedisConnection,它利⽤優秀 Netty NIO 架構來⾼效地管理多個連接配接。
- Spring Data:是 Spring 架構中的⼀個主要項⽬,⽬的是為了簡化建構基于 Spring 架構應⽤的資料通路,包括⾮關系資料庫、Map-Reduce 架構、雲資料服務等,另外也包含對關系資料庫的通路⽀持。
- Spring Data Redis:是 Spring Data 項⽬中的⼀個主要子產品,實作了對 Redis 用戶端 API 的⾼度封裝,使對 Redis 的操作更加便捷。
2、引入依賴
建立一個SpringBoot項目,引入相關依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--使⽤ commons-pool 2 建立 Redis 連接配接池-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
如果習慣使用Jdedis,也可以從spring-boot-stater-data-redis 排除 Lettuce ,使用Jedis :
<!--排除lettuce,使用jedis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
3、配置
3.1、application 配置
在application.properties中添加Redis相關配置:
# Redis
# Redis 資料庫索引(預設為 0)
spring.redis.database=0
# Redis 伺服器位址
spring.redis.host=localhost
# Redis 伺服器連接配接端⼝
spring.redis.port=6379
# Redis 伺服器連接配接密碼(預設為空)
spring.redis.password=
# 連接配接池最⼤連接配接數(使⽤負值表示沒有限制) 預設 8
spring.redis.lettuce.pool.max-active=8
# 連接配接池最⼤阻塞等待時間(使⽤負值表示沒有限制) 預設 -1
spring.redis.lettuce.pool.max-wait=-1
# 連接配接池中的最⼤空閑連接配接 預設 8
spring.redis.lettuce.pool.max-idle=8
# 連接配接池中的最⼩空閑連接配接 預設 0
spring.redis.lettuce.pool.min-idle=0
3.2、緩存配置
在自動配置類裡可以為 Redis 設定⼀些全局配置,⽐如配置主鍵的⽣産政策 KeyGenerator,如不配置會預設使⽤參數名作為主鍵。
@Configuration
//開啟緩存配置
@EnableCaching
public class RedisConfig {
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
}
3.3、測試
在單元測試中,注⼊ RedisTemplate。String 是最常⽤的⼀種資料類型,普通的 key/value 存儲都可以歸為此類,value 其實不僅是 String 也可以是數字。
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testString() {
redisTemplate.opsForValue().set("trytry", "it's time");
Assert.assertEquals("it's time", redisTemplate.opsForValue().get("trytry"));
}
}
在這個單元測試中,我們使⽤ redisTemplate 存儲了⼀個字元串 "it's time",存儲之後擷取進⾏驗證,多次進⾏set 相同的 key,鍵對應的值會被覆寫。
綜上所述,進行使⽤ spring-boot-starter-data-redis隻需要上面的三步。
三、操作Redis各資料類型
Redis ⽀持多種資料類型,實體、哈希、清單、集合、有序集合,通過spring-boot-starter-data-redis都可以進行存儲、讀取、逾時失效、删除操作。
1、實體
建立一個實體類:
public class User implements Serializable {
private String name;
private String description;
public User(String name, String description) {
this.name = name;
this.description = description;
}
}
将實體類放入緩存,再取出來:
@Test
public void testModel(){
User user=new User("zhangtiedan","頭鐵");
//擷取一個操作對象
ValueOperations<String, User> operations=redisTemplate.opsForValue();
//将user存入緩存
operations.set("tiedan", user);
//取出緩存中的user
User u=operations.get("tiedan");
System.out.println("user: "+u.toString());
}
運作結果:
2、逾時失效
Redis 在存⼊每⼀個資料的時候都可以設定⼀個逾時時間,過了這個時間就會⾃動删除資料,這種特性⾮常适合我們對階段資料的緩存。
建立⼀個 User 對象,存⼊ Redis 的同時設定 100 毫秒後失效,設定⼀個線程暫停 1000 毫秒之後,判斷資料是否存在并列印結果。
@Test
public void testExpire() throws InterruptedException {
User user=new User("gangdan","人狠話不多");
ValueOperations<String, User> operations=redisTemplate.opsForValue();
//将user存⼊ Redis 的同時設定 100 毫秒後失效
operations.set("expire", user,100, TimeUnit.MILLISECONDS);
Thread.sleep(1000);
boolean exists=redisTemplate.hasKey("expire");
if(exists){
System.out.println("exists is true");
}else{
System.out.println("exists is false");
}
}
3、删除資料
删除資料也是必需的。
@Test
public void testDelete(){
User user=new User("hage","不慫但是沒什麼用");
ValueOperations<String, User> operations=redisTemplate.opsForValue();
//存入
redisTemplate.opsForValue().set("deletekey", user);
System.out.println("存入--是否存在:"+redisTemplate.hasKey("deletekey"));
//删除
redisTemplate.delete("deletekey");
System.out.println("删除--是否存在:"+redisTemplate.hasKey("deletekey"));
}
4、Hash(哈希)
Redis 存儲⼀個 key會有⼀個最⼩記憶體,不管你存的這個鍵多⼩,都不會低于這個記憶體,是以合理的使⽤ Hash 可以幫我們節省很多記憶體。
在Redis中,哈希類型是指鍵值本身又是一個鍵值對 結構,形如value={{field1,value1},...{fieldN,valueN}}。
String和Hash的差別
Hash Set 就在哈希表 Key 中的域(Field)的值設為 value。如果 Key 不存在,⼀個新的哈希表被建立并進⾏ HSET 操作;如果域(field)已經存在于哈希表中,舊值将被覆寫。
@Test
public void testHash(){
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
hash.put("hash","you","you");
String value=(String) hash.get("hash","you");
System.out.println("hash value :"+value);
}
Hash set 的時候需要傳⼊三個參數,第⼀個為 key,第⼆個為 field,第三個為存儲的值。⼀般情況下 Key 代表⼀組資料,field 為 key 相關的屬性,⽽ value 就是屬性對應的值。
5、List
Redis List 的應⽤場景⾮常多,也是 Redis 最重要的資料結構之⼀。 使⽤ List 可以輕松的實作⼀個隊列,List 典型的應⽤場景就是消息隊列,可以利⽤ List 的 Push 操作,将任務存在 List 中,然後⼯作線程再⽤POP 操作将任務取出進⾏執⾏。
List push和pop示意圖
@Test
public void testList() {
ListOperations<String, String> list = redisTemplate.opsForList();
list.leftPush("list","you");
list.leftPush("list","go");
list.leftPush("list","home");
String value=(String)list.leftPop("list");
System.out.println("list value :"+value.toString());
}
上面的例子上⾯的例⼦從左側插⼊⼀個 key 為 "list" 的隊列,然後取出左側最近的⼀條資料。List 有很多 API可以操作,⽐如從右側進⾏插⼊隊列從右側進⾏讀取,或者通過⽅法 range 讀取隊列的⼀部分。接着上⾯的例⼦使⽤ range 來讀取。
子清單擷取、删除等操作
List<String> values=list.range("list",0,2);
for (String v:values){
System.out.println("list range :"+v);
}
6、Set
集合(set)類型也是用來儲存多個的字元串元素,但和清單類型不一 樣的是,集合中不允許有重複元素,并且集合中的元素是無序的,不能通過 索引下标擷取元素。
集合類型示意圖
@Test
public void testSet() {
String key="set";
SetOperations<String, String> set = redisTemplate.opsForSet();
set.add(key,"kick");
set.add(key,"you");
set.add(key,"you");
set.add(key,"now");
Set<String> values=set.members(key);
for (String v:values){
System.out.println("set value :"+v);
}
}
Redis 為集合提供了求交集、并集、差集等操作,可以⾮常⽅便的使⽤。
- difference
difference() 函數會把 key 1 中不同于 key 2 的資料對⽐出來
@Test
public void testDifference(){
SetOperations<String, String> set = redisTemplate.opsForSet();
String key1="setMore1";
String key2="setMore2";
set.add(key1,"it");
set.add(key1,"you");
set.add(key1,"you");
set.add(key1,"know");
set.add(key2,"xx");
set.add(key2,"know");
Set<String> diffs=set.difference(key1,key2);
for (String v:diffs){
System.out.println("diffs set value :"+v);
}
}
- unions
unions 會取兩個集合的合集。
@Test
public void testUnions(){
SetOperations<String, String> set = redisTemplate.opsForSet();
String key3="setMore3";
String key4="setMore4";
set.add(key3,"it");
set.add(key3,"you");
set.add(key3,"xx");
set.add(key4,"aa");
set.add(key4,"bb");
set.add(key4,"know");
Set<String> unions=set.union(key3,key4);
for (String v:unions){
System.out.println("unions value :"+v);
}
}
7、ZSet
有序集合(ZSet)相對于哈希、清單、集合來說會有一點點陌生,但既然叫有序 集合,那麼它和集合必然有着聯系,它保留了集合不能有重複成員的特性, 但不同的是,有序集合中的元素可以排序。但是它和清單使用索引下标作為 排序依據不同的是,它給每個元素設定一個分數(score)作為排序的依據。
有序集合類型示意圖
@Test
public void testZset(){
String key="zset";
redisTemplate.delete(key);
ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
zset.add(key,"it's",1);
zset.add(key,"your",6);
zset.add(key,"show",4);
zset.add(key,"time",3);
Set<String> zsets=zset.range(key,0,3);
for (String v:zsets){
System.out.println("zset value :"+v);
}
Set<String> zsetB=zset.rangeByScore(key,0,3);
for (String v:zsetB){
System.out.println("zsetB value :"+v);
}
}
在實際開發中,不會給每⼀個使⽤的類都注⼊redisTemplate 來直接使⽤,⼀般都會對業務進⾏
簡單的包裝,最後提供出來對外使⽤。
參考:【1】:《Spring Boot Vue全棧開發實戰》
【2】:《精通 Spring Boot 42 講》
【3】:《Redis開發和運維》
【4】:
Spring Boot(三):Spring Boot 中 Redis 的使用【5】:
SpringBoot非官方教程 | 第九篇: springboot整合Redis【6】:
spring-data-redis文檔