Redis 面试题

Redis 面试题

Q:Redis有哪些优势?

  • 速度快,因为数据存在内存中
  • 支持丰富数据类型,支持string,list,set,sorted set,hash
  • 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
  • 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
  • 单线程,单进程,采用IO多路复用技术。

Q:哪些场景不适合使用Redis?

  • 更新频繁的情景,对于更新频率过高的数据,频繁同步缓存中的数据会花费较多的时间。
  • 一致性要求严格的情景,只要数据产生了副本,就一定会出现副本数据与原数据之间的差异;
  • 读少的情景,对于读取非常少的系统而言,使用缓存就完全没有意义了,毕竟使用缓存的目的是为了读取数据更高效;
  • 数据量很小的情况下,也没必要使用缓存了,因为数据库本身完全可以支持。

Redis数据结构

Q:Redis的存储结构是怎样的?
key-value键值对。

Q:Redis支持哪些数据结构?
string(字符串),hash(哈希),list(队列),set(集合)及 sorted set(有序集合)。其他的还有 bitmaps(位图),hyperloglog(可用于基数统计),GEO(地理位置定位) 等。

Q:Redis的数据结构,有哪些应用场景?
string,简单地get/set缓存。
hash,可以缓存用户资料。比如命令: hmset user1 name “lin” sex “male” age “25” ,缓存用户user1的资料,姓名为lin,性别为男,年龄25。
list,可以做队列。往list队列里面push数据,然后再pop出来。
set,set堆放的是一堆不重复值的集合。所以可以做全局去重的功能。
zset,sorted set多了一个权重参数score,集合中的元素能够按score进行排列。可以用来做排行榜。
详情见:https://www.cnblogs.com/expiator/p/10274151.html

Q:Redis做计数器,可以使用哪些命令?
incr,做加一操作,是原子性的。

Q:Redis的数据结构,底层分别是由什么实现的?
(1)string:Redis自己构建了一种名为 简单动态字符串(simple dynamic string,SDS)的抽象类型,并将 SDS 作为 Redis的默认字符串表示。
(2)list:Redis的List,底层是ZipList,不满足ZipList就使用quicklist双向链表。
首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是 ziplist ,即压缩列表 . 它将所有的元素紧挨着一起存储,分配的是一块连续的内存,由于内存是连续的,就减少了很多内存碎片和指针的内存占用,进而节约了内存。
当数据量比较多才会改成 quicklist. quickList就是一个标准的双向链表的配置,有head 有tail;
(3)hash:底层是zipList和hashTable。
当同时满足下面两个条件时,使用ziplist(压缩列表)编码: 列表保存元素个数小于512个; 每个元素长度小于64字节
不能满足这两个条件的时候使用 hashtable 编码。
hashtable 编码的哈希表对象底层使用字典数据结构,哈希对象中的每个键值对都使用一个字典键值对。
(4)set:底层是intset 和 HashTable。
intset 编码的集合对象使用整数集合作为底层实现,集合对象包含的所有元素都被保存在整数集合中。
hashtable 编码的集合对象使用 字典作为底层实现,字典的每个键都是一个字符串对象,这里的每个字符串对象就是一个集合中的元素,而字典的值则全部设置为 null。这里可以类比Java集合中HashSet 集合的实现,HashSet 集合是由 HashMap 来实现的,集合中的元素就是 HashMap 的key,而 HashMap 的值都设为 null。
当集合同时满足以下两个条件时,使用 intset 编码:集合对象中所有元素都是整数;集合对象所有元素数量不超过512。
不能满足这两个条件的就使用 hashtable 编码。
(5)sorted set:底层是 zipList 和 skipedList 。
skiplist 编码的有序集合对象使用 zset 结构作为底层实现,一个 zset 结构同时包含一个字典和一个跳跃表:
字典的键保存元素的值,字典的值则保存元素的分值;跳跃表节点的 object 属性保存元素的成员,跳跃表节点的 score 属性保存元素的分值。

typedef struct zset{//跳跃表zskiplist *zsl;//字典dict *dice;
} zset;

当有序集合对象同时满足以下两个条件时,对象使用 ziplist 编码:保存的元素数量小于128;保存的所有元素长度都小于64字节。  
不能满足上面两个条件的使用 skiplist 编码。

参考资料:https://www.cnblogs.com/ysocean/p/9102811.html
详情见:https://blog.csdn.net/Sqdmn/article/details/103634208

Q:为什么sorted set(有序集合)需要同时使用跳跃表和字典来实现?
使用字典,根据key查找value的复杂度是O(1),但是字典是以无序的方式来保存元素的,进行范围型操作会比较麻烦。
而跳跃表进行范围型操作会比较快。
因此sorted set(有序集合)需要同时使用跳跃表和字典来实现。

Q:讲一下SDS的存储结构?

