redis常见数据类型
redis以key,value的键值对存放数据,这里的数据类型指的是value类型
String,Hash,List,Set,Zet
redis为什么是高性能
1.redis是单线程的,避免了上下文切换
2.redis存储的数据在内存中
3.redis有IO多路复用(Netty相关知识点),减少IO时间
redis的持久化
RDB快照
命令有save和bgsave
save是同步,因为是单线程,会阻塞其他命令
bgsave是异步(默认的),redis的主线程会fork一个子线程去写快照,同时满足写时复制,bgsave执行中若redis还有修改数据,则会生成一个数据副本,bgsave子进程再把副本数据写入rdb
若只开启rdb快照持久化,则宕机时只会恢复最近的一次快照,由于快照是一段时间生成一次,则未写入快照的数据会丢失
AOF日志
记录每一条修改的指令,故可以保证数据不丢失
可以配置redis多久写一次日志(先写入os cache ,根据配置决定什么时候通过fsync持久化到磁盘)
appendsync always 每次有新命令就执行一次 效率低但数据不丢失
appendsync everysec(默认) 每一秒执行一次
appendsync no 不主动执行,交给操作系统决定什么时候执行
aof会自动重写,只记录最新的数据(去除一些无用的指令),生成新的aof文件
redis4.0后可以混合持久化
也就是aof在重写时,不再是把数据转换成命令写入aof文件,而是把重写这一刻之前的内存做快照处理。并将rdb快照和增量aof修改内存数据的命令存在一起,写入新的aof文件中,写完后替换旧的aof(也是写时复制的原理),这样redis在重启恢复数据时,可以先恢复rdb快照,再执行aof的增量指令,效率更高
Redis的主从架构
主写从读
主从复制原理
主从风暴:多个从节点同时复制主节点导致主节点压力过大
管道:将多个命令一次性发给redis执行
redis哨兵高可用架构
哨兵是特殊的redis服务,不读不写只负责监听redis节点。当有主节点宕机时,哨兵负责选举新的主节点,并将新的主节点信息告知客户端,也会修改哨兵配置文件中的元数据信息
客户端第一次访问时,是链接的哨兵,哨兵再链接主节点,并把主节点信息告知客户端,客户端后续就会直接访问主节点
哨兵模式的缺点
1.主从切换时,会出现访问瞬断的情况
2.哨兵模式只有一个主节点提供对外的服务,不太能满足并发很高的场景
3.单个主节点也不宜设置过大,否则会导致持久化文件过大,影响数据恢复和主从同步效率
高可用集群模式
大公司常用的一种模式,与哨兵模式很相似,是多个主从节点组成的分布式集群,具有复制,高可用,分片的特点。支持水平扩展(但官方不建议超过1000个)
每个小主从节点存储数据是不一样的,客户端通过一个hash公式计算一个值,再对16384取模,划分几个slot槽位(根据集群的多少),确定不同的值放在哪一个区间(对应的集群)
集群节点中的通讯机制
1.集中式
优点在于元数据的更新和读取,时效性非常高。一旦元数据变化就会更新到集中式存储中,但是因此存在的不足则是所有的元数据更新集中在一个地方,从而导致压力非常大。一般会用zookeeper集中存储元数据
2.gossip
就是点对点,一个个节点通知,时效性比较低
Java操作序列化
StringRedisTemplate 默认采用String的序列化策略
redisTemplate 默认采用JDK的序列化策略
redis实战
分布式锁
如果只用redisTemplate,setNX,可能出现死锁情况(比如执行一半宕机了)
private StringRedisTemplate redisTemplate;
void lock(){
Boolean isLock = redisTemplate.opsForValue().setIfAbsent("lock", "value");
if(isLock){
try{
System.out.println("执行业务代码");
// 这里宕机,死锁了
}catch (Exception e){
}finally {
redisTemplate.delete("lock");
}
}
}
如果设置超时时间,容易出现线程1还没执行完就超时,锁过期,线程2获取锁,线程1执行完后把线程2的锁删掉的情况
private StringRedisTemplate redisTemplate;
void lock(){
Boolean isLock = redisTemplate.opsForValue().setIfAbsent("lock", "value",30, TimeUnit.SECONDS);
if(isLock){
try{
System.out.println("执行业务代码");
// 这里执行太久,超过了设定的30s
}catch (Exception e){
}finally {
redisTemplate.delete("lock");
}
}
}
这个时候就需要锁续命
redisson 跟jedis一样都是java的redis客户端
redisson使用了lua脚本,执行了redis加锁,并设置了过期时长(默认30s)
看门狗每隔10s续锁一次
scheduleExpirationRenewal(threadId);(这个方法)
redLock 半数以上的redis节点加锁成功,则成功。但是可能存在第一把锁加成功了,但是其中一个加锁的主节点挂掉,选举出了从节点当主节点,则有可能再次加锁也能达到半数以上成功
解决缓存穿透(存在查询的数据,本该有缓存值,但是缓存可能过期了,此时大量请求直接打到数据库)
双重检测锁
检测一遍缓存,锁内再检测一遍缓存,如果没有再查数据库。锁的目的是为了防止缓存尚未更新时大量请求直接打在数据库上
解决缓存击穿(不存在查询的数据,没有缓存值,大量请求访问数据库)
为不存在的缓存设置一个初始值
未完待续