Redis3️⃣客户端:Jedis & Spring Data Redis
- 命令行:👉 3.1 命令行客户端
- 可视化界面:👉 3.2 可视化界面
- 常用 Java 客户端:
- Jedis:
- 以 Redis 指令作为方法名,学习成本低。
- 线程不安全,需要配置连接池。
- Lettuce:
- 基于 Netty 实现,支持同步、异步、响应式编程方式。
- 支持 Redis 的哨兵、集群、管道模式。
- 线程安全。
- Redission:
- 基于 Redis 实现的分布式可伸缩 Java 数据结构集合。
- 包含 Map,Queue,Lock,Semaphore,AtomicLong 等功能,通常用于实现分布式锁。
- Jedis:
1、Jedis
1.1、基本使用(❗)
<!-- Jedis 客户端依赖 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.0</version>
</dependency>
1.1.1、获取连接
① 直接获取
private Jedis jedis;
static {
// 1、创建连接
jedis = new Jedis("主机号", "端口号");
// 2、设置密码(如果有)
jedis.auth("密码");
}
② 连接池
Jedis 实例是线程不安全的,频繁的创建和销毁会影响性能。
- 定义连接池。
public class JedisConnectionFactory {
private static final JedisPool JEDIS_POOL;
static {
// 1、创建连接池配置
JedisPoolConfig config = new JedisPoolConfig();
// 最大连接数
config.setMaxTotal(8);
// 最大,最小空闲连接
config.setMaxIdle(8);
config.setMinIdle(0);
// 最大等待时间
config.setMaxWaitMills(200);
// 2、创建连接池
JEDIS_POOL = new JedisPool(config,
"主机号",
"端口号",
"超时时间",
"密码");
}
public static Jedis getJedis() {
return JEDIS_POOL.getResources();
}
}
-
获取连接:
Jedis jedis = JedisConnectionFactory.getJedis();
1.1.2、操作 Redis
Hint:方法名与 Redis 指令名相同。
Key
jedis.set("k1", "v1");
jedis.set("k2", "v2");
jedis.set("k3", "v3");
Set<String> keys = jedis.keys("*");
System.out.println(keys.size());
for (String key : keys) {
System.out.println(key);
}
System.out.println(jedis.exists("k1"));
System.out.println(jedis.ttl("k1"));
System.out.println(jedis.get("k1"));
String
jedis.mset("str1","v1","str2","v2","str3","v3");
System.out.println(jedis.mget("str1","str2","str3"));
List
List<String> list = jedis.lrange("mylist",0,-1);
for (String element : list) {
System.out.println(element);
}
Hash
jedis.hset("hash1","userName","lisi");
System.out.println(jedis.hget("hash1","userName"));
Map<String,String> map = new HashMap<String,String>();
map.put("telphone","13810169999");
map.put("address","atguigu");
map.put("email","abc@163.com");
jedis.hmset("hash2",map);
List<String> result = jedis.hmget("hash2", "telphone","email");
for (String element : result) {
System.out.println(element);
}
set
jedis.sadd("orders", "order01");
jedis.sadd("orders", "order02");
jedis.sadd("orders", "order03");
jedis.sadd("orders", "order04");
Set<String> smembers = jedis.smembers("orders");
for (String order : smembers) {
System.out.println(order);
}
jedis.srem("orders", "order02");
zset
jedis.zadd("zset01", 100d, "z3");
jedis.zadd("zset01", 90d, "l4");
jedis.zadd("zset01", 80d, "w5");
jedis.zadd("zset01", 70d, "z6");
Set<String> zrange = jedis.zrange("zset01", 0, -1);
for (String e : zrange) {
System.out.println(e);
}
1.1.3、释放连接
调用 close() 方法释放连接。
jedis.close();
close() 源码
public void close() {
if (dataSource != null) {
Pool<Jedis> pool = this.dataSource;
this.dataSource = null;
if (isBroken()) {
pool.returnBrokenResource(this);
} else {
// 归还连接
pool.returnResource(this);
}
} else {
// 直接释放
connection.close();
}
}
1.2、高级使用
1.2.1、主从复制
private static JedisSentinelPool jedisSentinelPool=null;
public static Jedis getJedisFromSentinel(){
if(jedisSentinelPool==null){
Set<String> sentinelSet=new HashSet<>();
sentinelSet.add("172.16.88.168:26379"); // 端口为sentinal
JedisPoolConfig jedisPoolConfig =new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(10); // 最大可用连接数
jedisPoolConfig.setMaxIdle(5); // 最大闲置连接数
jedisPoolConfig.setMinIdle(5); // 最小闲置连接数
jedisPoolConfig.setBlockWhenExhausted(true); // 连接耗尽是否等待
jedisPoolConfig.setMaxWaitMillis(2000); // 等待时间
jedisPoolConfig.setTestOnBorrow(true); // 取连接的时候进行测试
jedisSentinelPool=new JedisSentinelPool("mymaster",sentinelSet,jedisPoolConfig); // 服务主机名
return jedisSentinelPool.getResource();
}
else {
return jedisSentinelPool.getResource();
}
}
1.2.2、集群
即使连接的不是主机,集群会自动切换主机存储。主机写,从机读。
无中心化主从集群。无论从哪台主机写的数据,其他主机上都能读到数据。
public class JedisClusterTest {
public static void main(String[] args) {
Set<HostAndPort>set =new HashSet<HostAndPort>();
set.add(new HostAndPort("172.16.88.168",6379)); // 任何一个端口
JedisCluster jedisCluster = new JedisCluster(set);
jedisCluster.set("k1", "v1");
System.out.println(jedisCluster.get("k1"));
}
}
2、Spring Data Redis
2.1、简介
Spring Data 是 Spring 数据操作的模块,包含对各种数据库的集成。
- 低级抽象:整合不同 Redis 客户端实现(Lettuce 和 Jedis)
- 高级抽象:
RedisTemplate
执行 Redis 操作、异常编译和序列化支持。 - 支持技术:
- 发布/订阅模型
- 哨兵和集群模式
- 基于 Lettuce 的响应式编程
- 基于 JDK、String、JSON、Spring Object/XML 的数据序列化。
- 基于 Redis 的 JDK Collection 实现。
- ...
Jedis:方法名与 Redis 指令相同,意味着 Jedis 内部封装上百个方法,代码臃肿。
RedisTemplate:将不同数据类型的操作,封装到了不同类型中。
说明 | 返回值 | |
---|---|---|
redisTemplate | 通用命令 | |
redisTemplate.opsForValue() | 操作 String 类型 | ValueOperations |
redisTemplate.opsForHash() | 操作 Hash 类型 | HashOperations |
redisTemplate.opsForList() | 操作 List 类型 | ListOperations |
redisTemplate.opsForSet() | 操作 Set 类型 | SetOperations |
redisTemplate.opsForZSet() | 操作 ZSet 类型 | ZSetOperations |
2.2、基本使用(❗)
创建 Spring Boot 工程
-
Maven 依赖:
<!-- Spring Data Redis --> <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>
-
核心配置文件:根据开发需求,application.yml 示例如下。
spring: redis: host: 服务器地址 port: 服务器端口号 timeout: 连接超时时间(ms) password: 连接密码(如果有) # 连接池配置 lettuce: pool: # 最大连接数 max-active: 8 # 最大、最小空闲数 max-idle: 8 min-idle: 0 # 最大等待时间(ms,负数表示无限制) max-wait: 100
-
使用:自动注入 RedisTemplate。
@Resource private RedisTemplate redisTemplate; @Test void testString() { String key = "name"; String value = "Jaywee"; ValueOperations operation = redisTemplate.opsForValue(); // 写入 String 数据 operation.set(key, value); // 读取 String 数据 String val = (String) operation.get(key); System.out.println(value.equals(val)); }
2.3、序列化
RedisTemplate
-
接收 Object 类型的 value。
-
写入 Redis 时,基于 RedisSerializer 将 value 序列化为字节。
-
默认序列化器(JdkSerializationRedisSerializer):会产生乱码,且占用额外内存。
解决方案
- 自定义 RedisTemplate
- StringRedisTemplate
自定义 RedisTemplate
自定义 RedisConfig 配置类,设置 RedisSerializer
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){
// 1、创建 RedisTemplate
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 2、设置连接工厂
redisTemplate.setConnectionFactory(factory);
// 3、创建序列化对象
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// 4、设置 key 和 hashKey 的序列化方式:String
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
// 5、设置 value 和 hashValue 的序列化方式:JSON
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
}
测试:写入对象
-
测试代码:
@Test void testSaveUser() { String key = "user:101"; redisTemplate.opsForValue().set(key, new User("Vz", 21)); User user = (User) redisTemplate.opsForValue().get(key); System.out.println(user); }
-
查看结果:
-
现象:JSON 序列化器为了实现反序列化时的类型判断,会将类的全限类名存入 Redis。
-
问题:占用额外内存。
-
StringRedisTemplate(❗)
为了节省内存,通常不会使用 JSON 序列化器,而是统一使用 String 序列化器。
- 规定只能存储 String 类型的 key-value。
- 需要存储 Java 对象时,提前将对象序列化为 JSON 字符串后存入。
StringRedisTemplate:Spring 内置,K-V 序列化方式都是 String。
-
测试:以 ObjectMapper 工具类为例。
@Resource private StringRedisTemplate redisTemplate; @Test void testSaveUser() throws JsonProcessingException { String key = "user:101"; ValueOperations operations = redisTemplate.opsForValue(); // 1、序列化 ObjectMapper objectMapper = new ObjectMapper(); String userJson1 = objectMapper.writeValueAsString(new User("Vz", 21)); // 2、写入数据 operations.set(key, userJson1); // 3、读取数据 String userJson2 = operations.get(key); // 4、反序列化 User user = objectMapper.readValue(userJson2, User.class); System.out.println(user); }
-
查看结果:仅存储对象信息,不会占用额外内存。