Redis八股文

189 阅读6分钟

Redis 常见数据结构以及使用场景分析

string

list

hash

存对象,用户信息(用field:value)

set

可以基于 set 轻易实现交集、并集、差集的操作。 比如:你可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis 可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。

sorted set

比如在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息。

bitmap

就很多状态需要存储,但是这些状态都是 是/否 的问题。所以一个信息用一位存储就好了。那么就可以用如8位存储8个状态

Redis 单线程模型详解

关于reactor设计模式

餐厅只用一个服务员,当客户多,点单慢的时候,服务员可以终于发现了一个新的方法,那就是:当客

人点菜的时候,服务员就可以去招呼其他客人了,等客人点好了菜,直接招呼一声“服

务员”,马上就有个服务员过去服务。

redis给缓存数据设置过期时间的两个作用

1.防止内存溢出

2.完成token的过期策略(用传统数据库来判断相当麻烦)

过期数据的删除策略

1.惰性删除

优点:对CPU友好

缺点:万一我再也不get这个数据,就永远不会删除,造成内存泄漏

2.定期删除

Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。

redis的做法:1+2

还是有缺陷。

因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。


这样就导致大量过期 key 堆积在内存里,然后就 Out of memory 了。

Redis 内存淘汰机制了解么?

相关问题:MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证 Redis 中的数据都是热点数据?

图片.png

分成两种,一种是面向设置了过期时间的数据进行淘汰。一种是面向键空间的所有数据

Redis 持久化机制### (怎么保证 Redis 挂掉之后再重启数据可以进行恢复)

1.快照

策略:不能定时无脑创建快照,在一段时间内,内存变化量必须达到一个标准才值得去创建快照。

save 900 1           #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

save 300 10          #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

save 60 10000        #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照

2.AOF持久化

AOF 持久化的实时性更好,因此已成为主流的持久化方案。默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启 :appendonly yes

Redis Bigkey

定义: 如果一个 key 对应的 value 所占用的内存比较大,那这个 key 就可以看作是 bigkey。具体多大才算大呢?有一个不是特别精确的参考标准:string 类型的 value 超过 10 kb,复合类型的 value 包含的元素超过 5000 个(对于复合类型的 value 来说,不一定包含的元素越多,占用的内存就越多)。

Bigkey的危害

1、使用 Redis 自带的 --bigkeys 参数来查找。

2、分析 RDB 文件

通过分析 RDB 文件来找出 big key。这种方案的前提是你的 Redis 采用的是 RDB 持久化。

Redis 事务

通过键入 MULTI 命令,表示MULTI后的命令集合暂不执行,放到队列里,一直碰到EXEC命令的时候,将这之间的命令作为一个事务一起执行。

配套使用的命令: DISCARD:取消一个事务,它会清空事务队列中保存的所有命令。 WATCH: 该命令用于监听指定的键,当调用 EXEC 命令执行事务时,如果一个被 WATCH 命令监视的键被修改的话,整个事务都不会执行,直接返回失败。

Redis 是不支持 roll back 的,因而不满足原子性的(而且不满足持久性)。

Redis 可以做消息队列吗

可以,但是效果不好。还是有很多欠缺的地方比如消息丢失堆积问题不好解决。

缓存穿透

缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。举个例子:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库。

解决方案

  1. (最基本)参数校验

错误的入参肯定不能在redis里面命中,会导致这些无效请求落到数据库,也会造成数据库的压力。所以要尽量在到达业务层之前或者在业务层做好参数校验,拒绝非法请求。

  1. 缓存无效key

无效的入参到达服务器后,也给打入redis,如果后续这个攻击的key没有频繁变化的话,则后续的请求可以被redis命中。

所以缓存无效key不能从根本上解决问题。因为无法解决key频繁变化的情景,会导致无效key大量堆积。所以如果非要使用它,请使用一个较短的过期时间

用Java代码展示:

public Object getObjectInclNullById(Integer id) {
    // 从缓存中获取数据
    Object cacheValue = cache.get(id);
    // 缓存为空
    if (cacheValue == null) {
        // 从数据库中获取
        Object storageValue = storage.get(key);
        // 缓存空对象
        cache.set(key, storageValue);
        // 如果存储数据为空,需要设置一个过期时间(300秒)
        if (storageValue == null) {
            // 必须设置过期时间,否则有被攻击的风险
            cache.expire(key, 60 * 5);
        }
        return storageValue;
    }
    return cacheValue;
}

3.布隆过滤器

将所有可能的请求存储到布隆过滤器中。

但布隆过滤器可能会存在误判的情况。总结来说就是: 布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。

图片.png

布隆过滤器的原理后续单独补充

缓存雪崩

缓存雪崩描述的就是这样一个简单的场景:缓存在同一时间大面积的失效,后面的请求都直接落到了数据库上,造成数据库短时间内承受大量请求。 这就好比雪崩一样,摧枯拉朽之势,数据库的压力可想而知,可能直接就被这么多请求弄宕机了。

针对 Redis 服务不可用的情况:

  1. 采用 Redis 集群,避免单机出现问题整个缓存服务都没办法使用。
  2. 限流,避免同时处理大量的请求。

针对热点缓存失效的情况:

  1. 设置不同的失效时间比如随机设置缓存的失效时间。
  2. 缓存永不失效。

Redis缓存与数据库一致性解决方案