本文最后更新于355 天前,其中的信息可能已经过时,如有错误请发送邮件到3368129372@qq.com
简介
- key-value存储系统
- NoSQL数据库
- 基于内存,读写性能很高,简单的数据单机 qps 5w - 10w(解决用户信息读取 / 是否登录的判断极其频繁 的问题)
可实现共享存储,同样地可使用下面的方法来实现
- mysql
- 文件服务器ceph
指令
见csdn
用法
导入相关库
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.7.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.session/spring-session-data-redis -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>2.7.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.session/spring-session-data-redis -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>2.7.3</version>
</dependency>
版本最好与springboot版本相一致
修改application.yaml
spring:
session:
timeout: 86400(单位分钟,默认两个月)
#从redis中读写session
store-type: redis(默认为none)
redis:
port: 6379
host: localhost
database: 0
数据结构
String 字符串类型: name: "iceice"
List 列表:names: ["iceice", "iceiceice", "iceice"]
Set 集合:names: ["iceice", "iceiceice"](值不能重复)
Hash 哈希:nameAge: { "ice": 1, "iceiceice": 2 }
Zset 集合:names: { ice - 9, iceice - 12 }(适合做排行榜)
bloomfilter(布隆过滤器,主要从大量的数据中快速过滤值,比如邮件黑名单拦截)
geo(计算地理位置)
hyperloglog(pv / uv)
pub / sub(发布订阅,类似消息队列)
BitMap (1001010101010101010101010101)
Java实现
Spring Data Redis
-
spring data:通用数据访问框架,具有增删改查的接口,上面引入过配置了
//使用默认jdk序列化器,可能会乱码 @Resorse private RedisTemplate redisTemplate; //String的序列化器,不会出现乱码问题 @Resorce private StringRedisTemplate stringRedisTemplate; //opsForValue代表操作字符串,其他数据结构的使用类似 ValueOperations valueOperations = redisTemplate.opsForValue(); //设置redis,value可以为数字、字符串等等等 valueOperations.set("key","value"); //查找 Object value = valueOperations.get("key"); //删除 redisTemplate.delete("key");
默认的redisTemplate会出现乱码而stringRedisTemplate只能写字符串,所以最好自定义
@Configuration public class RedisTemplateConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setDefaultSerializer(new StringRedisSerializer()); return redisTemplate; } }
设计缓存
不同用户缓存不同
systemId:moduleId:func:options(不要和别人冲突)
mobile:user:recommed:userId(可以使用String.format()方法)
redis 内存有限,记得设置过期时间
zset
- 用法:看这篇csdn
- 数据结构:
- 跳跃表
多级索引,即每个几个数链接一下,不这样的话要一个一个访问,这样的话能够一次访问多个。时间复杂度位log(n) - 压缩列表:
- 为啥不用二叉树或者红黑树?为了范围查找更加方便,且实现简单
- 为啥不用b+树?mysql为啥不用跳表?
这个问题在于 Redis是直接操作内存的并不需要磁盘io而MySQL需要去读取io,所以mysql要使用b+树的方式减少磁盘io,B+树的原理是 叶子节点存储数据,非叶子节点存储索引,每次读取磁盘页时就会读取一整个节点,每个叶子节点还有指向前后节点的指针,为的是最大限度的降低磁盘的IO;因为数据在内存中读取耗费的时间是从磁盘的IO读取的百万分之一
而Redis是 内存中读取数据,不涉及IO,因此使用了跳表,跳表明显是更快更简单的方式。
- 跳跃表
面试题
- 缓存穿透:查不存在的数据,穿透了redis到数据库
- 缓存空数据(可能数据不一致)
- 布隆过滤器(缓存预热时添加数据)
- 缓存击穿:某个key过期了,一瞬间大量请求到数据库
- 互斥锁
- 热点key不设置过期时间
- 逻辑过期
- 缓存雪崩:大量key同时失效
- 给不同key设置过期时间
- 集群模式
- 降级限流策略
- 添加多级缓存
- 双写一致性
- 延迟双删(主从节点,所以延时),但是延时时间不好说,bad
- redisson加锁。读写锁保证强一致性
- MQ实现,更新数据库之后通知更新缓存,允许短暂的不一致
- 持久化
- RDB,把数据写入磁盘中
- AOF,记录所有的写操作
- 过期策略
- 惰性检查,用到的时候看有没有过期
- 定期检查
- 实现
- setnx
- 主从数据不一致
举例:A请求主节点M拿锁,M拿到锁,向从节点同步数据时挂了,S1变为M1,B请求M1拿锁又成功了。(Wait能够增强一致性但是不能解决)- Redission锁不能解决,但他提供的RedLock可解决,但性能太低了,一般不用。
- 为啥这么快?
- 减少上下文切换
- 基于内存
- IO多路复用
用户空间<-1->内核空间<-2->硬件- 阻塞型IO:1、2阶段都得等
- 非阻塞型IO:1通过轮询(可能空转),2还是阻塞
- IO多路复用:单线程监听多个socket,1阻塞,2是等socket主动告诉你准备好了,你再去拿数据。
- poll与select只告诉用户有socket可读可写,需要全部便利一遍
- epoll告诉用户是哪一个socket可读可写