struct sdshdr {//记录buf数组中已使用字节的数量//等于SDS所保存字符串的长度int len;//记录buf数组中未使用字节的数量int free;//字节数组,用于保存字符串。SDS遵循C字符串以空字符结尾的惯例。比如'R','e','d','i','s','\0'char buf[];
}

Q:C字符串和SDS,有什么区别?

详情见: 《Redis设计与实现》

Q:对比C字符串,Redis的SDS有哪些优点?
(1)常数复杂度获取字符串的长度。因为SDS中的len字段就记录了字符串长度。
(2)杜绝缓冲区溢出。
(3)减少修改字符串长度时所需的内存重分配的次数。
(4)二进制安全。
(5)兼容部分C字符串的函数。

Redis持久化

Q:Redis怎么保证可靠性?
Q:Redis的持久化方式有哪些?有哪些优缺点?
一个可靠安全的系统,肯定要考虑数据的可靠性,尤其对于内存为主的redis,就要考虑一旦服务器挂掉,启动之后,如何恢复数据的问题,也就是说数据如何持久化的问题。

  • AOF,就是备份操作记录。AOF由于是备份操作命令,备份快,恢复慢。
    AOF以独立日志的方式,记录了每次写入命令,重启时再重新执行AOF文件中的命令来恢复数据。AOF的工作流程包括:命令写入(append)、文件同步(sync)、文件重写(rewrite)、重启加载(load)。
    AOF持久化的优缺点如下:
    优点:与RDB持久化可能丢失大量的数据相比,AOF持久化的安全性要高很多。通过使用everysec选项,用户可以将数据丢失的时间窗口限制在1秒之内。
    缺点:AOF文件存储的是协议文本,它的体积要比二进制格式的”.rdb”文件大很多。AOF需要通过执行AOF文件中的命令来恢复数据库,其恢复速度比RDB慢很多。AOF在进行重写时也需要创建子进程,在数据库体积较大时将占用大量资源,会导致服务器的短暂阻塞。

  • RDB,就是备份所有数据,使用了快照。RDB恢复数据比较快。
    RDB(Redis Database)是Redis默认采用的持久化方式,它以快照的形式将进程数据持久化到硬盘中。RDB会创建一个经过压缩的二进制文件,文件以“.rdb”结尾,内部存储了各个数据库的键值对数据等信息。RDB持久化的触发方式有两种:
    手动触发:通过SAVE或BGSAVE命令触发RDB持久化操作,创建“.rdb”文件;
    自动触发:通过配置选项,让服务器在满足指定条件时自动执行BGSAVE命令。
    其中,SAVE命令执行期间,Redis服务器将阻塞,直到“.rdb”文件创建完毕为止。而BGSAVE命令是异步版本的SAVE命令,它会使用Redis服务器进程的子进程,创建“.rdb”文件。BGSAVE命令在创建子进程时会存在短暂的阻塞,之后服务器便可以继续处理其他客户端的请求。总之,BGSAVE命令是针对SAVE阻塞问题做的优化,Redis内部所有涉及RDB的操作都采用BGSAVE的方式,而SAVE命令已经废弃.
    RDB持久化的优缺点如下:
    优点:RDB生成紧凑压缩的二进制文件,体积小,使用该文件恢复数据的速度非常快;
    缺点:RDB快照是一次全量备份,存储的是内存数据的二进制序列化形式,存储上非常紧凑。当进行快照持久化时,会开启一个子进程专门负责快照持久化,子进程会拥有父进程的内存数据,父进程修改内存子进程不会反应出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据。
    无法保证最近一次快照后的数据。所以RDB持久化没办法做到实时的持久化。

  • RDB-AOF混合持久化:
    Redis从4.0开始引入RDB-AOF混合持久化模式,这种模式是基于AOF持久化构建而来的。用户可以通过配置文件中的“aof-use-rdb-preamble yes”配置项开启AOF混合持久化。Redis服务器在执行AOF重写操作时,会按照如下原则处理数据:
    像执行BGSAVE命令一样,根据数据库当前的状态生成相应的RDB数据,并将其写入AOF文件中;
    对于重写之后执行的Redis命令,则以协议文本的方式追加到AOF文件的末尾,即RDB数据之后。
    通过使用RDB-AOF混合持久化,用户可以同时获得RDB持久化和AOF持久化的优点,服务器既可以通过AOF文件包含的RDB数据来实现快速的数据恢复操作,又可以通过AOF文件包含的AOF数据来将丢失数据的时间窗口限制在1s之内。

参考资料: https://www.nowcoder.com/issue/tutorial?tutorialId=94&uuid=d96fa15a169c44e9a94dda690f27da28
Q:AOF文件过大,怎么处理?
会进行AOF文件重写。
1.随着AOF文件越来越大,里面会有大部分是重复命令或者可以合并的命令
2.重写的好处:减少AOF日志尺寸,减少内存占用,加快数据库恢复时间。
执行一个 AOF文件重写操作,重写会创建一个当前 AOF 文件的体积优化版本。
详情见: https://blog.csdn.net/stevendbaguo/article/details/82855726

Q:讲一下AOF重写的过程。
AOF文件重写的具体过程分为几步:
首先,根据当前Redis内存里面的数据,重新构建一个新的AOF文件,
然后,读取当前Redis里面的数据,写入到新的AOF文件里面,
最后,重写完成以后,用新的AOF文件覆盖现有的AOF文件.

另外,因为AOF在重写的过程中需要读取当前内存里面所有的键值数据,再生成对应的一条指令进行保存。
而这个过程是比较耗时的,对业务会产生影响。
所以Redis把重写的过程放在一个后台子进程里面来完成,
这样一来,子进程在做重写的时候,主进程依然可以继续处理客户端请求。
最后,为了避免子进程在重写过程中,主进程的数据发生变化,
导致AOF文件和Redis内存中的数据不一致的问题,Redis还做了一层优化。
就是子进程在重写的过程中,主进程的数据变更需要追加到AOF重写缓冲区里面。
等到AOF文件重写完成以后,再把AOF重写缓冲区里面的内容追加到新的AOF文件里面

Q:讲一下Redis的事务
先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令。如果想放弃这个事务,可以使用DISCARD命令。
Redis事务无法回滚,那怎么处理?

Q:怎么设置Redis的key的过期时间?
key的的过期时间通过EXPIRE key seconds命令来设置数据的过期时间。返回1表明设置成功,返回0表明key不存在或者不能成功设置过期时间。

Redis过期策略

Q:Redis的过期策略有哪些?
惰性删除:当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key,并按照key不存在去处理。
惰性删除,对内存不太好,已经过期的key会占用太多的内存。

定期删除:每隔一段时间,就会对Redis进行检查,主动删除一批已过期的key。
定期删除的策略,默认情况下Redis每秒检测10次, 检测的对象是所有设置了过期时间的键集合,每次从这个集合中随机检测20个键查看他们是否过期,如果过期就直接删除,如果过期的key比例超过1/4,那就把上述操作重复一次(贪心算法)。同时为了保证过期扫描不会出现循环过度,导致线程卡死现象,算法还增加了扫描时间的上限,默认不会超过25ms。

逻辑记忆:为什么要删除?过期了没用了,要删除,得用过期删除策略。
谐音联想记忆:过惰,过除人

Q:为什么Redis不使用定时删除?
定时删除,就是在设置key的过期时间的同时,创建一个定时器,让定时器在过期时间来临时,立即执行对key的删除操作。

定时删除,会占用CPU,影响服务器的响应时间和性能。

Redis内存淘汰

Q:Redis 的内存淘汰机制都有哪些?
当前已用内存超过maxmemory限定时,会触发主动清理策略,也就是Redis的内存回收策略。

LRU、TTL。

noeviction:默认策略,不会删除任何数据,拒绝所有写入操作并返回客户端错误信息,此时Redis只响应读操作。
volatitle-lru:根据LRU算法删除设置了超时属性的键,知道腾出足够空间为止。如果没有可删除的键对象,回退到noeviction策略。
allkeys-lru:根据LRU算法删除键,不管数据有没有设置超时属性,直到腾出足够空间为止。
allkeys-random:随机删除所有键,知道腾出足够空间为止。
volatitle-random:随机删除过期键,知道腾出足够空间为止。
volatitle-ttl:根据键值对象的ttl属性,删除最近将要过期数据。如果没有,回退到noeviction策略

更详细的策略如下:
volatile-lru:从已设置过期时间的key中,移出最近最少使⽤的key进⾏淘汰
allkeys-lru:当内存不⾜以容纳新写⼊数据时,在键空间中,移除最近最少使⽤的key(这个是最常⽤的)
volatile-ttl:从已设置过期时间的key中,移出将要过期的key
volatile-random:从已设置过期时间的key中,随机选择key淘汰
allkeys-random:从key中随机选择key进⾏淘汰
no-eviction:禁⽌淘汰数据。当内存达到阈值的时候,新写⼊操作报错
volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使⽤的数据淘汰(LFU(Least
Frequently Used)算法,也就是最频繁被访问的数据将来最有可能被访问到)
allkeys-lfu:当内存不⾜以容纳新写⼊数据时,在键空间中,移除最不经常使⽤的key。

Q:手写一下LRU算法 。

主从模式、哨兵模式

Q:Redis的搭建有哪些模式?
主从模式、哨兵模式、Cluster(集群)模式。
最好是用集群模式。
详情见:https://new.qq.com/omn/20180126/20180126G00THE.html

主从模式:一主多从,读写分离。主节点写,从节点读,主从模式的弊端是如果master挂了,服务器只能进行读功能。
哨兵模式:

