Redis基础常见面试题


欢迎指正内容不严谨或有误的地方!欢迎关注我的个人博客网站:人生の驚異

Reference

  1. Predis使用手册
  2. Redis命令参考

1.Redis基础

1.1 Redis数据类型
  1. String:简单的 key-value 存储;
  2. Hash:唯一性数据存储;
  3. List:适用于轻量级消息队列,有序可重复;
  4. Set:无序不可重复的集合,适用于不重复的列表;
  5. ZSet:有序不可重复的集合,适用于排行榜。
1.2 Redis 和 Memcache 的区别
  • Memcache 所有的值均是简单的字符串,而 redis 支持更为丰富的数据类型;
  • Redis 使用单核,Memcache 可以使用多核,所以平均每个核上 Redis 在存储小数据时比 Memcache 性能更高(100k 以上数据是,Memcache性能更优);
  • Redis 支持持久化,Memcache不支持。(Redis 物理内存用完时,会将冷数据交换到磁盘存储,而 Memcache 超出内存比例则会抹掉前面的数据。)
  • Memcache 是全内存的数据缓冲系统,Redis 虽然支持数据的持久化,但是全内存比较才是其高性能的本质。
    作为基于内存的存储系统来说,机器物理内存的大小就是系统能够容纳的最大数据量。

分布式存储:如果需要处理的数据量超过了单台机器的物理内存大小,就需要构建分布式集群来扩展存储能力。

  • Memcache 本身不支持分布式,因此只能在客户端通过向一致性哈希这样的分布式算法来实现 Memcache 的分布式存储。
  • Redis 则可以在服务器端构建分布式存储,Redis Cluster是一个实现了分布式且允许单点故障的 Redis 高级版本,其没有中心节点,具有线性可伸缩的功能。
  • 为了保证单点故障下的数据可用性,Redis Cluster引入了 Master 和 Salve 节点。
  • 在 Redis Cluster 中,每个 Master 节点都会有对应的俩个用于冗余的 Slave 节点。这样在整个集群中,任意俩个节点的宕机都不会导致数据的不可用。
    当 Master 节点退出后,集群会自动选择一个 Slave 节点称为新的 Master 节点。Redis 支持 master-slave 复制模式。
    内存管理机制:
  • Memcache 主要的 cache 机制是 LRU(最近最少使用)算法 + 超时失效;
  • Redis 采用的是 mallc/free。
1.3 Redis配置文件详解

1. 通过配置文件配置

Redis安装目录下得 redis.config 配置文件中:

// 设置redis最大占用内存的大小
maxmemory 100mb

2. 通过命令修改

Redis 支持运行时通过命令动态修改内存大小:

//设置redis最大内存占用
127.0.0.1:6379> config set maxmemory 100mb
//获取Redis能使用的最大内存大小
127.0.0.1:6379> config get maxmemory

如果不设置最大内存大小或者设置最大内存大小为0,在64位操作系统下不限制内存大小,在32位系统下最多使用32G内存。

1.4 Redis的内存淘汰

Redis 定义了几种策略来处理内存溢出:

  1. noeviction(默认策略):内存溢出后,对于写请求不在提供服务,直接返回错误(DEL请求和部分特殊请求除外);
  2. allkeys-lru:所有key使用LRU算法进行淘汰;
  3. volatile-lru:从设置了过期时间的key中使用LRU算法进行淘汰;
  4. allkeys-random:从所有key中随机淘汰数据;
  5. volatile-random:从设置了过期时间的key中随机淘汰;
  6. volatile-ttl:在设置了过期时间key中,根据key的过期时间进行淘汰,越早过期的越优先淘汰;

当使用 volatile-lru、volatile-random、volatile-ttl这三种策略时,如果没有key可以淘汰,则和noeviction一样返回错误。

获取当前内存淘汰策略:

127.0.0.1:6379> config get maxmemory-policy

通过配置文件设置淘汰策略(修改redis.config):

maxmemory-policy allkeys-lru

通过命令修改淘汰策略:

127.0.0.1:6379> config set maxmemory-policy allkeys-lru

