redis
redis 数据类型以及应用场景
- string:验证码
- list:订单列表
- hash:购物车
- set:订单列表
- Sorted Set:排行榜
redis 持久化方式
- RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
- AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
RDB持久化配置
Redis会将数据集的快照dump到dump.rdb文件中。此外,我们也可以通过配置文件来修改Redis服务器dump快照的频率,在打开6379.conf文件之后,我们搜索save,可以看到下面的配置信息:
save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。
AOF持久化配置
在Redis的配置文件中存在三种同步方式,它们分别是:
appendfsync always #每次有数据修改发生时都会写入AOF文件。
appendfsync everysec #每秒钟同步一次,该策略为AOF的缺省策略。
appendfsync no #从不同步。高效但是数据不会被持久化。
参考资料:点击这里
redis 内存淘汰策略
过期策略
我们set key的时候,都可以给一个expire time,就是过期时间,指定这个key比如说只能存活1个小时,我们自己可以指定缓存到期就失效。如果假设你设置一个一批key只能存活1个小时,那么接下来1小时后,redis是怎么对这批key进行删除的?
答案是:定期删除+惰性删除
- 所谓定期删除,指的是redis默认是每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意,这里可不是每隔100ms就遍历所有的设置过期时间的key,那样就是一场性能上的灾难。
实际上redis是每隔100ms随机抽取一些key来检查和删除的。
- 但是,定期删除可能会导致很多过期key到了时间并没有被删除掉,所以就得靠惰性删除了。这就是说,在你获取某个key的时候,redis会检查一下
,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。并不是key到时间就被删除掉,而是你查询这个key的时候,redis再懒惰的检查一下
通过上述两种手段结合起来,保证过期的key一定会被干掉。但是实际上这还是有问题的,如果定期删除漏掉了很多过期key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期key堆积在内存里,导致redis内存块耗尽了,怎么办?
答案是:走内存淘汰机制。
内存淘汰机制:
如果redis的内存占用过多的时候,此时会进行内存淘汰,有如下一些策略:
- noeviction:当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧
- allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)
- allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key,这个一般没人用吧
- volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key(这个一般不太合适)
- volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key
- volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除
redis 缓存穿透、击穿、雪崩问题
- 缓存穿透:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
- 缓存击穿:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
- 缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
解决方式:使用setnx,配合双重锁的机制代码如下:
package com.redis.lock;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* setNx 悲观锁 解决雪崩 击穿 穿透问题
* 事物+监听 解决秒杀 库存卖超问题。
*/
@Component
public class RedisLockTest extends Thread {
@Override
public void run() {
try {
setNx();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Autowired
StringRedisTemplate stringRedisTemplate;
public String setNx() throws InterruptedException {
System.out.println(Thread.currentThread().getName()+"开始获取缓存数据");
String key = stringRedisTemplate.opsForValue().get("order");
if (StringUtils.isNotBlank(key)){
System.out.println(Thread.currentThread().getName()+"开始获取缓存数据成功,返回");
return key;
}
System.out.println(Thread.currentThread().getName()+"获取缓存数据失败");
Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("lock", "1");
if (aBoolean){
System.out.println(Thread.currentThread().getName()+"拿到锁成功,重新设置缓存");
stringRedisTemplate.expire("lock",1, TimeUnit.MINUTES);
//处理业务逻辑
key = stringRedisTemplate.opsForValue().get("order");
if (StringUtils.isNotBlank(key)){
System.out.println(Thread.currentThread().getName()+"---------返回缓存数据");
stringRedisTemplate.delete("lock");
return stringRedisTemplate.opsForValue().get("order");
}
//模拟查询数据库
Thread.sleep(2000);
stringRedisTemplate.opsForValue().set("order","订单号为123456");
stringRedisTemplate.delete("lock");
System.out.println(Thread.currentThread().getName()+"。。。。。。返回缓存数据");
return stringRedisTemplate.opsForValue().get("order");
}else {
Thread.sleep(500);
setNx();
}
return null;
}
}
redis 集群模式,以及cluster集群
参考资料:点击这里
redis 应用场景
- 登录锁定
- 验证码生成
- springSecurity token
- 分布式事务
- 限流令牌桶
Redis哨兵怎么实现的-主节点挂掉后从节点怎么实现转为主节点的
参考资料 : 点击这里
redis 集群的三种模式优缺点
参考资料:点击这里
求一键三连,谢谢!