Redis基础笔记
Redis概述
Redis:是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。为了保证效率,数据都是缓存在内存中。redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步,是一个非关系型数据库(Nosql)
关系数据库与非关系数据库的区别
关系型数据库:列+行,同一个表下数据的结构是一样的。
- 结构化组织
- SQL
- 数据和关系都存在单独的表中 row col
- 操作,数据定义语言
- 严格的一致性
- 基础的事务
非关系型数据库:数据存储没有固定的格式,并且可以进行横向扩展,甚至可以是图片、文档等数据。
- 不仅仅是数据 (Not Only Structured Query Language)
- 没有固定的查询语言
- 键值对存储,列存储,文档存储,图形数据库(社交关系)
- 最终一致性
- CAP定理和BASE
- 高性能,高可用,高扩展
核心:Redis是将所有的数据放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!),对于内存系统来说,如果没有上下文切换效率就是最高的,多次读写都是在一个CPU上的,在内存存储数据情况下,单线程就是最佳的方案。
数据类型
在redis中无论什么数据类型,在数据库中都是以key-value形式保存,通过进行对Redis-key的操作,来完成对数据库中数据的操作。
String(字符串)类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-46NyMbpD-1679993303268)(C:/Users/呆预言家/AppData/Roaming/Typora/typora-user-images/image-20220906224234033.png)]
适用场景:
- 计数器
- 统计多单位数量
- 粉丝数
- 对象缓存存储
List(列表)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J6jnO2m2-1679993303269)(C:/Users/呆预言家/AppData/Roaming/Typora/typora-user-images/image-20220906224353810.png)]
- 如果key不存在,则创建新的链表
- 如果key存在,新增内容
- 如果移除了所有值,空链表,也代表不存在
- 在两边插入或者改动值,效率最高!修改中间元素,效率相对较低
Set集合
set集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1),集合内不会出现重复元素。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D7kCdymx-1679993303269)(C:/Users/呆预言家/AppData/Roaming/Typora/typora-user-images/image-20220906224606519.png)]
Hash(哈希)
key-map类型,适合储存对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HgKFakwD-1679993303270)(C:/Users/呆预言家/AppData/Roaming/Typora/typora-user-images/image-20220906224904778.png)]
Zset(有序集合)
每个元素都会关联一个score(分数),系统通过元素的分数给每个元素从小到大排顺序,元素的分数可以相同,相同的元素按照字典序排序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wtiNLSzg-1679993303270)(C:/Users/呆预言家/AppData/Roaming/Typora/typora-user-images/image-20220906225328916.png)]
Geospatial(地理位置)
使用经纬度定位地理坐标并用一个有序集合zset保存,所以zset命令也可以使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vEYYQRtv-1679993303270)(C:/Users/呆预言家/AppData/Roaming/Typora/typora-user-images/image-20220906225519621.png)]
Hyperloglog(基数统计)
用来做基数统计的算法,HyperLogLog 的优点是在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A97T7J4a-1679993303271)(C:/Users/呆预言家/AppData/Roaming/Typora/typora-user-images/image-20220906225942769.png)]
BitMaps(位图)
使用位储存,储存信息只有0和1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a4YKI4nH-1679993303271)(C:/Users/呆预言家/AppData/Roaming/Typora/typora-user-images/image-20220906230113461.png)]
数据类型总结
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LIJiTwIQ-1679993303272)(C:/Users/呆预言家/AppData/Roaming/Typora/typora-user-images/image-20220906223550404.png)]
Redis事务
事务执行分析
Redis的单条命令是保持原子性的,但Redis的事务是一组命令的集合是不能保持原子性的。
事务中的每条命令都被序列化,执行过程中是按照顺序执行的,执行过程中不受其他命令的干扰。
事务执行过程
- 开启事务(multi)
- 命令入列
- 执行事务(exec)
所以事务中的命令在加入时都没有被执行,直到提交时才会开始执行(exec)一次性完成
事务执行出错
代码语法错误(编译时异常)所有的命令都不执行,出现语法问题时,无法正常执行(exec)命令
代码逻辑错误 (运行时异常) 其他命令可以正常执行 ,成功执行后,某一条命令执行失败,不影响其他命令的执行,所以Redis事务不存在原子性
事务监控
**悲观锁:**很悲观,认为什么时候都会出现问题,无论做什么都会加锁。
乐观锁:
很乐观,认为什么时候都不会出现问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据,获取version,更新的时候比较version,对比是否出现问题。
Redis存在watch关键字来监测数据,相当于乐观锁。
每次事务成功执行之后都会自动释放锁,无论结果成功与否。
SpringBoot整合Redis
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在springboot2.x之后,默认使用的jedis被替换为lettuce,原Jedis中大量方法已经失效,因为Jedis操作多线程的过程是不安全的,而lettuce在操作多线程时不存在线程安全问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ISrcrOIx-1679993303273)(C:/Users/呆预言家/AppData/Roaming/Typora/typora-user-images/image-20220907094933928.png)]
在配置文件application.yml中加入Redis连接的相关配置
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=192.168.0.24
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=10
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=1000
注入自定义的RedisTemplate组件(bean)配置
@Configuration
public class CommonConfig {
//Redis链接工厂
@Autowired
private RedisConnectionFactory redisConnectionFactory;
//缓存操作组件RedisTemplate的自定义配置
@Bean
public RedisTemplate<String, Object> redisTemplate(){
//定义RedisTemplate实例
RedisTemplate<String, Object> redisTemplate=new RedisTemplate
<String, Object>();
//设置Redis的链接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
//TODO:指定大Key序列化策略为String序列化,Value为JDK自带的序列化策略
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
//TODO:指定hashKey序列化策略为String序列化-针对hash散列存储
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
//缓存操作组件StringRedisTemplate
@Bean
public StringRedisTemplate stringRedisTemplate(){
//采用默认配置即可-后续有自定义配置时则在此处添加即可
StringRedisTemplate stringRedisTemplate=new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
return stringRedisTemplate;
}
}
主要时为了保证Key-Value键值对的序列化,防止中文乱码的情况
Redis持久化
redis是内存数据库,如果不将内存中的数据保存到磁盘,那么一旦服务器进程退出,服务器中的数据状态也会消失,所以redis提供了持久化的功能
RDB持久化
redis会单独创建一个与当前线程一模一样的子进程来进行持久化,这个子线程的所有数据(变量、环境变量、程序计数器等)都和原进程一模一样,会先将数据写入到一个临时文件中,待持久化结束了,再用这个临时文件替换上次持久化好的文件,整个过程中,主进程不进行任何的io操作,这样就保证了高效性。
Redis默认使用的是RDB方式,但RDB方式在回复大规模数据时会出现完整性缺失,虽然很高效但最后一次持久化的数据可能会丢失。
RDB持久化的保存文件为dump.rdb
触发条件:
- 满足save规则(redis配置文件中设置的保存条件,指定时间内达到固定条数据就会保存这些数据)
- 执行flushdb命令(清空当前数据库命令)
- 退出redis
以上条件redis会生成dump.rdb文件
恢复数据时只需要将rdb文件放在redis启动目录就可以,redis启动时就会自动检测dum.rdb文件,恢复其中数据
aof持久化
将我们所有的命令都记录下来,恢复的时候将这个文件全部执行一遍,以日志的形式来记录每一个写操作,将Redis执行过的所有写指令记录下来,只许追加文件不许改写文件,redis重启之后会读取该文件重新构建数据,即redis重启就会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
aof方式是一直追加文件的在大规模数据时可能会出现文件不足以保存这么多数据,Redis会创建一个新的文件进行重写,保证更多的数据能够加进去。
aof保存文件名appendonly.aof(配置文件中未开启,使用时需要手动开启)
redis重启后,如果aof文件错位或出现异常,可以使用官方设定命令redis-check-aof --fix appendonly.aof尝试修复。
Redis发布与订阅
redis发布与订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。微信,微博等作为第三方频道,提供平台供发送者发布信息和订阅者接收信息,各种关注系统都是同理。
Redis主从复制
主从复制:是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master/Leader),后者称为从节点(Slave/Follower), 数据的复制是单向的,只能由主节点复制到从节点(主节点以写为主,从节点以读为主)
作用
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余的方式。
- 故障恢复:当主节点故障时,从节点可以暂时替代主节点提供服务,是一种服务冗余的方式
- 负载均衡:在主从复制的基础上,配合读写分离,由主节点进行写操作,从节点进行读操作,分担服务器的负载;尤其是在多读少写的场景下,通过多个从节点分担负载,提高并发量。
- 高可用基石:主从复制还是哨兵和集群能够实施的基础。
环境准备
1.开启多个Redis服务
2.配置每个服务的信息
info replication查看当前服务的信息
- 端口号
- pid文件名
- 日志文件名
- rdb文件名
3.配置从机(每个服务默认都是主机
slave host port将当前服务设置为指定端口的从机
使用规则
-
从机只能读,不能写,主机可读可写但是多用于写。
-
当主机断电宕机后,默认情况下从机的角色不会发生变化 ,集群中只是失去了写操作,当主机恢复以后,又会连接上从机恢复原状。
-
当从机断电宕机后,若不是使用配置文件配置的从机,再次启动后作为主机是无法获取之前主机的数据的,若此时重新配置称为从机,又可以获取到主机的所有数据。这里就要提到一个同步原理。
-
第二条中提到,默认情况下,主机故障后,不会出现新的主机,有两种方式可以产生新的主机:
- 从机手动执行命令slaveof no one,这样执行以后从机会独立出来成为一个主机
- 使用哨兵模式(自动选举)
哨兵模式
启动命令redis-sentinel xxx/sentinel.conf
哨兵模式最主要的作用就是,在主从复制主机宕机时会从所有从机中选举出新的主机,继续完成原来主机的工作,如果原主机恢复工作,则作为新主机的从机继续工作。
Redis缓存穿透或雪崩
缓存穿透
在默认情况下,用户请求数据时,会先在缓存(Redis)中查找,若没找到即缓存未命中,再在数据库中进行查找,数量少可能问题不大,可是一旦大量的请求数据(例如秒杀场景)缓存都没有命中的话,就会全部转移到数据库上,造成数据库极大的压力,就有可能导致数据库崩溃。网络安全中也有人恶意使用这种手段进行攻击被称为洪水攻击。
缓存击穿
一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到数据库,造成瞬时数据库请求量大、压力骤增。这就是缓存被击穿,只是针对其中某个key的缓存不可用而导致击穿,但是其他的key依然可以使用缓存响应,经典案例微博热搜,服务器爆炸。
缓存雪崩
大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时数据库请求量大、压力骤增,引起雪崩。