1.redis 有哪些数据类型

  1. 字符串(String):
    Redis最基本的数据类型,一个键最大能存储512MB,可以包含任何数据。比如jpg图片或者序列化的对象,json。
  2. 列表(List):
    简单的字符串列表,按照插入顺序排序
  3. 哈希表(Hash):
    是一个string类型的field和value的映射表
  4. 集合(Set):
    Redis的Set是string类型的无序集合。底层通过哈希表实现的
  5. 有序集(zset):
    和set一样,不同的是每个元素都会关联一个double类型的分数(score)。redis正是通过分数来为集合中的成员进行从小到大的排序。

2.Redis有哪几种持久化方式及对比

  • RDB(快照)
    Redis 使用操作系统的多进程 COW(Copy On Write) 机制来实现快照持久化。

    1. Redis 在持久化时会调用 glibc 的函数 fork 产生一个子进程,快照持久化完全交给子进来处理,父进程继续处理客户端请求。子进程刚刚产生时,它和父进程共享内存里面的代段和数据段。这时你可以将父子进程想像成一个连体婴儿,共享身体(内存)。
    2. 如果进程要对内存数据修改,这个时候就会使用操作系统的 COW 机制来进行数据段页面的分离。数据段是由很多操作系统的页面组合而成,当父进程对其中一个页面的数据进行修改时,会将被共享的页面复制一份分离出来,然后对这个复制的页面进行修改。这时子进程相应的页面是没有变化的,还是进程产生时那一瞬间的数据。
  • AOF
    AOF 日志存储的是 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改的指令记录。内容是redis通讯协议(RESP )格式的命令文本存储.
    当文件大小触碰到临界时,rewrite会被运行, 对 AOF 日志进行瘦身。其原理就是开辟一个子进程对内存进行遍历转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件中

优缺点是什么?

  1. AOF文件比RDB更新频率高,优先使用AOF还原数据。
    RDB 是间隔一段时间进行持久化,数据有点延迟

  2. AOF比RDB更安全也更大
    RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失

  3. RDB性能比AOF好
    性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能。RDB是内存快照,恢复速度肯定比指令速度快

3.缓存穿透,缓存雪崩

  • 缓存穿透:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
    解决方案:布隆过滤器,缓存空对象

  • 缓存击穿: 是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。。
    解决方案:限流降级,对某个key只允许一个线程查询数据和写缓存,其他线程等待

  • 缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
    解决方案:数据预热,就是在部署启动之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。为key设置不同的缓存失效时间。使redis高可用,不会造成所有key同时失效的情况。

4.怎么实现redis分布式锁

先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。setnx相当于分布式的CAS,我们可以发展成重入锁,读写锁的。

5.Redis的过期键的删除策略

  1. 定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。

  2. 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。

  3. 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

6.redis数据淘汰策略

redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略

  1. volatile-lru:从设置了过期时间的数据集中,选择最近最久未使用的数据释放;
  2. allkeys-lru:从数据集中(包括设置过期时间以及未设置过期时间的数据集中),选择最近最久未使用的数据释放;
  3. volatile-random:从设置了过期时间的数据集中,随机选择一个数据进行释放;
  4. allkeys-random:从数据集中(包括了设置过期时间以及未设置过期时间)随机选择一个数据进行入释放;
  5. volatile-ttl:从设置了过期时间的数据集中,选择马上就要过期的数据进行释放操作;
  6. noeviction:不删除任意数据(但redis还会根据引用计数器进行释放),这时如果内存不够时,会直接返回错误。

7.redis高可用

redis缓存面试

  1. 采用Master/Slave主备的方式,使用AOF方式
  2. 增加Sentinel(哨兵),负责持续监控主从节点的健康,当主节点挂掉时,自动选择一个最优的从节点切换为主节点
  1. 客户端来连接集群时,会首先连接 sentinel,通过 sentinel 来查询主节点的地址
  2. 然后再去连接主节点进行数据交互。
  3. 当主节点发生故障时,客户端会重新向 sentinel 要地址,sentinel 会将最新的主节点地址告诉客户端。

如此应用程序将无需重启即可自动完成节点切换。Sentinel 会持续监控已经挂掉了主节点,待它恢复后,原先挂掉的主节点现在变成了从节点,从新的主节点那里建立复制关系。

8.hash一致性算法

先构造一个0到2^32的整数环,然后将服务器节点的Hash值,放在该环上(可以理解为将你的ip做hash,将ip的HashCode放在环上)。然后根据需要缓存的数据的Key,计算Key的HashCode,然后在环上,顺时针查找距离这个Key的Hash值最近的缓存服务器的节点,然后将Value,存储到该服务器节点上。
redis缓存面试
如果新增节点node5,计算的hash值趋于node2,node4之间,则 node2,node4之间的数据就有一部分需要迁移到node5,一方面对其他节点影响小,但是会导致数据会分配不均,其他节点数据压力依然很大。

解决方式:

  1. 引入虚拟节点,在现有节点上层在增加一层虚拟节点,

  2. 我们将每台缓存服务器,虚拟为一组虚拟缓存服务器,将虚拟服务器的Hash值,放置在Hash环上,Key在环上先找到虚拟服务器节点,再得到物理服务器的信息。

这样,一台服务器节点,被环中多个虚拟节点所代表,且多个节点均匀分配。很显然,每个物理节点对应的虚拟节点越多,各个物理节点之间的负载越均衡,新加入物理服务器对原有的物理服务器的影响越保持一致。

9.codis集群

Codis 将所有的 key 默认划分为 1024 个槽位(slot),它首先对客户端传过来的 key 进行 crc32 运算计算哈希值,再将 hash 后的整数值对 1024 这个整数进行取模得到一个余数,这个余数就是对应 key 的槽位。每个槽位都会唯一映射到后面的多个 Redis 实例之一,Codis 会在内存维护槽位和Redis 实例的映射关系.如果是Codis集群,则关系维护在ZK中
redis缓存面试

如何扩容

  1. 通过ZK将一半的槽位划分到新的节点。
  2. Codis 通过 SLOTSSCAN 扫描出待迁移槽位的所有的 key,然后挨个迁移每个 key 到新的 Redis 节点。
  3. 当 Codis 接收到位于正在迁移槽位中的 key的请求后,会立即强制对当前的单个 key 进行迁移,迁移完成后,再将请求转发到新的 Redis 实例。

缺点

因为 Codis 中所有的 key分散在不同的 Redis 实例中,所以事务就不能再支持了

相关文章: