Redis的Java客户端
在Redis官网中提供了各种语言的客户端,地址:https://redis.io/docs/clients/
其中Java客户端也包含很多:
标记为*的就是推荐使用的java客户端,包括:
- Jedis 和 Lettuce:这两个主要是提供了 Redis 命令对应的API,方便我们操作Redis,而SpringDataRedis又对这两种做了抽象和封装,因此我们后期会直接以 SpringDataRedis 来学习。
- Redisson:是在 Redis 基础上实现了分布式的可伸缩的 java 数据结构,例如 Map、Queue 等,而且支持跨进程的同步机制:Lock、Semaphore 等待,比较适合用来实现特殊的功能需求。
Jedis客户端
Jedis的官网地址: https://github.com/redis/jedis
快速入门
我们先来个快速入门:
1)引入依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>redis_code</artifactId> <groupId>tech.chen</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>redis-client-jedis</artifactId>
<dependencies> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.7.0</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.7.0</version> <scope>test</scope> </dependency> </dependencies> </project>
|
2)建立连接
新建一个单元测试类,内容如下:
1 2 3 4 5 6 7
| private Jedis jedis; @BeforeEach void setUp() { jedis = new Jedis("192.168.200.130",6379); jedis.auth("123321"); jedis.select(0); }
|
3)测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Test void string() { String set = jedis.set("name", "czk"); System.out.println("set = " + set); String name = jedis.get("name"); System.out.println("name = " + name); }
@Test void hash() { long hset = jedis.hset("login:user:1", "name", "阿陈"); System.out.println("hset = " + hset); String name = jedis.hget("login:user:1", "name"); System.out.println("name = " + name); }
|
4)释放资源
1 2 3 4 5 6
| @AfterEach void afterAll() { if(jedis!=null){ jedis.close(); } }
|
连接池
Jedis 本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用Jedis连接池代替Jedis的直连方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
public class JedisPoolTest { private static JedisPool jedisPool; @BeforeEach void setUp() { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxWaitMillis(1000); config.setMinIdle(0); config.setMaxIdle(8); config.setMaxTotal(8); jedisPool = new JedisPool(config,"192.168.200.130",6379,1000,"123321"); } public static Jedis getJedis(){ return jedisPool.getResource(); }
@Test void string() { String set = getJedis().set("name", "小猪"); System.out.println("set = " + set); String name = getJedis().get("name"); System.out.println("name = " + name); }
@Test void hash() { long hset = getJedis().hset("login:user:2", "name", "小猪"); System.out.println("hset = " + hset); String name = getJedis().hget("login:user:2", "name"); System.out.println("name = " + name); } }
|
SpringDataRedis客户端
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,官网地址:https://spring.io/projects/spring-data-redis
- 提供了对不同Redis客户端的整合(Lettuce和Jedis)
- 提供了RedisTemplate统一API来操作Redis
- 支持Redis的发布订阅模型
- 支持Redis哨兵和Redis集群
- 支持基于Lettuce的响应式编程
- 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化
- 支持基于Redis的JDKCollection实现
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:
快速入门
SpringBoot已经提供了对SpringDataRedis的支持,使用非常简单。
首先,新建一个maven项目,然后按照下面步骤执行:
1)引入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.6</version> <relativePath/> </parent> <groupId>tech.chen</groupId> <artifactId>spring-client-spring-data-redis</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-client-spring-data-redis</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
|
2)配置Redis
1 2 3 4 5 6 7 8 9 10 11
| spring: redis: host: 192.168.200.130 port: 6379 password: 123321 lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 max-wait: 1000ms
|
3)注入RedisTemplate
因为有了SpringBoot的自动装配,我们可以拿来就用:
1 2 3 4 5
| @SpringBootTest class RedisStringTests { @Autowired private RedisTemplate redisTemplate; }
|
4)编写测试
1 2 3 4 5 6 7 8 9 10 11 12 13
| @SpringBootTest class SpringClientSpringDataRedisApplicationTests { @Autowired private RedisTemplate redisTemplate; @Test void string() { redisTemplate.opsForValue().set("name", "阿陈"); Object name = redisTemplate.opsForValue().get("name"); System.out.println("name = " + name); } }
|
查看 redisGUI,发现 redis 中存的数据并不像我们预期的那样name:阿陈
,为什么呢?请看下一节:序列化
自定义序列化
redis 序列化器 有以下几种:常用的是StringRedisSerializer
和 Jackson2JsonRedisSerializer
。
RedisTemplate可以接收任意Object作为值写入Redis:
只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是这样的:
缺点:
我们可以自定义RedisTemplate的序列化方式,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
@Configuration public class RedisConfig {
@Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); template.setKeySerializer(RedisSerializer.string()); template.setHashKeySerializer(RedisSerializer.string()); template.setValueSerializer(jsonRedisSerializer); template.setHashValueSerializer(jsonRedisSerializer); return template; } }
|
这里采用了JSON序列化来代替默认的JDK序列化方式,一般我们使用redis的时候,redis 的 key 都是 String 类型的,故在对 key 使用序列化器时,都采用RedisSerializer.string()
的方式。再次测试 ,结果如图:
如果我们 是存 java 对象,那 redis 会怎样显示?
1 2 3 4 5 6 7 8 9 10 11
| @Test void stringObjTest() { User user = new User(); user.setName("阿陈"); user.setAge(21); redisTemplate.opsForValue().set("login:user:101", user); User user1 = (User) redisTemplate.opsForValue().get("login:user:101"); System.out.println("user1 = " + user1); }
|
整体可读性有了很大提升,并且能将 Java 对象自动的序列化为 JSON 字符串,并且查询时能自动把JSON反序列化为Java对象。不过,其中记录了序列化时对应的 class 名称,目的是为了查询时实现自动反序列化。这会带来额外的内存开销。
StringRedisTemplate
为了节省内存空间,我们可以不使用 JSON 序列化器来处理 value,而是统一使用 String 序列化器,要求只能存储 String 类型的 key和 value。当需要存储Java对象时,手动完成对象的序列化和反序列化。
因为存入和读取时的序列化及反序列化都是我们自己实现的,SpringDataRedis 就不会将 class 信息写入 Redis 了。
这种用法比较普遍,因此 SpringDataRedis 就提供了 RedisTemplate 的子类:StringRedisTemplate
,它的 key 和 value 的序列化方式默认就是 String 方式。
省去了我们自定义RedisTemplate的序列化方式的步骤,而是直接使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
@SpringBootTest public class StringRedisTemplateTest { @Autowired private StringRedisTemplate stringRedisTemplate;
private static final ObjectMapper objectMapper = new ObjectMapper(); @Test void stringTest() { stringRedisTemplate.opsForValue().set("name", "阿陈"); Object name = stringRedisTemplate.opsForValue().get("name"); System.out.println("name = " + name); } @Test void stringObjTest() { User user = new User(); user.setName("阿陈"); user.setAge(21); String userJson = null; try { userJson = objectMapper.writeValueAsString(user); stringRedisTemplate.opsForValue().set("login:user:101", userJson); String result = stringRedisTemplate.opsForValue().get("login:user:101"); User user1 = objectMapper.readValue(result, User.class); System.out.println("user1 = " + user1); } catch (JsonProcessingException e) { e.printStackTrace(); } } }
|