Radis加强及一些面试题

Redis

什么是Redis?

Redis,英文全称是Remote Dictionary Server(远程字典服务),是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

作用

与MySQL数据库不同的是,Redis的数据是存在内存中的。它的读写速度非常快,每秒可以处理超过10万次读写操作。因此redis被广泛应用于缓存,另外,Redis也经常用来做分布式锁。除此之外,Redis支持事务、持久化、LUA 脚本、LRU 驱动事件、多种集群方案。

Redis的基本数据结构类型

五种基本类型:

String(字符串)

String是Redis最基础的数据结构类型,它是二进制安全的,可以存储图片或者序列化的对象,值最大存储为512M
数据缓存、共享session、分布式锁,计数器、限流

Hash(哈希)

Hash在Redis中,哈希类型是指v(值)本身又是一个键值对(k-v)结构
内部编码:ziplist(压缩列表) 、hashtable(哈希表)
应用场景:缓存用户信息等。

List(列表)

List列表类型是用来存储多个有序的字符串,一个列表最多可以存储2^32-1个元素。
内部编码:ziplist(压缩列表)、linkedlist(链表)
应用场景:消息队列,文章列表。

Set(集合)

Set集合类型也是用来保存多个的字符串元素,但是不允许重复元素。
内部编码:intset(整数集合)、hashtable(哈希表)
应用场景:用户标签、生成随机数抽奖、社交需求(共同好友等)。

Zset(有序集合)

Zset是有序的字符串集合,同时元素不能重复。
内部编码:ziplist(压缩列表)、skiplist(跳跃表)
应用场景:排行榜,社交需求(如用户点赞)。

Redis6三种新的数据结构类型:

  • Geospatial (地理信息)
  • Hyperloglog (基数统计)
  • Bitmaps (二进制位图)

Redis的过期策略

定时过期:

expire key 60:指定60s后过期

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

Redis采用的是两种折中方案:定期过期惰性过期两种过期策略

定期过期

  • 每隔一定的时间(默认100ms),会随机抽取一些expires字典中的key,并清除其中已过期的key。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

惰性过期

  • 定期过期会导致一部分数据在过期后不能被立即删除,这种情况依靠惰性过期来删除数据
  • 访问一个key时,先判断该key是否已过期,过期则清除。
  • 该策略可以最大化地节省CPU资源

Redis过期策略

(定期过期和惰性过期)的问题:
大量已经过期的键没有被扫描到,仍然保存在内存中
这些键如果长时间不被访问,就不会触发惰性过期,长时间占用内存
为了解决问题,Redis提供了内存淘汰机制

Radis缓存淘汰机制

Redis服务器繁忙时,有大量信息要保存

如果Redis服务器内存全满,再要往Redis中保存新的数据,就需要淘汰老数据,才能保存新数据

Radis缓存淘汰机制:

  • noeviction:返回错误**(默认)**
  • allkeys-random:所有数据中随机删除数据
  • volatile-random:有过期时间的数据库中随机删除数据
  • volatile-ttl:删除剩余有效时间最少的数据
  • allkeys-lru:所有数据中删除上次使用时间最久的数据
  • volatile-lru:有过期时间的数据中删除上次使用时间最久的数据
  • allkeys-lfu:所有数据中删除使用频率最少的
  • volatile-lfu:有过期时间的数据中删除使用频率最少的
  1. LRU(Least Recently Used)最近最少使用算法
    随机挑选被访问的数据,添加到5个长度(redis默认)的数组中
  2. LFU(Least Frequently Used)最不经常使用算法

Redis的LRU, LFU和 TTL算法都不是精确的淘汰算法,而是近似算法,为了节省内存。所以redis默认会随机选择5个key,然后从中选择使用最少用的key来移除。 设置5个是比较合适的,10个接近真LRU但是非常消耗CPU,3个很快但不是非常精确

Redis持久化

Redis提供了RDB和AOF两种持久化机制

RDB(Redis Database),

就是把内存数据以快照的形式保存到磁盘上
优点:适合大规模的数据恢复场景,如备份,全量复制等,恢复速度快
缺点:没办法做到实时持久化/秒级持久化,可能丢失一部分新数据。新老版本存在RDB格式兼容问题

AOF(append only file) 持久化

采用日志的形式来记录每个写操作,追加到文件中,重启时再重放AOF文件中的命令来恢复数据
优点:数据的一致性和完整性更高
缺点:AOF记录的内容越多,文件越大,数据恢复越慢

Redis官方建议,采用RDB+AOF混合模式

配置:
aof-use-rdb-preamble yes # redis5.0后默认开启
需要先开启AOF模式: appendonly yes
优点:
RDB保存全量数据
AOF时时持久化避免丢失数据,以增量的方式记录数据操作指令

使用RDB快速批量恢复数据,使用AOF来恢复增量数据

