一、简介
redis是一个非关系型数据库NoSQL(No Structured Query Language)数据库,数据以键值对存在(key 和 value)
(一)非关系型数据库简识
1.结构化与非结构化
像如上,该表便被各种条件框定住而写死了,该列的值一定是什么特性,是什么类型,这样就实现了结构化
redis的键值对型(包括以json数据作为值)数据存储,不固定值的类型和特性
2.关联的与非关联的
sql中,如上表,一个order通过user_id item_id 两个值关联了两个表,使得两个表中的单独对任一表的修改是会关联传递的
如redis中,像刚刚的order订单表,在redis中被以json嵌套的形式存储记录,张三的订单和李四的订单内容是单独例开的
3.在事务的差别
在sql数据库中,满足ACID(Atomicity原子性 Consistency一致性 Isolation隔离性 Durability持久性)理论
在nosql数据库中,只能满足基本的一致,即BASE理论
(二)redis简介
特征:
键值型数据存储
单线程,每条命令具备原子性,不受下一条命令入侵
低延迟,速度快(基于内存,IO多路复用)
支持数据持久化
支持主从集群、分片集群,即从节点可以备份主节点数据,主从节点可以做读写分离;分片,即数据拆分,存储到不同节点
支持多语言客户端
二、数据结构
key-value类型,key一般为string,但是value可以各种各样,包括string,hash,List,Set,SortedSet五种基本类型,BigMap,HyperLog,GEO三种特殊类型
三、命令
redis是通过命令行进行操作数据库的,对不同的类型数据的命令也不尽相同,在官网(https://redis.io/commands)可查看
四、Redis的Java客户端
(一)分类
1.Jedis
是以命令作为方法名称,所以简单使用,学习成本低。但是Jedis实例是线程不安全的,多线程下需要基于连接池来使用
2.Lettuce
是基于Netty实现的,支持同步、异步和响应式编程方式,而且线程安全的,支持redis的哨兵模式、集群模式和管道模式,推荐使用
Spring整合了Jedis和Lettuce为SpringDataRedis
(二)Jedis入门
1.引入依赖
进入Jedis官网查看自己jdk和redis版本适配的jedis版本,我jdk21
XML
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.2.0</version>
</dependency>
2.建立连接
Jedis类是jedis客户端依赖中自带的类
3.测试String
4.释放资源
java
package com.itheima.test;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;
public class JedisTest {
private Jedis jedis;
@BeforeEach
void setUp(){
//1.建立连接
jedis = new Jedis("127.0.0.1",6379);
//2.设置密码
jedis.auth("123456");
//3.选择库
jedis.select(0);
}
@Test
void testString(){
//存入数据
String result = jedis.set("name", "虎哥");
System.out.println("result = "+ result);
//获取数据
String name = jedis.get("name");
System.out.println(
"name = "+ name
);
}
@AfterEach
void tearDown(){
//释放空间
if(jedis != null){
jedis.close();
}
}
}
(三)Jedis连接池
Jedis本身是线程不安全的,且频繁的创建和销毁连接会有性能损耗,推荐使用Jedis连接池代替Jedis直连的方式
创建连接池工厂:
java
package com.itheima.jedis.util;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.time.Duration;
public class JedisConnectionFactory {
private static final JedisPool jedisPool;
static {
//配置连接池
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(8);//最大连接数
poolConfig.setMaxIdle(8);//最大空闲连接,即便没有人连接,池子里也可以预备这么多的连接,而不用重新建立
poolConfig.setMinIdle(0);//最小空闲连接数,超过一段时间后如果连接一直没被人连接,则释放,直到最低值
poolConfig.setMaxWait(Duration.ofMillis(1000));//等待时长
//poolConfig.setMaxWaitMillis(1000); 该方法被遗弃,用Duration.of....标识时间
//创建连接池对象
jedisPool = new JedisPool(poolConfig,"127.0.0.1",6379,1000,"123456");
}
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
然后在jedis连接类就可以修改建立连接的方法为获取连接池
java
@BeforeEach
void setUp(){
//1.建立连接
// jedis = new Jedis("127.0.0.1",6379);
jedis = JedisConnectionFactory.getJedis();
//2.设置密码
jedis.auth("123456");
//3.选择库
jedis.select(0);
}
(四)SpringDataRedis入门
根据spring.io官网的信息,目前最新正式版的SpringDataRedis为3.4.0
特点(简述)
对Jedis和Lettuce的整合,提供了RedisTemplate统一API来操作Redis,支持基于JDK、Json、字符串、Spring对象的数据序列化及反序列化,支持基于Redis的JDKCollection实现
(五)RedisTemplate工具类
在该工具类内封装了各种对Redis的操作,并将不同的数据类型的操作API封装到了不同的类型中:
1.引入依赖
使用idea自带的springboot的initializr,插件处勾选lombok和SpringDataRedis(Access+Driver)即可
然后引入commons-pool依赖,因为jedis和lettuce都基于这个池
XML
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</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>
<!-- 连接池依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</dependencies>
2.配置文件
Crystal
spring:
application:
name: RedisTemplateDemo
data:
redis:
host: 127.0.0.1
port: 6379
password: 123456
lettuce:
pool:
max-active: 8 #最大连接
max-idle: 8 #最大空闲
min-idle: 0 #最小空闲
max-wait: 100 #连接等待时间,单位毫秒
3.注入RedisTemplate
4.编写测试
java
@SpringBootTest
class RedisTemplateDemoApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
//写入一条String数据
redisTemplate.opsForValue().set("name","Jack");
// 获取String数据
Object name = redisTemplate.opsForValue().get("name");
System.out.println("name = "+ name);
}
}
(六)RedisTemplate的序列化RedisSerializer
RedisTemplate可以接受任意Object作为值写入Redis,只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是OutputStream输出流形式,缺点是:
可读性差 内存占用较大
所以我们改变默认的序列化方法
有两种:
StringRedisSerializer 用于string
GenericJackson2JacksonRedisSerializer 用于其他值
1.自定义序列化方式
java
package com.heima.redistemplatedemo.redis.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 RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
// 创建RedisTemplate对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
//设置连接工厂
template.setConnectionFactory(connectionFactory);
//创建Json序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//设置Key的序列化
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
//设置Value的序列化
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
//返回
return template;
}
}
2.通过自定义序列化方式存储中文字符串
java
@Test
void contextLoads() {
//写入一条String数据
redisTemplate.opsForValue().set("name","虎哥");
// 获取String数据
Object name = redisTemplate.opsForValue().get("name");
System.out.println("name = "+ name);
}
3.通过自定义序列化方式存储对象
java
@Test
void testSaveUser(){
//写入数据
redisTemplate.opsForValue().set("user:100",new User("虎哥",21));
//获取数据
User o = (User) redisTemplate.opsForValue().get("user:100");
System.out.println("o = "+ o);
}
4.序列化与反序列化------StringRedisTemplate类
用json序列化器存储数据,为了方便反序列化,会存储其类的class路径,这在数据量庞大时占用了大量内存
为了节省内存空间,我们并不会用json序列化器来处理Value,而是统一使用String序列化器,要求只存储String类型的key和value。当真的需要存储Java对象时,手动完成对象的序列化和反序列化
不过,好消息,Spring默认提供了一个StringRedisTemplate类,它的key和value序列化方式默认是String,省去了我们配置自定义RedisTemplate的时间
java
package com.heima.redistemplatedemo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.heima.redistemplatedemo.redis.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.StringRedisTemplate;
@SpringBootTest
class RedisStringTests {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
void contextLoads() {
//写入一条String数据
stringRedisTemplate.opsForValue().set("name","虎哥");
// 获取String数据
Object name = stringRedisTemplate.opsForValue().get("name");
System.out.println("name = "+ name);
}
//SpringMvc中默认使用的Json处理工具
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testSaveUser() throws JsonProcessingException {
//创建对象
User user = new User("虎哥",21);
//手动序列化
String json = mapper.writeValueAsString(user);
//写入数据
stringRedisTemplate.opsForValue().set("user:200",json);
//获取数据
String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
//手动反序列化
mapper.readValue(jsonUser, User.class);//手动将该string按照User格式反序列化
System.out.println("JsonUser = "+ jsonUser);
}
}
以后可以通过手动打包手动序列化和反序列化的进程,进一步简化代码