前言
之前只是在項(xiàng)目簡單使用了Redis(只是充當(dāng)緩存層實(shí)現(xiàn)),對Redis的體系技術(shù)沒深入了解,最近時間比較充裕,所以再次復(fù)習(xí)鞏固Redis,然后打算寫幾篇博客記錄以及分享所復(fù)習(xí)的Redis知識。
- Redis從入門到實(shí)戰(zhàn):入門篇
- Redis從入門到實(shí)戰(zhàn):實(shí)戰(zhàn)篇
- Redis從入門到實(shí)戰(zhàn):進(jìn)階篇
- Redis從入門到實(shí)戰(zhàn):完結(jié)篇
Redis從入門到實(shí)戰(zhàn):實(shí)戰(zhàn)篇
- Redis Java客戶端介紹
- Jedis操作Redis
- SpringData-Redis操作Redis
- SpringBoot操作Redis
Redis Java客戶端介紹
Redis Java客戶端主要有三種:Jedis、Lettuce、Redisson。
Jedis
- Jedis是Redis的Java實(shí)現(xiàn)的客戶端,其API提供了比較全面的Redis命令的支持。
- Jedis中的方法調(diào)用是比較底層的暴露的Redis的API,也即Jedis中的Java方法基本和Redis的API保持著一致,了解Redis的API,也就能熟練的使用Jedis。
- Jedis使用阻塞的I/O,且其方法調(diào)用都是同步的,程序流需要等到sockets處理完I/O才能執(zhí)行,不支持異步。Jedis客戶端實(shí)例不是線程安全的,所以需要通過連接池來使用Jedis。
- Jedis僅支持基本的數(shù)據(jù)類型如:String、Hash、List、Set、Sorted Set。
Lettuce
- Lettuce是高級Redis客戶端,用于線程安全同步,異步和響應(yīng)使用,支持集群,Sentinel,管道和編碼器。目前springboot默認(rèn)使用的客戶端。
- Lettuce基于Netty框架的事件驅(qū)動的通信層,其方法調(diào)用是異步的。Lettuce的API是線程安全的,所以可以操作單個Lettuce連接來完成各種操作。
Redisson
- Redisson實(shí)現(xiàn)了分布式和可擴(kuò)展的Java數(shù)據(jù)結(jié)構(gòu),提供很多分布式相關(guān)操作服務(wù),例如,分布式鎖,分布式集合,可通過Redis支持延遲隊(duì)列。和Jedis相比,功能較為簡單,不支持字符串操作,不支持排序、事務(wù)、管道、分區(qū)等Redis特性。Redisson的宗旨是促進(jìn)使用者對Redis的關(guān)注分離,從而讓使用者能夠?qū)⒕Ω械胤旁谔幚順I(yè)務(wù)邏輯上。
- Redisson中的方法則是進(jìn)行比較高的抽象,每個方法調(diào)用可能進(jìn)行了一個或多個Redis方法調(diào)用。
- Redisson基于Netty框架的事件驅(qū)動的通信層,其方法調(diào)用是異步的。Redisson的API是線程安全的,所以可以操作單個Redisson連接來完成各種操作。
- Redisson不僅提供了一系列的分布式Java常用對象,基本可以與Java的基本數(shù)據(jù)結(jié)構(gòu)通用,還提供了許多分布式服務(wù),其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service)。
Jedis操作Redis
上面介紹了三種Redis Java客戶端,接下來就使用Jedis操作Redis,首先引入Jedis依賴:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.1.0</version>
</dependency>
在使用Jedis的時候,有兩種方式可選:
- 單機(jī)版Jedis,對應(yīng)
redis.clients.jedis.Jedis類。 - Jedis連接池,對應(yīng)
redis.clients.jedis.JedisPoolConfig類。
單機(jī)版Jedis
單機(jī)版的Jedis就是一旦與Redis建立連接成功后,然后進(jìn)行一系列對Redis的操作之后,就要關(guān)閉Redis連接對象,否則會占用系統(tǒng)資源。
@Test
public void jedis(){
Jedis jedis = null;
try {
jedis = new Jedis("localhost",6379);//設(shè)置地址和端口
// jedis.auth(PASSWORD);//如果redis服務(wù)器配置了需要密碼,此處必須設(shè)置
jedis.select(5);//選擇指定數(shù)據(jù)庫
//存儲集合到redis,并取出
jedis.lpush("names","xxxq","xxq","xq");
System.out.println(jedis.lrange("names", 0, -1));
}finally {
jedis.close();
}
}
Jedis連接池
以前我們使用MySQL、Oracle等關(guān)系型數(shù)據(jù)庫的時候,都會使用數(shù)據(jù)庫連接池,比如:C3P0、Druid等等。在Jedis中,也提供了一個Redis連接池來管理Redis連接對象,就是redis.clients.jedis.JedisPool,還有一個對Jedis連接池配置的類:redis.clients.jedis.JedisPoolConfig,下面我們對它進(jìn)行配置。
- 配置連接池需要一些參數(shù),把這些參數(shù)單獨(dú)放在一個配置文件中:redis.properties
redis.host = xxx redis.port = 6379 redis.password = xxx redis.timeout = 5000 redis.maxTotal = 100 redis.maxIdle = 20 redis.minIdle = 5 - 下面封裝一個Jedis連接池工具類,簡化后續(xù)的開發(fā)工作。
public class JedisPoolUtils { private static JedisPool pool = null; /** * 讀取屬性配置文件,配置Jedis連接池 */ static { Properties properties = new Properties(); try { properties.load(JedisPoolUtils.class.getClassLoader().getResourceAsStream("redis.properties")); } catch (IOException e) { e.printStackTrace(); } JedisPoolConfig poolConfig = new JedisPoolConfig(); int maxIdle = new Integer(properties.getProperty("redis.maxIdle")); int minIdle = new Integer(properties.getProperty("redis.minIdle")); int maxTotal = new Integer(properties.getProperty("redis.maxTotal")); poolConfig.setMaxIdle(maxIdle); poolConfig.setMinIdle(minIdle); poolConfig.setMaxTotal(maxTotal); //設(shè)置主機(jī) String host = properties.getProperty("redis.host"); //設(shè)置端口號 int port = new Integer(properties.getProperty("redis.port")); //創(chuàng)建Jedis連接池 pool = new JedisPool(poolConfig,host,port); } /** 獲取單機(jī)版Jedis */ public static Jedis getJedis(){ return pool.getResource(); } } - 使用Jedis連接池創(chuàng)建單機(jī)版Jedis,測試是否配置成功。使用Jedis連接池,我們不需要手動關(guān)閉Jedis連接對象。
public class JedisPoolTest{ public static void main(String[] args) { Jedis jedis = JedisPoolUtils.getJedis(); System.out.println("Redis服務(wù)正在運(yùn)行:"+jedis.ping()); //set命令 jedis.set("name", "zwq"); System.out.println(jedis.get("name")); } } 執(zhí)行結(jié)果: Redis服務(wù)正在運(yùn)行:PONG zwq
SpringData-Redis操作Redis
- Jedis支持五種數(shù)據(jù)類型(string/hash/list/set/zset/)的操作,但在Java中我們卻通常以類對象為主,所以在實(shí)際項(xiàng)目中,我們需要將Redis存儲的五種數(shù)據(jù)類型與Java對象之間進(jìn)行切換,我們可以自己編寫一些工具類,實(shí)現(xiàn)兩者之間的轉(zhuǎn)換,但是涉及到許多對象的時候,這其中無論工作量還是工作難度都是很大的,所以總體來說,就操作對象而言,使用原生的Jedis還是挺難的,好在Spring對這些進(jìn)行了封裝和支持,也就是
SpringData-Redis。 - 引入SpringData-Redis依賴
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>2.2.4.RELEASE</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.1.0</version> </dependency> - 接下來我們使用Spring配置Jedis連接池。
#redis.properties配置文件 redis.host = xxx redis.port = 6379 redis.password = xxx redis.timeout = 5000 redis.maxTotal = 100 redis.maxIdle = 20 redis.minIdle = 5<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <!--Redis服務(wù)地址--> <property name="hostName" value="${redis.host}"/> <!--端口號--> <property name="port" value="${redis.port}"/> <!--如果有密碼則需要配置密碼--> <!--<property name="password" value="${redis.password}"/>--> <!--連接池配置--> <property name="poolConfig" ref="poolConfig"/> </bean> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="connectionFactory"/> <!--加載屬性文件--> <context:property-placeholder location="classpath:redis.properties"/> <!--Spring整合配置,連接池配置--> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!--最大連接數(shù)--> <property name="maxTotal" value="${redis.maxTotal}"/> <!--最大空閑數(shù)--> <property name="maxIdle" value="${redis.maxIdle}"/> <!--最大等待時間--> <property name="maxWaitMillis" value="20000"/> <!--最小空閑數(shù)--> <property name="minIdle" value="${redis.minIdle}"/> </bean> </beans> - 上面配置了RedisTemplate,因?yàn)槠胀ǖ腞edis連接對象沒有辦法直接將Java對象直接存入Redis內(nèi)存中,我們需要替代的方案:將對象序列化(可以簡單的理解為實(shí)現(xiàn)Serializable接口)。我們可以把對象序列化之后存入Redis緩存中,然后在取出的時候又通過轉(zhuǎn)換器,將序列化之后的對象反序列化回對象,這樣就完成了我們的要求。RedisTemplate可以幫助我們完成這份工作,它會找到對應(yīng)的序列化器去轉(zhuǎn)換Redis的鍵值。
- 創(chuàng)建用戶實(shí)體類,實(shí)現(xiàn)Serializable接口。
@Data public class User implements Serializable { private String name; private Integer age; } - 測試Spring配置是否成功。
@Test public void springDataRedis(){ ApplicationContext context = new ClassPathXmlApplicationContext("redis.xml"); RedisTemplate template = context.getBean(RedisTemplate.class); User user = new User(); user.setName("zhangsan"); user.setAge(22); template.opsForValue().set("zhangsan",user); User u = (User) template.opsForValue().get("zhangsan"); System.out.println(u); } 運(yùn)行結(jié)果: User(name=zhangsan, age=22) -
從運(yùn)行結(jié)果來看,SpringData-Redis已經(jīng)自動幫我們序列化對象與反序列化對象了,使用起來非常簡便。但是當(dāng)我們查看Redis服務(wù)器的時候,發(fā)現(xiàn)存入的數(shù)據(jù)亂碼了,如下圖所示:
在這里插入圖片描述 - 上面說了RedisTemplate通過序列化器來轉(zhuǎn)換Redis的鍵值,而RedisTemplate默認(rèn)采用的是JDK的序列化策略,保存的key和value都是采用此序列化器完成的。出現(xiàn)上面問題說明就是JDK序列化器在序列化與反序列化時編碼問題,我們可以更改RedisTemplate序列化器,除了JDK序列化器JdkSerializationRedisSerializer,還有一個字符串序列化器StringRedisSerializer,因?yàn)镽edis的key都是字符串類型,所以我們將key的序列化器改為StringRedisSerializer,而Value使用默認(rèn)的JdkSerializationRedisSerializer。
- 接下來按照上面總結(jié)的原因來更改上面配置文件:redis.xml
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="connectionFactory" p:keySerializer-ref="stringSerializer" p:valueSerializer-ref="jdkSerializer"/> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" id="stringSerializer"/> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" id="jdkSerializer"/>在這里插入圖片描述
從上圖來看,修改序列化器之后,key已經(jīng)不再亂碼了,但是值又亂碼了。按照上面的分析可以將value的序列化器改為StringRedisSerializer就不會亂碼了。
SpringBoot操作Redis
上面我們已經(jīng)介紹了原生Jedis操作Redis與SpringData-Redis操作Redis,雖然沒有講大量的API,但是我們已經(jīng)學(xué)會怎么使用了,剩下的就是大家自己來操作API實(shí)現(xiàn)效果了。接下來再介紹在SpringBoot中如何操作Redis。不管是SpringData-Redis,還是在SpringBoot中,都是通過RedisTemplate操作Redis,只不過SpringBoot做了更多的封裝,讓我們操作Redis更加簡便。接下來先介紹RedisTemplate。
RedisTemplate
-
RedisTemplate只是提供操作Redis的模板API,底層還是使用Redis Java客戶端來實(shí)現(xiàn)。
在這里插入圖片描述 - 這里說一個細(xì)節(jié),SpringBoot2.0之前默認(rèn)是使用Jedis客戶端底層實(shí)現(xiàn),SpringBoot2.0之后就改為使用Lettuce客戶端底層實(shí)現(xiàn),如果需要在SpringBoot2.0之后使用Jedis,則需要排除Lettuce依賴,如下所示:
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.1.0</version> </dependency> <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> - 接下來配置Redis連接池,SpringBoot省去了繁雜的XML配置,直接在application.properties配置即可。
#redis配置 #Redis服務(wù)器地址 spring.redis.host=127.0.0.1 #Redis服務(wù)器連接端口 spring.redis.port=6379 # Redis服務(wù)器連接密碼(默認(rèn)為空) spring.redis.password= #Redis數(shù)據(jù)庫索引(默認(rèn)為0) spring.redis.database=0 #連接池最大連接數(shù)(使用負(fù)值表示沒有限制) spring.redis.jedis.pool.max-active=50 #連接池最大阻塞等待時間(使用負(fù)值表示沒有限制) spring.redis.jedis.pool.max-wait=3000ms #連接池中的最大空閑連接 spring.redis.jedis.pool.max-idle=20 #連接池中的最小空閑連接 spring.redis.jedis.pool.min-idle=2 #連接超時時間(毫秒) spring.redis.timeout=5000ms - 編寫測試代碼,測試配置是否成功。
@RunWith(SpringRunner.class) @SpringBootTest public class AppTest { @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void test(){ stringRedisTemplate.opsForValue().set("name","zwq"); System.out.println(stringRedisTemplate.opsForValue().get("name")); } } 運(yùn)行結(jié)果: zwq -
上面使用的是StringRedisTemplate,它繼承了RedisTemplate,從名字看它就是支持Value為String類型的讀寫操作,也就是Value使用了StringRedisSerializer序列化器,截圖驗(yàn)證:
在這里插入圖片描述
在這里插入圖片描述 - 我們驗(yàn)證讀寫對象會不會出現(xiàn)之前使用SpriingData-Redis的亂碼問題,編寫一個用戶實(shí)體類
@Data public class User implements Serializable { private String name; private Integer age; } - 上面都已經(jīng)配置好了,所以直接編寫測試代碼即可。
@Test public void saveUser(){ User user = new User(); user.setName("zhangsan"); user.setAge(22); redisTemplate.opsForValue().set("zhangsan",user); User zhangsan = (User) redisTemplate.opsForValue().get("zhangsan"); System.out.println(zhangsan); } 運(yùn)行結(jié)果: User(name=zhangsan, age=22) -
在程序代碼中讀寫也是沒問題,但是寫入到Redis服務(wù)器上就亂碼了。
在這里插入圖片描述 - 這個原因也是序列化與反序列化過程中產(chǎn)生的問題,接下來開始指定序列化器。在SpringBoot中沒有了xml配置,所以需要編寫一個配置類來配置序列化器,如下代碼:
@Configuration public class RedisCnfig { @Autowired RedisConnectionFactory factory; @Bean public RedisTemplate<Object, Object> redisTemplate() { RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(factory); //StringRedisSerializer序列化器 StringRedisSerializer stringRedisSerializer =new StringRedisSerializer(); //JdkSerializationRedisSerializer序列化器 JdkSerializationRedisSerializer jdkRedisSerializer = new JdkSerializationRedisSerializer(); redisTemplate.setValueSerializer(jdkRedisSerializer); redisTemplate.setKeySerializer(stringRedisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } } -
再執(zhí)行上面測試代碼,發(fā)現(xiàn)key已經(jīng)不亂碼了,而Value還是出現(xiàn)亂碼。
在這里插入圖片描述 - 通過把Value的序列化器修改為JSON序列化器,也就是對象與JSON之間的轉(zhuǎn)換,這樣就可以解決亂碼的問題。
除了改配置之外,還需要引入下面依賴。@Configuration public class RedisCnfig { @Autowired RedisConnectionFactory factory; @Bean public RedisTemplate<Object, Object> redisTemplate() { RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(factory); //StringRedisSerializer序列化器 StringRedisSerializer stringRedisSerializer =new StringRedisSerializer(); //JdkSerializationRedisSerializer序列化器 JdkSerializationRedisSerializer jdkRedisSerializer = new JdkSerializationRedisSerializer(); //Json序列化器 GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); // redisTemplate.setValueSerializer(jdkRedisSerializer); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setKeySerializer(stringRedisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } }<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.5.4</version> </dependency> -
再執(zhí)行上面的測試代碼,發(fā)現(xiàn)Key、Value都不會亂碼了。
在這里插入圖片描述
總結(jié)
- 這樣就把Java操作Redis的技術(shù)講完了,還是和上面說了,這里講的API比較少,我們還是得自己敲,把API練熟悉了,才能把Redis運(yùn)用自如,雖然我也還沒把Redis運(yùn)用自如。
- 大家覺得OK的話,不妨來個??或者關(guān)注也行。