1.可以解决主从模式的弊端:master挂掉之后不能提供写功能。
2.哨兵模式是建立在主从模式的
3.当master挂掉之后,会自动从slave中选一个作为master。若master重新启动,master则会转化为现有的master下的一个slave
4.当slave切换时,会通过发布订阅方式,将slave所对应的master更改
5.因为哨兵也是一个进程,所以也有挂掉的可能,需要配置多个哨兵互相监督。一个哨兵可以监督多个主从数据库。同样,一个主从数据库可以被多个哨兵监督。

集群模式:

1.自动将数据进行分片,每个master上放一部分数据提供内置的高可用支持,部分master不可用时,还是可以继续工作的。
2.支撑N个redis master node,每个master node都可以挂载多个slave node。
3.高可用,因为每个master都有salve节点,那么如果mater挂掉,redis cluster这套机制,就会自动将某个slave切换成master。

Q:讲一下Redis主从复制的过程。(Redis是怎么同步数据的?讲一下Redis的同步机制)
从机发送SYNC(同步)命令,主机接收后会执行BGSAVE(异步保存)命令备份数据。
主机备份后,就会向从机发送备份文件。主机之后还会发送缓冲区内的写命令给从机。
当缓冲区命令发送完成后,主机执行一条写命令,就会往从机发送同步写入命令。
更详细的步骤见: https://www.cnblogs.com/expiator/p/9881989.html

Q:Redis主从同步,是全量同步,还是部分同步?
redis主从数据同步可以分为全量同步和部分同步。
每个redis启动时都会生成一个随机字符串RID(replication ID ),从节点怎么判断需要同步多少呢?
通过偏移量offset来确定。主节点每次有数据写入,都会在offset上加上写入的字节长度,所以从节点通过比较RID和offset就能确定需要同步多少数据。

部分同步:
1.从节点保存的主节点RID与主节点的RID一致(相同)时,从节点非第一次同步,通过offset把这部分需要同步的数据放入缓冲区,然后异步同步到从节点。
(谐音记忆:同步,同"部"。ID相"同","部"分同步)

全量同步:
1.当从节点第一次启动,进行数据同步时,是全量同步的。
2.当从节点保存的主节点RID与主节点的RID不一致时,说明主节点可能宕机或重启过啥的,数据已经不一致了,所以需要重新全量同步,同时删除掉旧的数据。
3.当部分同步需要同步的数据超过了缓存区的大小限制时,采用全量同步。

详情见: https://blog.csdn.net/zhujuntiankong/article/details/122938306

Q:讲一下Redis哨兵机制。
下面是Redis官方文档对于哨兵功能的描述:
监控(Monitoring):哨兵会不断地检查主节点和从节点是否运作正常。
自动故障转移(Automatic Failover):当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点。
配置提供者(Configuration Provider):客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址。
通知(Notification):哨兵可以将故障转移的结果发送给客户端。

Redis集群

Q:你用过的Redis是多主多从的,还是一主多从的?集群用到了多少节点?用到了多少个哨兵?
集群模式。三主三从。

Q:你们的Redis采用的是哪个版本?
3.0及以上。支持集群。

Q:Redis采用多主多从的集群模式,各个主节点的数据是否一致?
各个主节点的数据不会一样。每个主节点,负责一部分哈希槽的数据。
查询的时候,如果当前节点没有对应的key数据, Redis会转发到对应的节点上进行查询。

Q:Redis集群有哪些特性?
master和slaver。主从复制。哨兵模式。

Q:Redis集群会提供读写分离么?
不提供。
slave充当副本,不对外提供读、写服务,只作为故障转移使用。

哈希槽

Q:Redis集群数据分片的原理是什么?(Redis集群使用的是什么算法?)
Redis数据分片原理是哈希槽(hash slot)。
Redis 集群有 16384 个哈希槽。 每一个 Redis 集群中的节点都承担一个哈希槽的子集。

Q: Redis是怎么进行水平扩容的?
Redis的哈希槽让在集群中添加和移除节点非常容易。
在Redis集群中新增加一个主节点,主要是会重新分片,需要给新增的节点指派哈希槽。
例如,如果我想添加一个新节点 D,Redis会让我从节点 A,B, C 移动一些哈希槽到节点 D。同样地,如果我想从集群中移除节点 A,我只需要移动 A 的哈希槽到 B 和 C。 当节点 A 变成空的以后,我就可以从集群中彻底删除它。 因为从一个节点向另一个节点移动哈希槽并不需要停止操作,所以添加和移除节点,或者改变节点持有的哈希槽百分比,都不需要任何停机时间(downtime)。

Q:讲一下一致性Hash算法。
一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环, 我们对key进行哈希计算,使用哈希后的结果对2^32取模,
hash环上必定有一个点与这个整数对应。依此确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器。

一致性Hash算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。

