当前位置: 首页>后端>正文

Redis面试题总结

Redis集群最大节点个数是多少?

16384个

Redis集群的主从复制模型是怎样的?

为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个点都会有N-1个复制品

Redis和 Redisson有什么关系?

Redisson是一个高级的分布式协调redis客服端,能帮助用户在分布式环境中轻松实现一些java的对象(Bloom filter,BitSet,set, SetMultimap, ScoredSortedSet,SortedSet, Map,ConcurrentMap, List, List Multimap, Queue, BlockingQueue, Deque, Blocking Deque, Semaphore, Lock, ReadWriteLock, Atomi cLong, CountDownLa, Publish/ Subscribe, HyperLogLog)

MySQL里有2000w数据, redis中只存20w的数据,如何保证 redis中的数据都是热点数据?

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

Redis集群方案应该怎么做?都有哪些方案?

1.codis.
目前用的最多的集群方案,基本和 twemproxy一致的效果,但它支持在节点数量改变情况下,旧节点数据可恢复到新hash节点。

2.redis cluster3.0
自带的集群,特点在于他的分布式算法不是一致性hash,而是hash槽的概念,以及自身支持节点设置从节点。

3.在业务代码层实现
起几个毫无关联的 redis实例,在代码层对key进行hash计算,然后去对应的 redis实例操作数据。这种方式对hash层代码要求比较高,考虑部分包括,节点失效后的替代算法方案,数据震荡后的自动脚本恢复,实例的监控等等。

Redis集群方案什么情况下会导致整个集群不可用?

有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会因为缺少5501-11000这个范围的槽而不可用。

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

使用keys指令可以扫出指定模式的key列表。

对方接着追问如果这个 redis正在给线上的业务提供服务,那使用keys指令会有什么问题?这个时候你要回答 redis关键的一个特性redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。

主从数据库不一致如何解决场景描述?

对于主从库,读写分离,如果主从库更新同步有时差,就会导致主从库数据的不一致
1、忽略这个数据不一致,在数据一致性要求不高的业务下,未必需要时时一致性
2、强制读主库,使用一个高可用的主库,数据库读写都在主库,添加一个缓存,提升数据读取的性能
3、选择性读主库,添加一个缓存,用来记录必须读主库的数据,将哪个库,哪个表,哪个主键,作为缓存的key,设置缓存失效的时间为主从库同步的时间,如果缓存当中有这个数据,直接读取主库;如果缓存当中没有这个主键,就到对应的从库中读取。

缓存与数据库不一致怎么办?

假设采用的主存分离,读写分离的数据库,如果一个线程A先删除缓存数据,然后将数据写入到主库当中,这个时候,主库和从库同步没有完成,线程B从缓存当中读取数据失败,从从库当中读取到旧数据,然后更新至缓存,这个时候,缓存当中的就是旧的数据。
发生上述不一致的原因在于,主从库数据不一致问题,加入了缓存之后,主从不一致的时间被拉长了;
处理:在从库有数据更新之后,将缓存当中的数据也同时进行更新,即当从库发生了数据更新之后,向缓存发出删除,淘汰这段时间写入的旧数据。

