redis是一个
key-value
存储系统。和Memcached类似,它支持存储的value类型相对更多。
redis可以存储哪些数据类型:
- string(字符串)
- list(链表)
- set(集合)
- zset(sorted set --有序集合)
- hash(哈希类型)(不常用)
这些数据类型都支持
(增删改查)
push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性
的。
在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中
。
持久化存储
:redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis能做什么?
Redis是一个开源的,内存中的数据结构存储系统,它可以用作数据库
、缓存
和消息中间件
。
1、 缓存,毫无疑问这是Redis当今最为人熟知的使用场景。再提升服务器性能方面非常有效;
2、排行榜,如果使用传统的关系型数据库来做这个事儿,非常的麻烦,而利用Redis的SortSet数据结构能够非常方便搞定;
3、计算器/限速器,
利用Redis中原子性的自增操作
,我们可以统计类似用户点赞数、用户访问数等,这类操作如果用MySQL,频繁的读写会带来相当大的压力;限速器比较典型的使用场景是限制某个用户访问某个API的频率,常用的有抢购时,防止用户疯狂点击带来不必要的压力;
注:限速器也是对请求限流的一种实现方式。
4、好友关系,利用集合的一些命令,比如
求交集、并集、差集
等。可以方便搞定一些共同好友、共同爱好之类的功能;
5、
简单消息队列
,除了Redis自身的发布/订阅模式,我们也可以利用List来实现一个队列机制,比如:到货通知、邮件发送之类的需求,不需要高可靠,但是会带来非常大的DB压力,完全可以用List来完成异步解耦;
6、Session共享,默认Session是保存在服务器的文件中,即当前服务器,如果是集群服务,同一个用户过来可能落在不同机器上,这就会导致用户频繁登陆;采用Redis保存Session后,无论用户落在那台机器上都能够获取到对应的Session信息。
redis特点
- Redis将其数据库完全保存在内存中,仅使用磁盘进行持久化。
- 与其它键值数据存储相比,Redis有一组相对丰富的数据类型。
- Redis可以将数据复制到任意数量的从机中。
redis优点
- 异常快:
Redis非常快,每秒可执行大约110000次的设置(SET)操作,每秒大约可执行81000次的读取/获取(GET)操作。- 支持丰富的数据类型:
Redis支持开发人员常用的大多数数据类型,例如列表,集合,排序集和散列等等。这使得Redis很容易被用来解决各种问题,因为我们知道哪些问题可以更好使用地哪些数据类型来处理解决。- 操作具有原子性:
所有Redis操作都是原子操作,这确保如果两个客户端并发访问,Redis服务器能接收更新的值。- 多实用工具:
Redis是一个多实用工具,可用于多种用例,如:缓存,消息队列(Redis本地支持发布/订阅),应用程序中的任何短期数据,例如,web应用程序中的会话,网页命中计数等。
下载:https://github.com/MicrosoftArchive/redis/releases
解压到自己常用软件类磁盘,目录打开如图:
三种启动反式:
解压目录下找到redis.windows.conf
:大概在387行左右有requirepass 下添加requirepass 密码,保存,重新运行即可。(密码不一定设置成功)
下载安装RedisDesktopManager,并连接redis(关闭防火墙并设置允许远程连接此电脑)
度娘搜索,或者资源下载:https://download.csdn.net/download/m0_70083523/87216140
一堆命令使用:redisdoc.com/string/index.html
示例:
-1:表示持久化保存,永久生效
-2:表示已经过期
正数:表示多少秒后过期
一般情况下,当我们关闭redis服务和可视化界面,之前存储的数据会消失,持久化存储需要在设置。但是在一些情况下我们会触发持久化存储。
RDB 持久化机制
RDB 全称为:Redis DataBase,是 Redis 当中默认的持久化方案。当触发持久化条件时,Redis 默认会生成一个dump.rdb
文件,Redis 在重启的时候就会通过解析 dump.rdb 文件进行数据恢复。
RDB 机制触发条件:自动触发和手动触发
详细内容:https://blog.csdn.net/zhangbaidi_WinCE/article/details/127209803
说明:
持久化机制在我们第一次使用redis服务,在可视化界面添加数据时,就会产生一个文件dump.rdb,此文件的存在,会使我们修改value无效,服务关闭在开启,发现值修改不成功,就是此文件的影响(不要管文件图标是否一样)。
肯定先创建springboot项目,使用redis就要导入jar包(最新版去maven官网下载)
其实导入jar包就已将redis的相关连接配置完成,不需要在application.xxx文件中配置
<!--redis数据库连接-->
>
>org.springframework.boot >
>spring-boot-starter-redis >
>1.3.8.RELEASE >
>
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=123456
一些application.properties的相关配置:
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0
【SpringBoot自动在Spring容器中配置一个redisTemplate的Bean,所以可以直接使用redisTemplate】
使用Spring封装的RedisTemplate操作redis,常用方法:
- redisTemplate.opsForValue(); //操作字符串
- redisTemplate.opsForHash(); //操作hash
- redisTemplate.opsForList(); //操作list
- redisTemplate.opsForSet(); //操作set
- redisTemplate.opsForZSet(); //操作有序set
1.存储字符串
使用:redisTemplate.opsForValue().set("name","tom");
结果:redisTemplate.opsForValue().get("name") 输出结果为tom
2.设置失效时间
使用:redisTemplate.opsForValue().set("name","tom",10, TimeUnit.SECONDS);
TimeUnit.DAYS //天
TimeUnit.HOURS //小时
TimeUnit.MINUTES //分钟
TimeUnit.SECONDS //秒
TimeUnit.MILLISECONDS //毫秒
结果:redisTemplate.opsForValue().get("name")由于设置的是10秒失效,十秒之内查询有结果,十秒之后返回为null
3.支持整型与浮点型(increment)
使用:redisTemplate.opsForValue().increment("sex",1);
System.out.println(redisTemplate.opsForValue().get("sex"));
结果:1
4.如果key已经存在并且是一个字符串,则该命令将该值追加到字符串的末尾。如果键不存在,则它被创建并设置为空字符串,因此APPEND在这种特殊情况下将类似于SET
使用:redisTemplate.opsForValue().append("name"," hello");
System.out.println(redisTemplate.opsForValue().get("name"));
结果:tom Hello
5.截取key所对应的value字符串
System.out.println("*********"+redisTemplate.opsForValue().get("name",0,3));
结果:tom
6.返回key所对应的value值得长度
System.out.println("***************"+redisTemplate.opsForValue().size("key"));
7.存储一个对象(此类必须先序列化实现接口Serializable)
import java.io.Serializable;
import java.util.Date;
@Data
public class Provider implements Serializable {
private Integer id; //id
======================================================
RedisSerializer rs = new StringRedisSerializer();
redisTemplate.setStringSerializer(rs);
ValueOperations ops = redisTemplate.opsForValue();
ops.set("user",user);//放入redis
//取出对象
User setuser = (User) redisTemplate.opsForValue().get("user");
具体范例代码:===========================================================
@RestController
public class RedisController {
// @Autowired
// private RedisTemplate redisTemplate; //RedisTemplate是从jar包中来的
private RedisTemplate redisTemplate;
//指定用redis的序列化方式进行序列化
@Autowired(required = false)
public void setRedisTemplate(RedisTemplate redisTemplate) {
RedisSerializer stringSerializer = new StringRedisSerializer();//序列化为String
//不能反序列化
//Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//序列化为Json
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(serializer);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setHashValueSerializer(serializer);
this.redisTemplate = redisTemplate;
}
@RequestMapping("/setRedisValue")
public String setRedisValue() {
// //获得redis操作字符串对象
// ValueOperations vo = redisTemplate.opsForValue();
// vo.set("uname","Jules"); //放值
// System.out.println("uname==="+vo.get("uname")); //读取值: vo.get("key")
//支持整型与浮点型使用increment
// redisTemplate.opsForValue().increment("sex",1);
// System.out.println(redisTemplate.opsForValue().get("sex"));
//追加:
// redisTemplate.opsForValue().set("name","tom");
// redisTemplate.opsForValue().append("name"," hello");
// System.out.println(redisTemplate.opsForValue().get("name"));
//序列化:
RedisSerializer rs = new StringRedisSerializer();
redisTemplate.setStringSerializer(rs); //设置redis支持序列化
Provider pro = new Provider();
pro.setId(1001);
redisTemplate.opsForValue().set("provider",pro);
Provider provider1 = (Provider)redisTemplate.opsForValue().get("provider");
System.out.println("取对象"+provider1.getId());
return "操作成功!!!";
}
}
【2,3,4,的具体示例:https://www.zybuluo.com/wangzhuanyun/note/15】
注意:zset的使用,在添加集合时,在加入的过程就进行了排序
通过分数返回有序集合指定区间内的成员,其中有序集成员按分数值递增(从小到大)顺序排列
使用:
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-1",1.0));
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-2",6.0));
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-3",8.0));
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-4",4.0));
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-5",10.0));
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-6",2.0));
如下图:
https://www.cnblogs.com/ryanpan/p/16582749.html
StringRedisTemplate与RedisTemplate区别点:
StringRedisTemplate常用操作:
@RestController
public class RedisController2 {
@Autowired
public StringRedisTemplate stringRedisTemplate;
@RequestMapping("/StringRedisTemplateTest")
public String StringRedisTemplateTest() {
//向redis里存入数据和设置缓存时间
stringRedisTemplate.opsForValue().set("test", "100",60*10, TimeUnit.SECONDS);
//val做-1操作
stringRedisTemplate.boundValueOps("test").increment(-1);
//根据key获取缓存中的val
stringRedisTemplate.opsForValue().get("test");
//val +1
stringRedisTemplate.boundValueOps("test").increment(1);
//根据key获取过期时间
stringRedisTemplate.getExpire("test");
//根据key获取过期时间并换算成指定单位
stringRedisTemplate.getExpire("test",TimeUnit.SECONDS);
//根据key删除缓存
stringRedisTemplate.delete("test");
//检查key是否存在,返回boolean值
stringRedisTemplate.hasKey("546545");
//向指定key中存放set集合
stringRedisTemplate.opsForSet().add("red_123", "1","2","3");
//设置过期时间
stringRedisTemplate.expire("red_123",1000 , TimeUnit.MILLISECONDS);
//根据key查看集合中是否存在指定数据
stringRedisTemplate.opsForSet().isMember("red_123", "1");
//根据key获取set集合
stringRedisTemplate.opsForSet().members("red_123");
return "操作成功!!!";
}
使用时注意事项:
- 当你的redis数据库里面本来存的是字符串数据或者你要存取的数据就是字符串类型数据的时候,那么你就使用StringRedisTemplate即可。
- 但是如果你的数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象,那么使用RedisTemplate是更好的选择。
RedisTemplate使用时常见问题:
redisTemplate 中存取数据都是字节数组。当redis中存入的数据是可读形式而非字节数组时,使用redisTemplate取值的时候会无法获取导出数据,获得的值为null
。
Redis序列化和反序列化失败:
2022-12-01 12:47:52.789 ERROR 19196 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed;
nested exception is org.springframework.data.redis.serializer.SerializationException: Cannot deserialize;
nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload.
Is the byte array a result of corresponding serialization for DefaultDeserializer?;
nested exception is java.io.EOFException] with root cause
java.io.EOFException: null
at java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2681) ~[na:1.8.0_202]
…………………………
原因:
SpringBoot使用spring-data-redis,RedisTemplate默认的序列化方式是用org.springframework.data.redis.serializer.JdkSerializationRedisSerializer这个类来做序列化,而Redis有自己的序列化方式,所以冲突了 报出了上面的序列化异常的信息。
需要在controller中添加一段代码:
//指定用redis的序列化方式进行序列化
@Autowired(required = false)
public void setRedisTemplate(RedisTemplate redisTemplate) {
RedisSerializer stringSerializer = new StringRedisSerializer();//序列化为String
//不能反序列化
//Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//序列化为Json
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(serializer);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setHashValueSerializer(serializer);
this.redisTemplate = redisTemplate;
}
//注意:Jackson2JsonRedisSerializer可以序列化成功,但是反序列化会失败,
// 所以建议用GenericJackson2JsonRedisSerializer即可序列化也可反序列化。