LRU(Least Recently Used)算法:
即最少使用,是一种缓存置换算法,在使用内存作为缓存的时候,缓存的大小一般是固定的。当缓存被占满,这个时候继续向缓存中添加数据,就需要淘汰一部分老的数据,释放内存空间用来存储新的数据。这个时候就可以使用LRU算法了。其核心思想是:如果一个数据在最近的一段时间没有被用到,那么将来被使用到的可能性也很小,所以就可以被淘汰掉。
Redis中的LUR算法(近似LRU):
近似LRU算法通过随机采样法淘汰数据,每次随机出5(默认)个key,从里面淘汰掉最近最少使用的key。样本数可以通过 maxmemory-samples 参数设置,样本数越大,淘汰的结果越接近严格的LRU算法。Redis为了实现近似LRU算法,给每个Key增加了一个额外的24bit的字段,用来存储该key最后一次被访问的时间。当取样数设置为10时接近严格的LRU,但是比较吃CPU。
Redis3.0对近似LRU的优化:
Redis3.0对近似LRU算法进行了一些优化。新的算法会维护一个候选池(大小为16),池中的数据根据访问时间进行排序,第一次随机选取的key都会放入池中,随后每次随机选取的key只有在访问时间小于池中最小的时间才会被放入池中,直到候选池被放满。放满后,如果有新的key需要放入,则将池中最后访问时间最大(最近被访问)的移除。当需要淘汰时,则直接从池中选取时间最小(最久没被访问)的key淘汰掉就行。
LFU(Least Frequently Used)算法:
Redis4.0新加的一种淘汰策略。它的核心思想是根据key的最近被访问的频率进行淘汰,很少被访问的优先被淘汰,被访问多的则被留下来。
LFU算法能很好的表示一个key被访问的热度。如果使用的LRU算法,一个Key很久没有被访问到,只是偶尔被访问一次,那么它就被认为是热点数据,不会被淘汰,而有些key将来是很有可能被访问到的则被淘汰了。如果使用的是LFU算法则不会出现这种情况,因为使用一次并不会使一个key成为热点数据。
LFU一共有俩种策略(仅Redis4.0以上可用):

  • volatile-lfu:在设置了过期时间的key中使用LFU算法淘汰key;
  • allkeys-lfu:在所有key中使用LFU算法淘汰数据。
1.5 Redis缓存雪崩、击穿、穿透

Redis 缓存雪崩

对于系统 A,假设每天高峰期每秒 5000 个请求,本来缓存在高峰期可以扛住每秒 4000 个请求,但是缓存机器意外发生了全盘宕机。缓存挂了,此时 1 秒 5000 个请求全部落数据库,数据库必然扛不住,它会报一下警,然后就挂了。此时,如果没有采用什么特别的方案来处理这个故障,DBA 很着急,重启数据库,但是数据库立马又被新的流量给打死了。这就是缓存雪崩。

解决方式:

  • 事前:redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃。
  • 事中:本地 memcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
  • 事后:redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

用户发送一个请求,系统 A 收到请求后,先查本地 ehcache 缓存,如果没查到再查 redis。如果 ehcache 和 redis 都没有,再查数据库,将数据库中的结果,写入 ehcache 和 redis 中。

限流组件,可以设置每秒的请求,有多少能通过组件,剩余的未通过的请求,怎么办?走降级!可以返回一些默认的值,或者友情提示,或者空白的值。

好处:

  • 数据库绝对不会死,限流组件确保了每秒只有多少个请求能通过。
  • 只要数据库不死,就是说,对用户来说,2/5 的请求都是可以被处理的。
  • 只要有 2/5 的请求可以被处理,就意味着你的系统没死,对用户来说,可能就是点击几次刷不出来页面,但是多点几次,就可以刷出来一次。

Redis 缓存穿透

对于系统A,假设一秒 5000 个请求,结果其中 4000 个请求是黑客发出的恶意攻击。

黑客发出的那 4000 个攻击,缓存中查不到,每次你去数据库里查,也查不到。

举个栗子。数据库 id 是从 1 开始的,结果黑客发过来的请求 id 全部都是负数。这样的话,缓存中不会有,请求每次都“视缓存于无物”,直接查询数据库。这种恶意攻击场景的缓存穿透就会直接把数据库给打死。

解决方式:

解决方式很简单,每次系统 A 从数据库中只要没查到,就写一个空值到缓存里去,比如 set -999 UNKNOWN。然后设置一个过期时间,这样的话,下次有相同的 key 来访问的时候,在缓存失效之前,都可以直接从缓存中取数据。

Redis 缓存击穿

缓存击穿,就是说某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。

解决方式:

解决方式也很简单,可以将热点数据设置为永远不过期;或者基于 redis or zookeeper 实现互斥锁,等待第一个请求构建完缓存之后,再释放锁,进而其它请求才能通过该 key 访问数据。

