Redis不知道这些还好意思面试?

程序员书单

共 7553字,需浏览 16分钟

 · 2021-01-30













不知道redis真的不好意思说自己是干互联网的。不管用多用少,用深用浅,总之是用点点。下面是redis相关网站。

redis中文站

redis官网

Redis 在线测试

Redis 命令参考

redis是什么

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

redis与memcache的区别

  • memcache的v没有类型的概念,可以储存图片、视频等。Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hashstring 等数据结构的存储。
  • redis 单线程,memcache多线程。
  • Redis虽然是基于内存的存储系统,但是它本身是支持内存数据的持久化的,而且提供两种主要的持久化策略:RDB快照和AOF日志。而memcached是不支持数据持久化操作的。灾难恢复--memcache挂掉后,数据不可恢复。redis数据丢失后可以通过aof恢复。
  • Redis支持数据的备份,即master-slave模式的数据备份
  • 应用场景不一样:Redis出来作为NoSQL数据库使用外,还能用做消息队列、数据堆栈和数据缓存等;Memcached适合于缓存SQL语句、数据集、用户临时性数据、延迟查询数据和session等。

redis为什么那么快

快是redis产生的原因。

  1. redis是基于内存的,内存的读写速度非常快。

  2. redis是单线程的,省去了很多上下文切换线程的时间。当然这不是主要原因。最主要的避免了各种锁的问题。

  3. redis使用多路复用技术,可以处理并发的连接。非阻塞IO 内部实现采用epoll,采用了epoll+自己实现的简单的事件框架。epoll中的读、写、关闭、连接都转化成了事件,然后利用epoll的多路复用特性,绝不在io上浪费一点时间。这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗)

  4. redis 优秀的存储结构。

  5. 不支持事务回滚。

redis为甚是单线程

因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。

如何提高效率?单线程、多进程的集群不失为一个时髦的解决方案。也可以单机开多个redis服务。

redis value支持的数据结构

它支持多种类型的数据结构,字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。

如何实现事务

redis的事务,本质是一组命令的集合。所有的命令都会序列化,然后按顺序串行地执行。事务开启后,所有的命令先入队,提交事务的时候,要么全执行,要么全不执行。常用命令如下:

  • multi 开启事务

  • exec 提交事务

  • discard 取消事务

  • watch key... 监视一个或多个key,如果事务提交前这些key被改动,事务将被打断,watch相当于乐观锁。

  • unwatch 取消对所有key的监视

redis不支持回滚,这和我们以往的理解有点出入。

Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。

因为不需要对回滚进行支持,所以 Redis的内部可以保持简单且快速。

redis 集群架构方案

  1. twemproxy,它类似于一个代理方式,使用方法和普通redis无任何区别,设置好它下属的多个redis实例后,使用时在本需要连接redis的地方改为连接twemproxy,它会以一个代理的身份接收请求并使用一致性hash算法,将请求转接到具体redis,将结果再返回twemproxy。使用方式简便(相对redis只需修改连接端口),对旧项目扩展的首选。

问题?:基于twemproxy自身单端口实例的压力,使用一致性hash后,当redis节点数量改变时候,数据无法自动移动到新的节点。

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

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

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

redis中zset(Sorted Sets)的实现

参考 redis zset底层实现原理

有序集合类型 (Sorted Set或ZSet) 相比于集合类型多了一个排序属性 score(分值),对于有序集合 ZSet 来说,每个存储元素相当于有两个值组成的,一个是有序结合的元素值,一个是排序值。有序集合保留了集合不能有重复成员的特性(分值可以重复),但不同的是,有序集合中的元素可以排序。

一、内部实现# 有序集合是由 ziplist (压缩列表) 或 skiplist (跳跃表) 组成的。

ziplist:ziplist 编码的 Zset 使用紧挨在一起的压缩列表节点来保存,第一个节点保存 member,第二个保存 score。ziplist 内的集合元素按 score 从小到大排序,其实质是一个双向链表。虽然元素是按 score 有序排序的, 但对 ziplist 的节点指针只能线性地移动,所以在 REDIS_ENCODING_ZIPLIST 编码的 Zset 中, 查找某个给定元素的复杂度为 O(N)O(N)。

跳表:像这种链表加多级索引的结构,就是跳跃表!Redis使用跳跃表作为有序集合键的底层实现之一,如果一个有序集合包含的元素数量比较多,又或者有序集合中元素的成员是比较长的字符串时, Redis就会使用跳跃表来作为有序集合健的底层实现。

redis cluster如何搭建,master-slave模式,如何选主

如何搭建redis集群 --- redis-cluster