1.Redis支持哪几种数据类型

  • String字符串
    格式: set key value
    string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
    string类型是Redis最基本的数据类型,一个键最大能存储512MB。
    使用场景:
    缓存功能:字符串最经典的使用场景,redis最为缓存层,Mysql作为储存层,绝大部分请求数据都是redis中获取,由于redis具有支撑高并发特性,所以缓存通常能起到加速读写和降低 后端压力的作用。
    计数器:许多运用都会使用redis作为计数的基础工具,他可以实现快速计数、查询缓存的功能,同时数据可以一步落地到其他的数据源。如:视频播放数系统就是使用redis作为视频播放数计数的基础组件。
    共享session:出于负载均衡的考虑,分布式服务会将用户信息的访问均衡到不同服务器上,用户刷新一次访问可能会需要重新登录,为避免这个问题可以用redis将用户session集中管理,在这种模式下只要保证redis的高可用和扩展性的,每次获取用户更新或查询登录信息都直接从redis中集中获取。
    限速:处于安全考虑,每次进行登录时让用户输入手机验证码,为了短信接口不被频繁访问,会限制用户每分钟获取验证码的频率。

  • Hash(哈希)
    格式: hmset name key1 value1 key2 value2
    Redis hash 是一个键值(key=>value)对集合。
    Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
    使用场景:
    哈希结构相对于字符串序列化缓存信息更加直观,并且在更新操作上更加便捷。所以常常用于用户信息等管理,但是哈希类型和关系型数据库有所不同,哈希类型是稀疏的, 而关系型数据库是完全结构化的,关系型数据库可以做复杂的关系查询,而redis去模拟关系型复杂查询 开发困难,维护成本高。

  • List(列表)
    Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
    格式: lpush name value
    在 key 对应 list 的头部添加字符串元素
    格式: rpush name value
    在 key 对应 list 的尾部添加字符串元素
    格式: lrem name index
    key 对应 list 中删除 count 个和 value 相同的元素
    格式: llen name
    返回 key 对应 list 的长度
    使用场景:
    消息队列: redis的lpush+brpop命令组合即可实现阻塞队列,生产者客户端是用lupsh从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞时的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡 和高可用性

  • Set(集合)
    格式: sadd name value
    Redis的Set是string类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
    使用场景:
    标签(tag):集合类型比较典型的使用场景,如一个用户对娱乐、体育比较感兴趣,另一个可能对新闻感兴趣,这些兴趣就是标签,有了这些数据就可以得到同一标签的人,以及用户的共同爱好的标签,这些数据对于用户体验以及曾强用户粘度比较重要。(用户和标签的关系维护应该放在一个事物内执行,防止部分命令失败造成数据不一致)

  • zset(sorted set:有序集合)
    格式: zadd name score value
    Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
    zset的成员是唯一的,但分数(score)却可以重复。
    使用场景:
    排行榜:有序集合经典使用场景。例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面: 按照时间、按照播放量、按照获得的赞数等。

2.Redis的持久化方式

持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。Redis 提供了两种持久化方式:RDB(默认) 和AOF 。

  • RDB
    快照(snapshotting)持久化(RDB)
    Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis主从结构,主要用来提高Redis性能),还可以将快照留在原地以便重启服务器的时候使用。
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命令创建快照。

默认开启,会按照配置的指定时间将内存中的数据快照到磁盘中,创建一个dump.rdb文件,Redis启动时再恢复到内存中。Redis会单独创建fork()一个子进程,将当前父进程的数据库数据复制到子进程的内存中,然后由子进程写入到临时文件中,持久化的过程结束了,再用这个临时文件替换上次的快照文件,然后子进程退出,内存释放。

需要注意的是,每次快照持久化都会将主进程的数据库数据复制一遍,导致内存开销加倍,若此时内存不足,则会阻塞服务器运行,直到复制结束释放内存;都会将内存数据完整写入磁盘一次,所以如果数据量大的话,而且写操作频繁,必然会引起大量的磁盘I/O操作,严重影响性能,并且最后一次持久化后的数据可能会丢失;

  • AOF
    开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof。
    在Redis的配置文件中存在三种不同的 AOF 持久化方式:
appendonly yes
appendfsync always     #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec   #每秒钟同步一次,显示地将多个写命令同步到硬盘
appendfsync no         #让操作系统决定何时进行同步

以日志的形式记录每个写操作(读操作不记录),只需追加文件但不可以改写文件,Redis启动时会根据日志从头到尾全部执行一遍以完成数据的恢复工作。包括flushDB也会执行。主要有两种方式触发:有写操作就写、每秒定时写(也会丢数据)。