1.6 Redis 单线程为什么那么快
  • Redis 分为客户端和服务端,一次完整的 Redis 请求时间有多个阶段
    (客户端到服务器的网络连接->redis读写时间发生->redis服务端的数据处理[单线程]->数据返回。
  • 平时所说的 Redis 单线程模型,本质上指的是服务端的数据处理。
  • 客户端和服务器是 socket 通信方式,socket 服务端监听可同时接受多个客户端请求。
    也就是说,Redis 服务同时面对多个 redis 客户端的连接请求,但是Redis服务本身是单线程运行的。
  • Redis 用单个 CPU 绑定一块内存的数据,然后针对这块内存的数据进行多次读写的时候,都是在一个 CPU 上完成的,所以它是单线程处理这个任务的。
  • 使用单线程的方式是无法发挥多核 CPU 性能的,为了充分利用多核CPU,大多需要在一台 server 上启动多个示例(多个Redis进程)。
    为了减少切换的开销,有必要为每个示例(redis进程)指定其运行的CPU。
  • 因为Redis 是单线程的,所以不存在锁的问题,不可能出现死锁而导致的性能消耗。

Redis 核心的就是:若数据都在内存中,那么单线程去操作就是效率最高的。

  • 因为多线程的本质就是 CPU 模拟出来的多个线程的情况,多线程的代价就是上下文的切换,对于一个系统来说,它没有上下文的切换就是效率最高的。
  • CPU 不是 Redis 的性能瓶颈,机器的内存大小和网络带宽才可能是。
1.7 Redis 的主从结构

主从结构一是可以进行冗余备份,二是可以实现读写分离。

1.7.1 主从复制

又称为冗余备份/数据冗余/数据备份,可以实现容灾快速恢复。

持久化保证了即使 Redis 服务重启也会丢失数据,因为 Redis 服务重启后会将硬盘上的持久化的数据恢复到内存中,
但是 Redis 服务器的硬盘损坏可能会导致数据丢失,通过 Redis 的主从复制机制就可以避免这种单点故障。

实例:
搭建Redis集群:Redis0(主)、Redis1(从)、Redis2(从)。
这样即使一台Redis服务器宕机,其他俩台Redis服务也可以继续提供服务。主Redis中的数据和从Redis中的数据保持实时同步,
当主Redis写入数据时通过主从复制机制会复制到俩个从Redis服务上。

  1. 一个 Master 可以有多个 Slave,不仅主服务器可以有从服务器,从服务器也可以有自己的从服务器;
  2. 复制在 Master 端是非阻塞模式的,这意味即便是多个 Slave 执行首次同步时,Master 依然可以提供查询服务。
  3. 复制在 Slave 端也是非阻塞模式的(可以在 redis.conf 设置),Slave 在执行首次同步的时候仍可以使用旧数据集提供查询;
    同样可以配置为阻塞模式:当 Master 与 Slave 失去联系时,让 Slave 返回客户算一个错误提示。
1.7.2 读写分离

主从架构中,可以考虑关闭主服务器的持久化功能(即数据只会持久化到从服务器),只让服务器进行持久化。
这样可以避免提高主服务器的处理性能。从服务器通常被设置为只读模式,这样可以避免从服务器的数据被误修改。

1.8 Redis 分布式锁

分布式锁的三种实现方式:

  1. 数据库乐观锁
  2. 基于Redis的分布式锁
  3. 基于 ZooKeeper 的分布式锁
1.9 Redis 持久化

bgsave 做镜像全量持久化,aof做增量持久化。
因为 bgsave 会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要aof来配合使用。
在Redis实例重启时,会优先使用 aof 来恢复内存的状态,如果没有 aof 日志就会使用 rdb 文件来恢复。

aof文件太大恢复时间过长怎么办?
Redis会定期做 aof 重写,压缩 aof 文件日志大小。
此外在 Redis4.0 之后有了混合持久化的功能,将 bgsave 的全量和 aof 的增量做了融合,这样既保证了恢复的效率又兼顾了数据的安全性。

机器突然宕机怎么办?
Redis数据备份完整性,取决于 aof 日志 sync 属性的配置,如果不要求性能,在每条写指令是都 sync 一下磁盘,就不会丢失数据。
但是在高性能的要求下,每次都 sync 是不现实的,一般都使用定时 sync,比如 1s一次,这个时候最多就会丢失 1s 的数据。

1.10 Redis 同步机制

主从同步。第一次同步时,主节点做一次 bgsave, 并同时将后续修改操作记录到内存 buffer,待完成后将 rdb 文件全量同步到复制节点,
复制节点接受完成后将 rdb 文件加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

1.11 其他

1.Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来

a.使用 keys 命令可以扫出指定模式的 key 列表。
但基于Redis是单线程的,keys 指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才会恢复。

b.使用 scan 指令
scan 指令可以无阻塞的提取出指定模式的 key 列表。
但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接使用 keys 指令长。

2.如何使用Redis做异步队列

一般使用 list 结构做队列, rpush 生产消息,lpop消费消息。当 lpop 没有消息的时候,要适当的 sleep 后再重试。

a.不使用sleep
list 有个指令叫做 blpop, 该命令会在没有消息的时候,阻塞住直到消息到来

b.一次生产,多次消费
使用 pub/sub 主题订阅者模式,可以实现多次消费,但是该模式在消费者下线的情况下,生产的消息会丢失,应采用专业的消息队列(rabbitMQ/Kafka)等。

c.Redis延时队列
使用有序集合,作为延时队列,将时间戳作为 score,使用 zadd 来生产消息,消费者使用 zrangebyscore 指令获取N秒之前的数据轮询进行处理。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值