redis-cluster引入了一个哈希槽(slot)的概念,让数据与哈希槽关联,哈希槽再与节点关联。这样做的好处就是方便集群节点的增加或删除。假如没有哈希槽,数据直接和节点关联,也就是key直接和节点关联,如果要删除一个节点,那这个节点上的所有key都需要一个个地移走,比较麻烦;有了哈希槽,就可以将该节点上的哈希槽全部移走,比较方便。就好比有一些豆子,没有哈希槽需要一个个地捡起来,有哈希槽就是有个碗装着这些豆子,直接把碗拿起来就好了。redis-cluster中固定有16384个哈希槽,假如redis-cluster中有6个redis实例,3个master,每个master带1个(没有固定是1个,可以是N个)slave,比如3个master是A、B、C,它们的slave分别为A1、B1、C1,整个集群对外表现为一个整体。当你执行set k1 v1的时候,到底是set到哪台master上去了呢?还是3台master都有?其实是每个master都会分配到一部分哈希槽,比如:

A:0 ~ 5500 B:5501 ~ 11000 C:11001 ~ 16384 set的时候,对key经过一番计算:slot = CRC16(key) % 16384,就可以拿到哈希槽slot,就可以判断该key要set到哪个master上。

3、slave的作用是什么?当有数据落在A上的时候,给客户端返回成功,并且异步将数据复制到A1上。如果A挂了,那么A1就会成为master继续提供服务。但如果A和A1都挂了,那么整个集群都将不能正常提供服务,所以每个master可以多整几个slave,保证高可用。而且正因为从master复制数据到slave是异步的,所以就有可能master给客户端返回成功了,还没来得及将数据复制到slave的时候,master挂了,然后slave变成了master,而这个master上是没有刚才写的数据的,这就出现了写丢失的情况。如果需要保证强一致性,就要用同步写的方式,但这将会牺牲性能。

redis过期策略

  1. 被动访问时判定。
  2. 主动轮询判定。牺牲内存,保证性能为王。当一些客户端尝试访问它时,key会被发现并主动的过期。当然,这样是不够的,因为有些过期的keys,永远不会访问他们。无论如何,这些keys应该过期,所以定时随机测试设置keys的过期时间。所有这些过期的keys将会从密钥空间删除。

具体就是Redis每秒10次做的事情:测试随机的20个keys进行相关过期检测。删除所有已经过期的keys。

如果有多于25%的keys过期, 重复步奏1. 这是一个平凡的概率算法,基本上的假设是,我们的样本是这个密钥控件,并且我们不断重复过期检测,直到过期的keys的百分百低于25%,这意味着,在任何给定的时刻,最多会清除1/4的过期keys。在复制AOF文件时如何处理过期

为了获得正确的行为而不牺牲一致性,当一个key过期,DEL将会随着AOF文字一起合成到所有附加的slaves。

在master实例中,这种方法是集中的,并且不存在一致性错误的机会。然而,当slaves连接到master时,不会独立过期keys(会等到master执行DEL命令),他们仍然会在数据集里面存在,所以当slave当选为master时淘汰keys会独立执行,然后成为master。

Reids如何实现分布式锁

  • setnx
  • 设置时间
  • Zookeeper

redis的有哪些持久化方式,优缺点是什么

RDB快照:能够在指定的时间间隔对数据进行快照存储。开启配置:snapshotting。它非常适用于数据集的备份,比如你可以在每个小时报保存一下过去12小时内的数据,同时每天保存过去10天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集。

AOF日志:持久化方式,它记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据。AOF命令以redis协议追加保存每次写的操作到文件末尾。Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。他是全量数据。

如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。

你也可以同时开启两种持久化方式, 在这种情况下。当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.最重要的事情是了解RDB和AOF持久化方式的不同,让我们以RDB持久化方式开始。

RDB优缺点:

  1. 速度快。
  2. RDB是一个非常紧凑的文件,它保存了某个时间点的数据集。
  3. RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能。

缺点:宕机重启的时候可能会丢失数据。即便是你设置每3分钟更新一次。也会造成这几分钟的丢失。

AOF:开启 append_only_mode

根据所使用的fsync策略,AOF的速度可能会慢于RDB。可以使用不同的fsync策略:无fsync,每秒fsync(默认),每次写的时候fsync。一旦出现故障,最多丢失1秒的数据。在一般情况下,每秒fsync的性能依然非常高, 而关闭fsync可以让AOF的速度和RDB一样快。对于相同的数据集来说,AOF文件的体积通常要大于RDB文件的体积。如果AOF被破坏,可使用Redis附带的redis-check-aof程序,对原来的 AOF 文件进行修复。

