题记
本文涵盖了Redis的各种数据结构和命令,Redis的各种常见Java客户端的应用和最佳实践
jedis案例github地址:https://github.com/whltaoin/fedis_java_demo
SpringbootDataRedis案例github地址:https://github.com/whltaoin/springbootDataRedis_demo
一、初始Redis
SQL与NoSQL对比

- 结构化(Structured)对比
- SQL具有严格的结构化格式(左图)
- NoSQL的数据存储格式相对随意(右图)

- 关系对比
- SQL在表和表间**可能存在联系**,在操作一张表时,需要考虑对应关联表的完整性。

2. NoSQL数据间是**<font style="color:#DF2A3F;">无关联</font>**的,(下图为存储方式)

- 查询对比
- SQL查询有**固定的语法**,只要是SQL都可以使用相同的语句查询。

2. NoSQL没有固定的语法,每个NoSQL都有自己的语法糖(下图为查询同一个东西,出现了不同的语法)

- SQL需要满足**事务的ACID特性**(原子性、一致性、隔离性、持久性)
- NoSQL只是**基本满足事务的ACID**
- 差异总结:

初始Redis

- 简介
Redis(远程词典服务器),是一个基于内存的**键值型NoSQL**数据库
- 特征:
- 键值型:key-value
- 单线程:命令具有原子性
- 低延迟、数据块原因:(基于**内存**、IO多路复用、良好的编码)
- 支持数据持久化
- 支持主从集群(主服务器和副服务器)、分片集群(将数据拆分,分别存在不同的服务器上)
- 支持多语言客户端(java、C…)
安装Redis(Linux安装)
忽略…
二、Redis常见命令
数据类型介绍
- String
1. - Hash
- 哈希表
- List
- 有序集合,本质是链表
- Set
- 无序集合,不可重复
- SortedSet
- 有序集合,不可重复
- GEO
- 地理坐标

通用命令
- 查询所有通用命令:
help @generic

- 查看符合模版的所有key
- 注意:不建议在生产环境设备上使用该命令,因为模糊查询,效率不高且浪费资源。

# 语法
keys +[pattern]

- 删除key
- 可以单个删除,也可以多个一起删除
del [key]

删除一个或多个示例

- 判断key是否存在
- 可以判断单个,也可以判断多个
exists [key]

判断单个和多个示例

- 给key设置有效期,到期后自动删除(单位秒)
- TTL查看可以的有效时间
expire [key] [seconds]
ttl key

示例:设置age1的有效期为10s,到期后自动删除

- 总结

String类型


- set

- get

- mset

- mget

- incr

- incrby

- incrbyfloat

- setnx(新增,存在则不创建)
- 等价于:set [key value] nx


- setex
- 等价于:set


Key的层级格式
- 思考:

- 解决方法

- 示例

heima:user:1
herma:product:1


层级结构:

Hash类型
- Hash类型(散列),其中的value是一个**无序字典**
- 类似于java 中的HashMap结构
- 使用场景:
- 当要修改JSON数据中某个属性的值时,使用String存储的需要重新覆盖数据,而我们的需求只是**想要修改某个值**
- 常用命令

- 示例
- hset


2. hget

3. hmset

4. hmget

5. hgetall

6. hkeys

7. hvals

8. hincrby

9. hsetnx(已存在,不新建)

List类型
- Redis中的List类型于java中的LinkedList类似,可以当它是一个双向链表结构。
- 既可以支持正向检索也可以支持反向检索。
- 特征:
- 有序
- 元素可重复
- 插入和删除快
- 查询速度一般
- 使用场景举例:
- 朋友圈点赞列表,评论列表等
- 常用命令:


1. 示例:

Set类型
- Redis的Set结构和Java中的HashSet类似,可以看做一个value为null的HashMap。
- 特征:
- 无序
- 元素不可重复
- 查找快
- 支持交集、并集、差集等功能
- 单个set常见命令

1. 示例

- 多个set操作命令
- 求交集(sinter):两集合公共的


2. 求并集(sunion):所有元素合并

3. 求差集(sdiff):set中有,但是set2中没有


- 练习题:

1. 张三的好友人数

2. 张三和李四的共同好友

3. 查询那些人是张三的好友但是不是李四的好友

4. 查询张三和李四的共同好友

5. 判断李四是否是张三的好友

6. 判断张三是否是李四的好友

7. 将李四从张三的好友列表中移除

SortedSet类型

- 特性:
- 可排序
- 元素不可重复
- 查询快
- 应用场景:
- 因为可排序,常用于实现**排行榜**功能
- 常用命令

- 练习题:

1. 添加数据

2. 删除Tom

3. 获取Amy分数

4. 获取Rose排名


5. 查询80分以上人数

6. 给Amy加2分

7. 查询排名前三的同学

8. 查询80以下的同学

三、Redis的Java客户端
常用Java客户端的优缺点对比

Jedis使用(单线程)
github地址:https://github.com/whltaoin/fedis_java_demo
- 使用Jedis分为了四步骤:
- 导入依赖
- 初始化Jedis对象
- 执行Jedis中的操作方法
- 释放Jedis对象
- 具体实现
- 导入依赖
<?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"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>jedis-test</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies>
<!-- 核心--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>6.0.0</version></dependency>
<!-- 测试类--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency></dependencies></project>
b. 测试类方法内容
package cn.varin;import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;public class JedisTest {private Jedis jedis;@Beforepublic void init(){jedis = new Jedis("ip",6379);jedis.auth("密码");jedis.select(0);}@Testpublic void StringTest(){String set = jedis.set("name", "varin");System.out.println("执行set后结果为:"+set);String name = jedis.get("name");System.out.println("key为name的value为:"+name);}@Afterpublic void close(){if(jedis !=null){jedis.close();}}}
3. 执行结果