redis的缓存击穿/穿透/雪崩

缓存穿透

数据在Redis缓存和数据库中都不存在。大量访问这种数据时,数据库频繁查找数据,造成压力过大产生故障

  • 非正常数据访问
  • 黑客攻击
    在这里插入图片描述

解决方案

  • 对不存在的数据缓存空值
  • 设置白名单
  • 可访问的数据id作为偏移值存入bitmaps
  • 访问时先检查bitmaps
  • 使用布隆过滤器
  • 对黑客攻击进行实时监控

缓存击穿

突发热点访问时,热点数据在Redis缓存中不存在或已过期。大量的对热点数据的访问,都直接访问数据库,造成数据库访问压力短时激增造成故障

在这里插入图片描述

解决方案

  • 提前预设热门数据缓存
  • 实时调整过期时间
  • 使用锁缓存数据不存在时,把数据库数据放入缓存

缓存雪崩

大量key集中过期,数据库短时访问量激增

在这里插入图片描述

解决方案

  • 多级缓存架构(Nginx-本地缓存(ehcache/guava)-Redis)
  • 锁或队列对并发访问进行序列化
  • Key设置过期标志,对即将过期数据提前进行更新,自动续期
  • 数据的过期时间用随机值,分散过期时间

redis热key

  • 访问频率高的key,称为热点key
  • 某些热点key的请求量特别大,可能会导致Redis服务器资源不足,甚至宕机,从而影响正常的服务。

产生热Key问题的场景:

  • 用户消费的数据远大于生产的数据,如秒杀、热点新闻等读多写少的场景。
  • 请求分片集中,超过单Redis服务器的性能,比如固定名称key,Hash落入同一台服务器,瞬间访问量极大,超过机器瓶颈,产生热点Key问题。

解决热key问题:

  • Redis集群扩容:增加分片、增加副本(从机),均衡读流量
  • 将热key分散到不同的服务器中
  • 使用多级缓存,使用本地缓存,减少Redis的读请求

如何实现Redis的高可用

  • 主从模式
  • 哨兵模式
  • 集群模式

主从模式

  • 主从模式中,主机负责读写操作,从机只负责读操作
  • 数据添加到主机后,会自动复制到从机
  • 全量复制当从机连接到主机时,执行全量复制。主机将RDB文件发送到从机进行数据导入
  • 增量复制全量复制后,主机数据再发生变化时就会触发增量复制。主机每一次增删改操作都会将指令发送到从机,从机执行相同的操作

哨兵模式

由来:

主从模式中,一旦主节点由于故障不能提供服务,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址。显然,多数业务场景都不能接受这种故障处理方式。Redis从2.8开始正式提供了Redis Sentinel(哨兵)架构来解决这个问题。

  • 一般由多个Sentinel实例组成的Sentinel系统
  • 它可以监视所有的Redis主节点和从节点
  • 主节点下线时,由Sentinel对主节点下线状态进行确认。
  • 自动将某个从节点升级为新的主节点

工作机制

  • 每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他Sentinel实例发送一个 PING命令。
  • 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel标记为主观下线。
  • 如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。
  • 当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线。
  • 在一般情况下, 每个 Sentinel 会以每10秒一次的频率向它已知的所有Master,Slave发送 INFO 命令。
  • 当Master被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次
  • 若没有足够数量的 Sentinel同意Master已经下线, Master的客观下线状态就会被移除;若Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。

Cluster集群模式

-负载均衡,故障切换,主从复制
Redis Cluster要求至少需要3个master才能组成一个集群,同时每个master至少需要有一个slave节点。各个节点之间保持TCP通信。当master发生了宕机, Redis Cluster自动会将对应的slave节点提拔为master,来重新对外提供服务。

Cluster集群模式 – 哈希槽

Redis集群采用哈希槽算法,哈希槽槽位从0到16383,共16384个槽位
每个Redis主机覆盖一段哈希槽
添加key时,首先用哈希槽算法计算该key存放的哈希槽,然后存放到对应的主机

在这里插入图片描述

  • 一致性哈希算法
  • 哈希槽算法

Redis实现分布式锁?

分布式锁

  • 我们的系统都是分布式部署的,日常开发中,秒杀下单、抢购商品等等业务场景,为了防库存超卖,都需要用到分布式锁。
  • 分布式锁其实就是,控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。

常见的分布式锁实现,一般有这3种方式:

  • 基于数据库实现的分布式锁
  • 基于Redis实现的分布式锁
  • 基于Zookeeper实现的分布式锁

使用Redis实现分布式锁的原理

  • 用Redis实现分布式锁,就像在图书馆占座
  • 加锁:在Redis中放一个数据占位
  • 解锁:从Redis删除占位数据

在这里插入图片描述

Redis实现分布式锁的七种方案