Redis 中的哨兵机制

  • 主从复制:master会复制到slave,master负责写,slave负责读。master会把数据同步到slave,但是slave不会把数据同步到master。所以master不能挂。

  • 哨兵模式:核心还是主从复制。只不过相对于主从模式在主节点宕机导致不可写的情况下,多了一个竞选机制——从所有的从节点竞选出新的主节点。竞选机制的实现,是依赖于在系统中启动一个sentinel进程。sentinel特点:

  • 监控:它会监听主服务器和从服务器之间是否在正常工作。

  • 通知:它能够通过API告诉系统管理员或者程序,集群中某个实例出了问题。

  • 故障转移:它在主节点出了问题的情况下,会在所有的从节点中竞选出一个节点,并将其作为新的主节点。

提供主服务器地址:它还能够向使用者提供当前主节点的地址。这在故障转移后,使用者不用做任何修改就可以知道当前主节点地址。

当竞选出新的主节点后,被选为新的主节点的从节点的配置信息会被sentinel改写为旧的主节点的配置信息。完成改写后,再将新主节点的配置广播给所有的从节点。

Redis集群(cluster) Redis 集群是一个提供在多个Redis间节点间共享数据的程序集。

Redis Cluster集群的搭建与实践

redis最开始使用主从模式做集群,若master宕机需要手动配置slave转为master;后来为了高可用提出来哨兵模式,该模式下有一个哨兵监视master和slave,若master宕机可自动将slave转为master,但它也有一个问题,就是不能动态扩充;所以在3.x提出cluster集群模式。

Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。

Redis 集群的数据分片 Redis 集群没有使用一致性hash, 而是引入了 哈希槽的概念.

Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么:

节点 A 包含 0 到 5500号哈希槽. 节点 B 包含5501 到 11000 号哈希槽. 节点 C 包含11001 到 16384号哈希槽.

获取数据: 如果存入一个值,按照redis cluster哈希槽的算法:CRC16('key')384 = 6782。那么就会把这个key 的存储分配到 B 上了。同样,当我连接(A,B,C)任何一个节点想获取'key'这个key时,也会这样的算法,然后内部跳转到B节点上获取数据

新增一个主节点: 新增一个节点D,redis cluster的这种做法是从各个节点的前面各拿取一部分slot到D上,我会在接下来的实践中实验。大致就会变成这样:

节点A覆盖1365-5460

节点B覆盖6827-10922

节点C覆盖12288-16383

节点D覆盖0-1364,5461-6826,10923-12287

同样删除一个节点也是类似,移动完成后就可以删除这个节点了

Redis 集群的主从复制模型 为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有N-1个复制品. 在我们例子中具有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用.

然而如果在集群创建的时候(或者过一段时间)我们为每个节点添加一个从节点A1,B1,C1,那么整个集群便有三个master节点和三个slave节点组成,这样在节点B失败后,集群便会选举B1为新的主节点继续服务,整个集群便不会因为槽找不到而不可用了

不过当B和B1 都失败后,集群是不可用的.

redis大key问题怎么解决,比如一个key对应的value有几十万的byte

Redis大key的一些场景及问题

大key场景

Redis使用者应该都遇到过大key相关的场景,比如:

  1. 热门话题下评论、答案排序场景。

  2. 大V的粉丝列表。

  3. 使用不恰当,或者对业务预估不准确、不及时进行处理垃圾数据等。

解决方案:

  • 分拆成几个key-value。multiGet取值。
  • 可以对存储元素按一定规则进行分类,分散存储到多个redis实例中。
  • 拆分之后可以考虑采用pipeline去取,由于redis是单线程的,一次只能执行一个命令,这里采用Pipeline模式,一次发送多个命令,无需等待服务端返回。这样就大大的减少了网络往返时间,提高了系统性能。

redis 击穿、穿透、雪崩

  • 击穿 :缓存击穿指的是使用不存在的key进行大量的高并发查询,这导致缓存无法命中,每次请求都要击穿到后端数据库系统进行查询,使数据库压力过大,甚至使数据库服务被压死。比如redis冷数据的key刚清空,就来了一大批数据直达数据库,某个key被击穿。

解决方案:第一个请求发现key不存在的时候,先setnx,只有获取锁成功的那个线程可以查数据库并回写缓存。其他线程随机等到一段时间。但是setnx可能会挂,发生死锁。所以要加过期时间。

  • 穿透

缓存穿透的概念是,用户想要查询一个数据,redis内存数据库没有,持久层数据也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。

解决方案:

  1. 布隆过滤器
  • 雪崩 缓存雪崩指缓存服务器重启或者大量缓存集中在某一个时间段内失效,给后端数据库造成瞬时的负载升高的压力,甚至压垮数据库的情况。

通常的解决办法是过期时间散开:对不同的数据使用不同的失效时间,甚至对相同的数据、不同的请求使用不同的失效时间。

我们总结了在使用redis的过程中遇到的棘手问题以及解决方案,希望对大家有所帮助。记得点在看哦。



浏览 30
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报