Redis面试题

缓存穿透
当接口查询一个不存在的数据时 从redis查询不到 再从数据库中查询 给数据库带来巨大压力 这就是缓存穿透

解决方法

  1. 将查询到的空值加入redis中,但仍旧会给redis带来较大压力

  2. 添加一个布隆过滤器 将查询到的空值加入过滤器中 过滤器初始维护一个值为01的数组 查询到的空值经过三次hash 得到三个值 将数组中对应下标的数记为1 这样有可能造成误判 但可以增大数组的长度来提升准确度

缓存击穿
当一个key恰好过期被删除时 大量请求访问redis查找该key 由于key被删除 大量请求访问数据库 导致数据库崩溃

解决方法

1.逻辑过期 给key的过期时间加入到key的值中 当过期时间到时不自动删除 交给下一次访问此key的请求来判断 如果过期 则返回过期数据并启动新线程 获取锁并对过期的key进行更新

2.分布式锁 在redis中查找不到数据时 获取一个锁 访问数据库并更新到redis中 最后再释放锁 若获取不到锁 则重复获取key值 直到其他线程更新key值被我们获取

缓存雪崩
当大量key同时过期被删除时 有大量请求访问redis获取不到数据 再访问数据库 导致数据库崩溃

解决方法

1.对不同key的ttl设置时加上随机变量 使不同key过期的时间略有不同

2.添加多级缓存

3.添加降级限流策略

4.使用redis集群

双写一致性
数据库中数据修改后 redis也应随之修改 但修改需要时间 其他请求在修改的时间内访问会得到旧数据

解决方法

1.延迟双删

数据库更新前 先将redis的内容删除 再更新数据库 等待一段时间后 再次删除redis相应内容(当更新数据库前 可能有请求访问redis 此时redis缓存已被删除 就会去数据库中获取 此时数据库还未更新 获取到的是旧数据并写入redis中 所以需要再次删除)

2.分布式锁 (强一致性)

共享锁 读取redis数据时获取一个读锁(只允许读不允许写)保证了读取的时候其他线程无法写入

排他锁 更新数据库前先获取一个读写锁(不允许读写) 将数据更新到redis后 再释放锁

3.异步通知

当数据库更新后 给mq发一个通知 监听mq的接口收到后更新redis

使用阿里的canal canal会伪装为mysql的一个从属 监听数据库的binlog 当数据库更新后 binlog会改动 此时canal就可以获取到改动 再将其写入redis

持久化
将redis中的数据储存到磁盘中 避免redis崩溃导致数据丢失

方式

  1. RDB redis启动时会启动建立一个页表 然后启动一个子线程 子线程会复制页表 当redis改动时 会往内存区中修改数据 子线程也可以访问内存区 获取修改的数据 为了保证一致性 当子线程获取数据时 内存中会创建一个副本 供主线程修改 当子线程获取新数据后 会将数据写入磁盘 (RDB数据恢复较快 为2进制文件 可直接读取)

  2. AOF redis每一步修改时都会将命令写入aof文件 aof可以设置刷盘时间 即将文件写入磁盘 aof有三种刷盘模式 同步刷盘 每秒刷盘 以及系统控制刷盘 (AOF 数据恢复较慢 保存的是命令 文件较大 恢复时间长)

过期策略

1.惰性删除 当数据过期后 不主动删除过期数据 当下一次访问时 判断是否过期 将其删除

2.定时任务 设置定时任务 定期查找过期数据并删除 分为两种模式 slow模式是不断获取部分数据 并删除过期数据 执行多次

fast模式是获取较多数据 删除过期数据 也是执行多次 但次数较少 两种策略均可比减轻删除过期数据时对其他线程访问数据的影响

淘汰策略

1.LRU 查找最近使用的时间 删除使用最近时间较长的数据

2.LFU 查找使用的频率 删除使用频率较短的数据

3.随机删除

以上几种可以分为全部数据(allkeys)和设置了过期时间的数据(volatile)

4.删除数据 当存储空间满了之后 redis报错(noeviction)

5.删除ttl剩余时间短的数据

分布式锁
setnx

使用redis中的setnx命令 对key的值进行设置 若成功则代表成功拿到锁 不可重入

redission

使用zset结构 key值为锁名 值分为field 和value field记录了线程的id value记录了同一线程锁获取的次数 可重入 当同一线程获取锁时 redis会判断线程id是否一直 一直则获取锁 并使value+1 释放锁时value-1 当获取次数为0时才删除锁

redission锁需要我们主动获取 在获取锁后 redission会启动一个子线程 (看门狗) 定时检查是否还持有锁 当超过锁有效时间的1/3时 自动为锁续期

redission无法解决主从一致性 当获取锁后 redis主节点崩溃 子节点会自动升级为主节点 此时新的主节点还没更新数据 其他线程可以拿到相同的锁 导致了锁的多次获取 可以使用redission的红锁解决 即在多个节点同时获取锁 成功才视为获取到锁 但这样较为耗费资源和内存 可以使用zookeeper实现的锁

集群模式
主从集群

节点内有offset 、replication id两个数据 从节点向主节点发送同步数据请求 主节点先通过判断replication id来确定节点是否是第一次请求同步 若是第一次 则将主节点的id和offset发给从节点 然后执行BGSAVE 将数据同步给从节点 这段时间内主节点修改的命令会存入RDB文件中 等待BGSAVE执行完毕后 将RDB文件发给从节点 使得同步数据期间的修改也能同步给从节点 若不是第一次请求同步 则获取从节点的offset并与主节点offset比较 取出新更改的命令 通过RDB文件发送给从节点

哨兵模式

Sentinel有三个功能

1.监控 不断检查主从节点运行的状态

主观下线 当某个节点超过规定时间没有响应哨兵 即视为主观下线

客观下线 当超过预设值个哨兵认为此节点主观下线 该节点视为客观下线

2.自动故障恢复 当主节点崩溃时 会将另一个从节点提升为主节点 先判断从节点与主节点断开时间的长短 超过指定值就选择该节点

再判断从节点的slave-priotity值 越小优先级越高 再判断offset值 越大优先级越高 最后判断运行id大小越小优先级越高

3.通知 充当服务发现 当集群发生故障转移时 会将消息通知给redis服务端

脑裂 由于网络波动 导致主从节点断开连接 哨兵会将其中一个子节点提升为主节点 但此时数据的修改命令还在发送给旧的主节点 当网络恢复后 旧的主节点自动被降级为从节点 同步新的主节点的数据 此时网络中断时间内发生的数据修改会丢失 导致脑裂

可以通过设置从节点的最小数量为1即没有从节点时不能修改数据 也可以设置数据复制同步的延迟的阈值

分片集群

数据分为多个节点储存 多节点均为主节点 可以拥有各自的从节点 各主节点之间可以互相转发 通过ping获取对方的状态

分片集群的数据读写依赖哈希槽 每个key经过CRC16校验后对16384取模来得到存放的槽

你可能感兴趣的:(java,redis)