Jedis使用(使用连接池)
github地址:https://github.com/whltaoin/fedis_java_demo
- 使用步骤:
- 创建连接池
- 获取Jedis对象
- 操作Jedis对象
- 归还连接池对象
- 具体代码
- 创建连接池对象
package cn.varin.jedis.utils;import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;// Jedis连接池对象
public class JedisConnectionFactory {//static public final JedisPool jedisPool;static {// 创建配置JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();// 最大连接数jedisPoolConfig.setMaxTotal(10);// 最大空闲数jedisPoolConfig.setMaxIdle(10);// 最小空闲数jedisPoolConfig.setMinIdle(2);// 空闲等待时间jedisPoolConfig.setMaxWaitMillis(1000);jedisPool = new JedisPool(jedisPoolConfig,"host",6379,100,"password");}public static Jedis getResource(){return jedisPool.getResource();}
}
2. 测试类代码
package cn.varin;import cn.varin.jedis.utils.JedisConnectionFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;public class JedisTest {private Jedis jedis;@Beforepublic void init(){jedis = JedisConnectionFactory.getResource();jedis.select(0);}@Testpublic void StringTest(){String set = jedis.set("name", "varya");System.out.println("执行set后结果为:"+set);String name = jedis.get("name");System.out.println("key为name的value为:"+name);}@Afterpublic void close(){if(jedis !=null){jedis.close();}}}
3. 执行结果:

SpringDataRedis使用
github示例案例地址:https://github.com/whltaoin/springbootDataRedis_demo
springData介绍
- 项目地址:https://spring.io/projects/spring-data-redis#learn

- Redis模版版本信息

SpringDataRedis快速入门

SpringbootDataRedis使用步骤

基本示例
- 导入依赖
<?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>3.4.5</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>cn.varin</groupId><artifactId>springbootDataRedis_demo</artifactId><version>0.0.1-SNAPSHOT</version><name>springbootDataRedis_demo</name><description>springbootDataRedis_demo</description><url/><licenses><license/></licenses><developers><developer/></developers><scm><connection/><developerConnection/><tag/><url/></scm><properties><java.version>17</java.version></properties><dependencies>
<!-- springbootDataRedis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
<!-- pool2--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><annotationProcessorPaths><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></path></annotationProcessorPaths></configuration></plugin><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>
- 配置yml
spring:data:redis:port: 6379password: passworddatabase: 0lettuce:pool:max-active: 10max-idle: 10min-idle: 0max-wait: 100mshost: address
- 编写测试类
package cn.varin.springbootdataredis_demo;import cn.varin.springbootdataredis_demo.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;@SpringBootTest
class SpringbootDataRedisDemoApplicationTests {@AutowiredRedisTemplate redisTemplate;@Testvoid setTest() {ValueOperations valueOperations = redisTemplate.opsForValue();valueOperations.set("user:1",new User("varin",1).toString());Object o = valueOperations.get("user:1");System.out.println(o);}}
- 结果:

重构redisTemplate序列化和反序列化工具
- 问题

1. 在我们直接使用redisTemplate时,存入到redis的内容,是经过编译的字节,
2. 影响阅读性
3. 增加了存储空间
- 解决方案:
- 自定义序列化和反序列话的编码格式
- 步骤
- 建立template
- 设置连接工厂
- 设置序列化工具
- 分别对key和value设置不同的格式
package cn.varin.springbootdataredis_demo.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;@Configuration
public class RedisTamplateConfig {@Beanpublic RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){RedisTemplate<String,Object> template = new RedisTemplate<>();// 设置连接工厂template.setConnectionFactory(factory);// 创建序列化工具GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();// 对keytemplate.setKeySerializer(RedisSerializer.string());template.setHashKeySerializer(RedisSerializer.string());// 对valuetemplate.setValueSerializer(genericJackson2JsonRedisSerializer);template.setHashValueSerializer(genericJackson2JsonRedisSerializer);return template;}
}
测试类
package cn.varin.springbootdataredis_demo;import cn.varin.springbootdataredis_demo.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;@SpringBootTest
class SpringbootDataRedisDemoApplicationTests {@AutowiredRedisTemplate redisTemplate;@Testvoid setTest() {ValueOperations valueOperations = redisTemplate.opsForValue();// value为user对象valueOperations.set("user:2",new User("varin",1));Object o = valueOperations.get("user:1");System.out.println(o);}}
- 测试结果

StringRedisTamplate类使用
- 问题:
- 虽然自定义序列化工具可以解决上一问题,但是修改后在JSON字符串中会多存储一个类的包名
- 导致增大存储的空间
- 虽然自定义序列化工具可以解决上一问题,但是修改后在JSON字符串中会多存储一个类的包名
- 解决方法,
- 使用StringRedisTamplate类,在加上自己使用第三方的序列化工具进行存储。
- 优点:在存储时不会增加额外的数据
- 缺点:增加少许的代码量
- 使用StringRedisTamplate类,在加上自己使用第三方的序列化工具进行存储。
- 示例代码
@AutowiredStringRedisTemplate stringRedisTemplate;// 用于转Json格式private static final ObjectMapper mapper = new ObjectMapper();@Testvoid StringRedisTamplateTest() throws JsonProcessingException {User user = new User("varya",1);// 转JSON格式String s = mapper.writeValueAsString(user);// 写入数据stringRedisTemplate.opsForValue().set("user:3",s);// 读取数据String s1 = stringRedisTemplate.opsForValue().get("user:3");// 反序列化User user1 = mapper.readValue(s1, User.class);System.out.println(user1);}