比如,集群有四个节点 Node A、B、C、D,增加一台节点 Node X。Node X 的位置在 Node B 到 Node C 直接,那么受到影响的仅仅是 Node B 到 Node X 间的数据,它们要重新落到 Node X 上。

所以一致性哈希算法对于容错性和扩展性有非常好的支持。

参考资料: https://blog.csdn.net/qq_21125183/article/details/90019034

Q:为什么Redis Cluster分片不使用Redis一致性Hash算法?
一致性哈希算法也有一个严重的问题,就是数据倾斜。

如果在分片的集群中,节点太少,并且分布不均,一致性哈希算法就会出现部分节点数据太多,部分节点数据太少。也就是说无法控制节点存储数据的分配。

Q:集群的拓扑结构有没有了解过?集群是怎么连接的?
无中心结构。Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。

Redis集群通信

Q:Redis集群中的各个节点发送和接收消息,有哪些类型?
MEET消息:发送者会向接收者发送MEET消息,请求接收者加入发送者当前所处的集群。
PING消息:集群里每个节点,每隔一秒钟就会随机选出5个节点,对这5个节点中最长时间没有发送过PING消息的节点发送PING消息,以此来检测被选中的节点是否在线。
PONG消息:当接收者收到发送者发来的MEET消息或者PING消息时,为了向发送 者确认这条MEET消息或者PING消息已到达,接收者会向发送者返回一条PONG 消息。另外,一个节点也可以通过向集群广播自己的PONG消息来让集群中的其他 节点立即刷新关于这个节点的认识,例如当一次故障转移操作成功执行之后,新的主节点会向集群广播一条PONG消息,以此来让集群中的其他节点立即知道这个节 点已经变成了主节点,并且接管了已下线节点负责的槽。
FAIL消息:当一个主节点A判断另一个主节点B已经进人FAIL状态时,节点A 会向集群广播一条关于节点B的EAI工消息,所有收到这条消息的节点都会立即将 节点B标记为已下线。
PUBLISH消息:当节点接收到一个PUBLISH命令时,节点会执行这个命令,并向 集群广播一条PUB工ISH消息,所有接收到这条PUBLISH消息的节点都会执行相同 的PUBLISH命令。

Q:讲一下Redis集群的通信协议。
Redis集群,采用Gossip协议进行通信。
其中Gossip协议,由MEET、PING、PONG三种消息实现。

###Redis缓存 面试题
Q:缓存雪崩是什么?
如果缓存数据设置的过期时间是相同的,并且Redis恰好将这部分数据全部删光了。这就会导致在这段时间内,这些缓存同时失效,全部请求到数据库中。这就是缓存雪崩。
怎么预防缓存雪崩?
解决方法:在缓存的时候给过期时间加上一个随机值,这样就会大幅度的减少缓存在同一时间过期。

Q:缓存雪崩怎么处理?

  • 修改数据放入缓存的时间,或修改数据在缓存中的过期时间;
  • 让缓存数据永不过期;
  • 互斥锁,由高并发转换成低并发,保护DB;
  • 热点隔离,实时热点发现系统;
  • 水平扩容数据库,压力平摊,保护DB;
  • 提前压测,得出阈值,限流处理,保护服务与DB;
    参考资料: https://blog.csdn.net/javahome_laohei/article/details/123724004

Q:缓存穿透是什么?
缓存穿透是指查询一个一定不存在的数据。由于缓存不命中,并且出于容错考虑,如果从数据库查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,失去了缓存的意义。
Q:怎么解决缓存穿透?
有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存
储系统的查询压力。
另外也有一个更为简单粗暴的方法, 如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库。

Q:讲一下布隆过滤器。
布隆过滤器的主要是由一个很长的二进制向量和若干个(k个)散列映射函数组成。因为每个元数据的存储信息值固定,而且总的二进制向量固定。所以在内存占用和查询时间上都远远超过一般的算法。当然存在一定的不准确率(可以控制)和不容易删除样本数据。
布隆过滤器的优点: 大批量数据去重,特别的占用内存。但是用布隆过滤器(Bloom Filter)会非常的省内存。
布隆过滤器的特点:当布隆过滤器说某个值存在时,那可能就不存在,如果说某个值不存在时,那肯定就是不存在了。
布隆过滤器的应用场景:新闻推送(不重复推送)。解决缓存穿透的问题。

Q:什么是缓存与数据库双写一致问题?
Q:如何保证缓存与数据库的一致性?
读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存。
先更新数据库,再删除缓存。

Q:缓存与数据库的更新,会出现哪些问题?
1.更新DB或更新缓存两个步骤,因为无法保证原子性,只要有一个失败,都会导致数据不一致。
2.并发场景,并发写或并发读写场景导致数据不一致。

Q:先更新数据库,再更新缓存,会有什么问题?
如果同时有请求A和请求B进行更新操作,那么会出现
(1)线程A更新了数据库
(2)线程B更新了数据库
(3)线程B更新了缓存
(4)线程A更新了缓存
这就出现请求A更新缓存应该比请求B更新缓存早才对,但是因为网络等原因,B却比A更早更新了缓存。这就导致了脏数据,因此不考虑。