因为AOF采用追加的方式,所以文件会越来越大,针对这个问题,新增了重写机制,就是当日志文件大到一定程度的时候,会fork出一条新进程来遍历进程内存中的数据,每条记录对应一条set语句,写到临时文件中,然后再替换到旧的日志文件(类似rdb的操作方式)。默认触发是当aof文件大小是上次重写后大小的一倍且文件大于64M时触发。

当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。一般情况下,只要使用默认开启的RDB即可,因为相对于AOF,RDB便于进行数据库备份,并且恢复数据集的速度也要快很多。开启持久化缓存机制,对性能会有一定的影响,特别是当设置的内存满了的时候,更是下降到几百reqs/s。所以如果只是用来做缓存的话,可以关掉持久化。

3.redis设置过期时间

Redis中有个设置时间过期的功能,即对存储在 redis 数据库中的值可以设置一个过期时间。作为一个缓存数据库,这是非常实用的。如我们一般项目中的 token 或者一些登录信息,尤其是短信验证码都是有时间限制的,按照传统的数据库处理方式,一般都是自己判断过期,这样无疑会严重影响项目性能。
我们 set key 的时候,都可以给一个 expire time,就是过期时间,通过过期时间我们可以指定这个 key 可以存活的时间。如果假设你设置了一批 key 只能存活1个小时,那么接下来1小时后,redis是怎么对这批key进行删除的?

定期删除+惰性删除

  • 定期删除:redis默认是每隔 100ms 就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。
  • 惰性删除 :定期删除可能会导致很多过期 key 到了时间并没有被删除掉。所以就有了惰性删除。

4.redis内存淘汰机制

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的).
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。

Redis面试题总结,第1张

5.redis 事务

Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的
Redis会将一个事务中的所有命令序列化,然后按顺序执行。

  • redis 不支持回滚“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”, 所以 Redis 的内部可以保持简单且快速。
  • 如果在一个事务中的命令出现错误,那么所有的命令都不会执行;
  • 如果在一个事务中出现运行错误,那么正确的命令会被执行。

1)MULTI命令用于开启一个事务,它总是返回OK。 MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。
2)EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil 。
3)通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退出。
4)WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。 可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。

缓存问题

缓存穿透:

数据库和缓存中都没有数据,如果此时大量的请求过来,所有的请求会直接访问数据库,造成数据库压力过大。
解决:
1.直接返回空数据 null
2.使用布隆过滤器 校验key(并不能百分之百保证一定能够)

缓存击穿:

缓存中没有,数据库中有,(有可能是在某一个key刚刚过期的时候 大量请求访问该数据) 所有的请求都去数据库获取数据,造成数据库压力过大。
解决:
1、对于获取数据的操作 加锁 如果缓存中没有 此时会有一个请求去数据库获取数据 并放入缓存 其他请求再次判断缓存中是否有数据 从缓存中获取
2、热点数据永不过期

缓存雪崩:

缓存中大量的数据,同时过期。如果此时大量请求过来,全部打到数据库,造成数据库压力过大。
解决:
1.设置过期时间为随机时间
2.热点数据永不过期

缓存和数据库 双写一致性?(缓存中的数据都是数据中来的)

在更新缓存方面,对于更新完数据库,是更新缓存呢,还是删除缓存。又或者是先删除缓存,再更新数据库,其实大家存在很大的争议。

解决方案:
1.先更新数据库,再更新缓存 (普遍反对,举例当你对某个商品的价格进行了改变,先改了数据库的价格,然后用户用缓存的价格下单了,损失惨重)
2.先删缓存,再更新数据库
这个情况也会有一点小问题,但是我们可以用延时双删来尽可能规避。

延时双删
A进行写操作 删除缓存
B查询数据 缓存中没有 去数据库拿 拿到以后放入缓存中 此时缓存中存放的脏数据
A 更新数据库
A睡小会儿
A再次删缓存 (放入消息队列中 重试 如果还不行 手动删除)


https://www.xamrdz.com/backend/3rc1925635.html

相关文章: