Redis专题-基础篇
题记
本文涵盖了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);}