Q:先删除缓存,再更新数据库,会有什么问题?
该方案会导致不一致的原因是。同时有一个请求A进行更新操作,另一个请求B进行查询操作。那么会出现如下情形:
(1)请求A进行写操作,删除缓存
(2)请求B查询发现缓存不存在
(3)请求B去数据库查询得到旧值
(4)请求B将旧值写入缓存
(5)请求A将新值写入数据库
上述情况就会导致不一致的情形出现。而且,如果不采用给缓存设置过期时间策略,该数据永远都是脏数据。

Q:先更新数据库,再删除缓存,会有什么问题?怎么解决?
如果第一步的先更新数据库成功了,但是在第二步的删除缓存失败了,那么就会出现数据库是新数据,而缓存是旧数据,数据不一致。
怎么解决呢?延迟双删。
异步重试。删除缓存失败,那就多重试几次。
可以通过MQ重试,把需要缓存的redis的key发到MQ,MQ消费者取出缓存,并删除缓存。

详细的解决方案,见: https://www.cnblogs.com/rjzheng/p/9041659.html

###Redis分布式锁 面试题
Redis分布式锁, 参数资料:https://blog.csdn.net/lisu061714112/article/details/120464096

Q:什么场景用Redis分布式锁?什么场景用zookeeper分布式锁?
如果你的实际业务场景,更需要的是保证数据高可用性。那么请使用AP类型的分布式锁,比如:redis,它是基于内存的,性能比较好,但有丢失数据的风险。
如果你的实际业务场景,更需要的是保证数据一致性。那么请使用CP类型的分布式锁,比如:zookeeper,它是基于磁盘的,性能可能没那么好,但数据一般不会丢。
参考资料:https://blog.csdn.net/lisu061714112/article/details/120464096

Q:Redis如何实现分布式锁?
使用set key value ex nx 命令。
当key不存在时,将 key 的值设为 value ,返回1。若给定的 key 已经存在,则setnx不做任何动作,返回0。
当setnx返回1时,表示获取锁,做完操作以后del key,表示释放锁,如果setnx返回0表示获取锁失败。
详细的命令如下:

set key value [EX seconds] [PX milliseconds] [NX|XX]
EX seconds:设置失效时长,单位秒
PX milliseconds:设置失效时长,单位毫秒
NX:key不存在时设置value,成功返回OK,失败返回(nil)
XX:key存在时设置value,成功返回OK,失败返回(nil)。

示例如下:

set name fenglin ex 100 nx

详情见:https://blog.csdn.net/qq_30038111/article/details/90696233
Q:为什么不先set nx,然后再使用expire设置超时时间?
我们需要保证setnx命令和expire命令以原子的方式执行,否则如果客户端执行setnx获得锁后,这时客户端宕机了,那么这把锁没有设置过期时间,导致其他客户端永远无法获得锁了。

Q:使用Redis分布式锁,key和value分别设置成什么?
value可以使用json格式的字符串。
示例:

{"count":1,"expireAt":147506817232,"requestId":22224,"mac":"28-D2-44-0E-0D-9A","threadId":14
}

Q:Redis实现的分布式锁,如果某个系统获取锁后,宕机了怎么办?
系统模块宕机的话,可以通过设置过期时间(就是设置缓存失效时间)解决。系统宕机时锁阻塞,过期后锁释放。

Q:如果线程A加锁成功了,但是由于业务功能耗时时间很长,超过了设置的超时时间,这时候redis会自动释放线程A加的锁,
其他线程B就能拿到锁,怎么处理这种问题?

属于锁超时的问题。
如果达到了超时时间,但业务代码还没执行完,需要给锁自动续期。
获取锁之后,自动开启一个定时任务,每隔10秒钟,自动刷新一次过期时间。这种机制在redisson框架中,有个比较霸气的名字:watch dog,即传说中的看门狗。

Q:Redis分布式锁如何删除锁?
Q:设置缓存失效时间,那如果前一个线程把这个锁给删除了呢?
Q:如果加锁和解锁之间的业务逻辑执行的时间比较长,超过了锁过期的时间,执行完了,又删除了锁,就会把别人的锁给删了。怎么办?
这两个属于锁超时的问题。
可以将锁的value设置为Json字符串,在其中加入线程的id或者请求的id,在删除之前,get一下这个key,判断key对应的value是不是当前线程的。只有是当前线程获取的锁,当前线程才可以删除。

Q:Redis分布式锁,怎么保证可重入性?
可以将锁的value设置为Json字符串,在其中加入线程的id和count变量。
当count变量的值为0时,表示当前分布式锁没有被线程占用。
如果count变量的值大于0,线程id不是当前线程,表示当前分布式锁已经被其他线程占用。
如果count变量的值大于0,线程id是当前线程的id,表示当前线程已经拿到了锁,不必阻塞,可以直接重入,并将count变量的值加一即可。
这种思路,其实就是参考了ReentrantLock可重入锁的机制。

Q:Redis做分布式锁,Redis做了主从,如果设置锁之后,主机在传输到从机的时候挂掉了,从机还没有加锁信息,如何处理?
可以使用开源框架Redisson,采用了redLock。

Q:讲一下Redis的redLock。
RedissonRedLock加锁过程如下:
(1)循环向所有的redisson node节点加锁,假设节点数为N,例子中N等于5。
(2)如果在N个节点当中,有N/2 + 1个节点加锁成功了,那么整个RedissonRedLock加锁是成功的。
(3) 如果在N个节点当中,小于N/2 + 1个节点加锁成功,那么整个RedissonRedLock加锁是失败的。
(4) 如果中途发现各个节点加锁的总耗时,大于等于设置的最大等待时间,则直接返回失败。
使用Redlock算法,确实能解决多实例场景中,假如master节点挂了,导致分布式锁失效的问题。
参考资料:https://blog.csdn.net/lisu061714112/article/details/120464096

IO多路复用

Q:什么是IO多路复用?
IO多路复用(IO Multiplexing)一种同步IO模型,单个进程/线程就可以同时处理多个IO请求。一个进程/线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;没有文件句柄就绪时会阻塞应用程序,交出cpu。
多路是指网络连接,复用指的是同一个进程/线程。
一个进程/线程虽然任一时刻只能处理一个请求,但是处理每个请求的事件时,耗时控制在 1 毫秒以内,这样 1 秒内就可以处理上千个请求,把时间拉长来看,多个请求复用了一个进程/线程,这就是多路复用,这种思想很类似一个 CPU 并发多个进程,所以也叫做时分多路复用。
参考资料: https://blog.csdn.net/m0_51319483/article/details/124264619
Q:IO多路复用,有哪些实现方式?
select、poll、epoll
Q:Redis采用的是哪一种IO多路复用方式?
epoll。
Q:select、poll、epoll的区别?

selectpollepoll
数据结构bitmap数组红黑树
最大连接数1024无上限无上限
fd拷贝每次调用selec拷贝每次调用poll拷贝fd首次调用epoll_ctl拷贝,每次调用epoll_wait不拷贝
工作效率轮询O:(n)轮询:O(n)回调:O(1)

详情见: https://blog.csdn.net/wteruiycbqqvwt/article/details/90299610

Redis限流

Q:使用 Redis设计一个限流的功能。
限流涉及的最主要的就是滑动窗口。
第一种: 使用Redis的 zset。
使用一个zset,当每一次请求进来的时候,value保持唯一,可以用UUID生成,而score可以用当前时间戳表示,因为score我们可以用来计算当前时间戳之内有多少的请求数量。而zset数据结构也提供了range方法让我们可以很轻易的获取到2个时间戳内有多少请求。
第二种:使用Redis的 list。
依靠Java的定时任务,定时往令牌桶List中加入新的令牌(使用List的rightPush方法),当然令牌也需要唯一性,这里还是用UUID生成令牌。
每访问一次请求的时候,可以从Redis中获取一个令牌,如果拿到令牌了,那就说明没超出限制,而如果拿不到,则结果相反。
详情见: https://zhuanlan.zhihu.com/p/439093222

参考资料:

《Redis设计与实现》
《Redis设计与实现》的笔记: https://www.cnblogs.com/expiator/p/10428655.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/83998.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

交换机端口镜像详解

交换机端口镜像是一种网络监控技术,它允许将一个或多个交换机端口的网络流量复制并重定向到另一个端口上,以便进行流量监测、分析和记录。通过端口镜像,管理员可以实时查看特定端口上的流量,以进行网络故障排查、安全审计和性能优…

Vue.js vs React:哪一个更适合你的项目?

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…

[NLP] LLM---<训练中文LLama2(五)>对SFT后的LLama2进行DPO训练