方案1-setnx+expire
方案2-SET的扩展命令(SET key value NX EX|PX time)
方案3-SETNX + value(值是系统时间+过期时间)
方案4-SET EX PX NX + 校验唯一随机值,再释放锁
方案5-使用Lua脚本(包含值的比对和删除)
方案6-多机实现的分布式锁Redlock
方案7-开源框架~Redisson

setnx+expire
加锁:
  • setnx key value
  • 添加数据成功即加锁成功(占位)
  • 添加数据失败即加锁失败
解锁:
  • del key
  • 删除占位数据即解锁
死锁:
  • 长时间不解锁,或忘记解锁就会发生死锁
死锁的解决方法:
  • 设置锁的过期时间expire key seconds
SET的扩展命令(SET key value NX EX|PX time)

方案1中为了防止死锁,加锁后要设置过期时间:

  1. 加锁
  2. 设置锁的过期时间

可以将两步操作合并为一步,例如:
set mylock 1 nx ex 10 如果锁数据不存在则添加锁,并设置过期时间为10秒
参数含义:nx不存在才能添加成功,ex秒,px毫秒

SETNX + value(值是系统时间+过期时间)
  • 在方案1和2中对锁设置了过期时间
  • 也可以不在Redis中设置过期,而是在客户端加锁失败时检查过期,其过程如下:
  1. 加锁失败
  2. 获取该锁的过期时间
  3. 如果判断该锁已过期,则先删除锁,再重新加锁
这种方案还可以防止误删除其他会话的锁

在什么情况下会出现误删:

  1. 加锁成功
  2. 执行数据操作
  3. 发生阻塞…
  4. 锁超时,自动删除
  5. 阻塞结束后执行解锁,删除锁

在删除之前可以先检查值是不是自己设置的值,如果不是,则不能执行删除

SET EX PX NX + 校验唯一随机值,再释放锁

与方案3相同,可以避免删除其他会话的锁
锁的值使用随机字符串,例如uuid
设置自动过期

使用Lua脚本(包含值的比对和删除)
  • 在方案3和4中,删除锁时,需要先比对值一致才能删除,这是两步操作:
    获取值,进行比对
  • 删除

这两步数据操作,可能会出现在两步执行的中间,锁自动过期,进而删除其他会话的锁的情况:

多机实现的分布式锁(Redlock)

Redis一般会做主从复制来提高可用性
当主机中添加了锁,但是还没同步到从机。恰好这时,主机发生故障,一个从机就会升级为主机。但是,新的主机上不存在锁,那么其他会话就可以添加锁。

在这里插入图片描述

为了解决这个问题,Redis作者antirez提出一种高级的分布式锁算法:Redlock。Redlock核心思想是这样的:
用多个Redis master部署,以保证它们不会同时宕机。并且这些master节点是完全相互独立的,相互之间不存在数据同步。同时,需要确保在这多个master实例上,是与在Redis单实例,使用相同方法来获取和释放锁。

假设当前有5个Redis master节点

在这里插入图片描述

  • 按顺序向5个master节点请求加锁
  • 根据设置的超时时间来判断,是不是要跳过该master节点。
  • 如果大于等于3个节点加锁成功,并且使用的时间小于锁的有效期,即可认定加锁成功啦。
  • 如果获取锁失败,解锁!
开源框架~Redisson

Redisson - 是一个高级的分布式协调Redis客服端,能帮助用户在分布式环境中轻松实现一些Java的对象,Redisson、Jedis、Lettuce 是三个不同的操作 Redis 的客户端,Jedis、Lettuce 的 API 更侧重对 Reids 数据库的 CRUD(增删改查),而 Redisson API 侧重于分布式开发

Redisson 提供了多种分布式锁实现:
  • 可重入锁
  • 公平锁
  • 联锁
  • 红锁
  • 读写锁

Redis与数据库数据一致策略

延迟双删策略

  1. 删除缓存
  2. 修改数据库数据
  3. 延迟3到5秒
    ,并且使用的时间小于锁的有效期,即可认定加锁成功啦。
  • 如果获取锁失败,解锁!
开源框架~Redisson

Redisson - 是一个高级的分布式协调Redis客服端,能帮助用户在分布式环境中轻松实现一些Java的对象,Redisson、Jedis、Lettuce 是三个不同的操作 Redis 的客户端,Jedis、Lettuce 的 API 更侧重对 Reids 数据库的 CRUD(增删改查),而 Redisson API 侧重于分布式开发

Redisson 提供了多种分布式锁实现:
  • 可重入锁
  • 公平锁
  • 联锁
  • 红锁
  • 读写锁

Redis与数据库数据一致策略

延迟双删策略

  1. 删除缓存
  2. 修改数据库数据
  3. 延迟3到5秒
  4. 再次删除缓存
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值