Redis知识点整理

318 阅读5分钟

一.redis数据类型

  • String 一个key对应一个value,应用场景:存储图片base64和任何序列化对象,最大存储512MB
  • Hash 存储对象,省去序列化/反序列化开销
  • List  简单的字符串列表,应用场景:消息队列
  • Set 无序,自动排重
  • SortedSet 可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。应用场景:排行榜,时间戳作为score实现延时队列
  • Geo redis3.2版本发布,可以将用户给定的地理位置信息储存起来,并对这些信息进行操作。应用场景:微信摇一摇
  • HyperLogLog 基数统计,应用场景:网站点击量
  • Pub/Sub 发布/订阅,消费者下线,生产者消息会消失,推荐使用mq

redis特点:基于内存,单线程

二.redis实现分布式锁

public class RedisLock{
  private static final Integer LOCK_EXPIRE = 30;
  private static final Integer LOCK_TIMEOUT = 20;
  private static final String LOCK_PREFIX = "test"; public static String lock(String key) {    
    Jedis jedis = new Jedis();
    String lock = LOCK_PREFIX + key;
    long beginTime = System.currentTimeMillis() + LOCK_TIMEOUT * 1000;
    boolean flag = false;
    while (true) {
        String acquire = jedis.set(lock, String.valueOf(beginTime), SetParams.setParams().nx().ex(LOCK_EXPIRE));
        if ("OK".equals(acquire)) {
            System.out.println(String.format("%s获得锁", Thread.currentThread().getName()));
            flag = true;
            break;
        }
        if (System.currentTimeMillis() >= beginTime) {
            flag = false;
            break;
        }
    }    if (!flag) {
            System.out.println(String.format("%s不能获得锁", Thread.currentThread().getName()));
            return null;
    }
    return String.valueOf(beginTime);
}
private static void unlock(String key, String value) {
    Jedis jedis = new Jedis();
    try {
        String curVal = jedis.get(LOCK_PREFIX + key);
        if (Objects.nonNull(curVal) && curVal.equals(value)) {
            System.out.println(String.format("%s释放锁", Thread.currentThread().getName()));
            jedis.del(LOCK_PREFIX + key);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }}
}

三.redis删除策略

  • 被动删除 当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key。只有key被操作时(如GET),REDIS才会被动检查该key是否过期,如果过期则删除之并且返回NIL,容易造成内存占用。
  • 定时删除 给key设置过期时间时同时设置一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。对cpu不友好,当存在大量过期key时,容易造成cpu占用过高。
  • 定期删除 每隔一段时间来删除过期键,并通过限制删除操作执行的时长和频率来减少删除操作对CPU时间的影响,除此之外,还有效的减少内存的浪费,但是该策略的难点在于间隔时长,这个需要根据自身业务情况来进行设置。

redis采用惰性删除+定期删除的策略

四.redis持久化

  • RDB 全量持久化
  • AOF 增量持久化
RDB持久化是把当前线程的数据生成快照保存到磁盘,对 Redis 中的数据执行周期性的持久化。通过fork一个子进程,利用copy on write把整个db给dump下来。优点:产生的文件紧凑压缩比更高,因此恢复速度更快;缺点:RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作,开销较大,且无法做到实时持久化。适用场景:一般用于数据冷备和复制传输。

AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 redis 协议的格式来保存,新命令会被追加到文件的末尾。优点: AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,redis仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据。redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写,重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。缺点:对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。适用场景:数据热备

Redis默认持久化机制:AOF持久化开启且存在AOF文件时,优先加载AOF文件;AOF关闭或者AOF文件不存在时,加载RDB文件。

五.redis集群

主从+哨兵

Redis集群:主从同步读写分离,读操作可以从主节点和从节点获取,写操作只能写到主节点上,然后再从主节点复制到从节点

复制过程 

  1.  从节点内部保存主节点的ip和port
  2. 从节点通过定时任务维护复制相关逻辑,每当定时任务发现存在新的主节点后都会尝试与该节点进行网络连接
  3. 连接建立成功后,从节点会发送ping消息到主节点请求首次通信
  4. 权限验证,如果主节点设置了requirepass,只需要密码
  5. 当通信建立后,主节点会把所有的数据集全部发送给从节点
  6. 命令持续复制,当主节点有新的写入操作时,会把写命令发送给从节点,保证数据同步
哨兵机制:redis集群中如果主节点发生故障就会在从节点中选举一个晋升为主节点,并通知应用方更新主节点的信息

六.redis问题与解决

  1. 缓存雪崩 当每秒上万个请求过来时,发现redis的key全部过期了,请求就会全部落在了数据库上,导致数据库宕掉了,即使重启数据库也会被新的流量打死。解决方案: 不同的key设置不同的过期时间,比如把每个key的失效时间加个随机数值;二级缓存;
  2. 缓存穿透 爬虫或则恶意用户不断发起请求查询key值不存在的数据,就会绕过redis直接打到数据库,导致数据库压力过大。解决方案:接口鉴权;缓存空对象;布隆过滤器
  3. 缓存击穿 一个Key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个Key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个完好无损的桶上凿开了一个洞。解决方案:设置热点key永不过期;加上互斥锁;nginx配置ip限制