当前关于LLM的共识 大型语言模型(LLM)使 NLP 中微调模型的过程变得更加复杂。最初,当 ChatGPT 等模型首次出现时,最主要的方法是先训练奖励模型,然后优化 LLM 策略。从人类反馈中强化学习(RLHF&#xff09…

优化系统报错提示信息,提高人机交互(一)

1、常规报错及处理 package com.example.demo.controller;import com.example.demo.service.IDemoService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.w…

【JAVA】idea初步使用+JDK详细配置

1、官方下载idea 官网:Download IntelliJ IDEA – The Leading Java and Kotlin IDE (1)、下载教程 我下载没截屏,详细教程请看 原文:手把手教你JDKIDEA的安装和环境配置_idea配置jdk_快到锅里来呀的博客-CSDN博客 2、启动项目时候需要配置J…

Spring事件机制之ApplicationEvent

博主介绍:✌全网粉丝4W,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验…

全栈性能测试工具:RunnerGo

随着自动化测试技术的不断进步,自动化测试已成为企业级应用的重要组成部分。然而,传统的性能测试工具往往复杂、繁琐,让企业陷入了两难的境地。软件测试正逐渐从手动测试向自动化测试转变,各种自动化测试工具和框架层出不穷&#…

手撕 LFU 缓存

大家好,我是 方圆。LFU 的缩写是 Least Frequently Used,简单理解则是将使用最少的元素移除,如果存在多个使用次数最小的元素,那么则需要移除最近不被使用的元素。LFU 缓存在 LeetCode 上是一道困难的题目,实现起来并不…

【Godot】解决游戏中的孤立/孤儿节点及分析器性能问题的分析处理

Godot 4.1 因为我在游戏中发现,越运行游戏变得越来越卡,当你使用 Node 节点中的 print_orphan_nodes() 方法打印信息的时候,会出现如下的孤儿节点信息 孤儿节点信息是以 节点实例ID - Stray Node: 节点名称(Type: 节点类型) 作为格式输出&a…

腾讯mini项目-【指标监控服务重构】2023-08-23

今日已办 进度和问题汇总 请求合并 feature/venus tracefeature/venus metricfeature/profile-otel-baserunner-stylebugfix/profile-logger-Syncfeature/profile_otelclient_enable_config 完成otel 开关 trace-采样metrice-reader 已经都在各自服务器运行,并接入…

创造性地解决冲突

1、冲突的根本原因是矛盾双方存在不可调和的目标冲突。 2、要知己知彼: 知己:就是对自己的问题、需求进行客观定义,说明需求和问题的意义或价值、阐述解决方案和期望效果; 知彼:站在对方立场,深挖对方真…

根据3d框的八个顶点坐标,求他的中心点,长宽高和yaw值(Python)

要从一个3D框的八个顶点求出它的中心点、长、宽、高和yaw值,首先需要明确框的几何形状和坐标点的顺序。通常这样的框是一个矩形体(长方体),但其方向并不一定与坐标轴平行。 以下是一个步骤来解决这个问题: 求中心点&a…

Unity Bolt UGUI事件注册方式总结

Bolt插件提供了丰富的事件注册方式,开发者几乎不用编写任何代码就可以完成事件的注册,进行交互。下面是我使用UI事件注册的相关总结。 1、通过UI控件自身拖拽实现事件的注册。 Button的事件注册: 新建一个UnityEvent事件, Butt…

Kafka消费者组重平衡(二)

文章目录 概要重平衡通知机制消费组组状态消费端重平衡流程Broker端重平衡流程 概要 上一篇Kafka消费者组重平衡主要介绍了重平衡相关的概念,本篇主要梳理重平衡发生的流程。 为了更好地观察,数据准备如下: kafka版本:kafka_2.1…

nodejs定时任务

项目需求: 每5秒执行一次,多个定时任务错开,即cron表达式中斜杆前带数字,例如 ‘1/5 * * * * *’定时任务准时,延误低 搜索了nodejs的定时任务,其实不多,找到了以下三个常用的: n…

OpenCV中的HoughLines函数和HoughLinesP函数到底有什么区别?

一、简述 基于OpenCV进行直线检测可以使用HoughLines和HoughLinesP函数完成的。这两个函数之间的唯一区别在于,第一个函数使用标准霍夫变换,第二个函数使用概率霍夫变换(因此名称为 P)。概率版本之所以如此,是因为它仅分析点的子集并估计这些点都属于同一条线的概率。此实…

2D游戏开发和3D游戏开发有什么不同?

2D游戏开发和3D游戏开发是两种不同类型的游戏制作方法,它们之间有一些显著的区别: 1. 图形和视觉效果: 2D游戏开发: 2D游戏通常使用二维图形,游戏世界和角色通常在一个平面上显示。这种类型的游戏具有平面的外观&…

数据仓库模型设计V2.0

一、数仓建模的意义 数据模型就是数据组织和存储方法,它强调从业务、数据存取和使用角度合理存储数据。只有将数据有序的组织和存储起来之后,数据才能得到高性能、低成本、高效率、高质量的使用。 高性能:良好的数据模型能够帮助我们快速查询…

shell脚本命令

Shell命令是在类Unix操作系统中使用的命令行解释器(shell)中执行的命令。Shell命令可以用于执行系统命令、操作文件、进行文本处理、管理进程等。以下是一些常见的Shell命令: 1. ls:列出当前目录下的文件和文件夹。 2. cd&#x…

界面组件DevExpress WinForms v23.1亮点 - 全新升级HTML CSS模板

DevExpress WinForms拥有180组件和UI库,能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序,无论是Office风格的界面,还是分析处理大批量的业务数据,它都能轻松胜…